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..d82adb3e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,12 +26,12 @@ jobs: if: needs.can_document.outputs.value == 'true' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Use Node.js - uses: actions/setup-node@v1 + uses: actions/setup-node@v5 with: - node-version: "14.x" + node-version: "20.x" registry-url: 'https://registry.npmjs.org' - name: Install Dependencies @@ -48,14 +48,13 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Setup dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v5 with: dotnet-version: | - 6.0.x - 7.0.x + 10.0.x - name: Build run: ./build.sh @@ -64,14 +63,13 @@ jobs: runs-on: windows-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v5 - name: Setup dotnet - uses: actions/setup-dotnet@v1 + uses: actions/setup-dotnet@v5 with: dotnet-version: | - 6.0.x - 7.0.x + 10.0.x - name: Build run: | diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index c1999a9f..3166f3eb 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -1,19 +1,56 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Build Schema", - "$ref": "#/definitions/build", "definitions": { - "build": { - "type": "object", + "Host": { + "type": "string", + "enum": [ + "AppVeyor", + "AzurePipelines", + "Bamboo", + "Bitbucket", + "Bitrise", + "GitHubActions", + "GitLab", + "Jenkins", + "Rider", + "SpaceAutomation", + "TeamCity", + "Terminal", + "TravisCI", + "VisualStudio", + "VSCode" + ] + }, + "ExecutableTarget": { + "type": "string", + "enum": [ + "Clean", + "Compile", + "CopyFiles", + "CreatePackage", + "Default", + "Package", + "PrePublish", + "Publish", + "PublishPackage", + "PublishPreRelease", + "PublishRelease", + "Restore", + "RunUnitTests" + ] + }, + "Verbosity": { + "type": "string", + "description": "", + "enum": [ + "Verbose", + "Normal", + "Minimal", + "Quiet" + ] + }, + "NukeBuild": { "properties": { - "Configuration": { - "type": "string", - "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", - "enum": [ - "Debug", - "Release" - ] - }, "Continue": { "type": "boolean", "description": "Indicates to continue a previously failed build attempt" @@ -23,25 +60,8 @@ "description": "Shows the help text for this build assembly" }, "Host": { - "type": "string", "description": "Host for execution. Default is 'automatic'", - "enum": [ - "AppVeyor", - "AzurePipelines", - "Bamboo", - "Bitbucket", - "Bitrise", - "GitHubActions", - "GitLab", - "Jenkins", - "Rider", - "SpaceAutomation", - "TeamCity", - "Terminal", - "TravisCI", - "VisualStudio", - "VSCode" - ] + "$ref": "#/definitions/Host" }, "NoLogo": { "type": "boolean", @@ -62,10 +82,6 @@ "type": "string" } }, - "ReleaseNotesFilePath": { - "type": "string", - "description": "ReleaseNotesFilePath - To determine the SemanticVersion" - }, "Root": { "type": "string", "description": "Root directory during build execution" @@ -74,61 +90,46 @@ "type": "array", "description": "List of targets to be skipped. Empty list skips all dependencies", "items": { - "type": "string", - "enum": [ - "Clean", - "Compile", - "CopyFiles", - "CreatePackage", - "Default", - "Package", - "PrePublish", - "Publish", - "PublishPackage", - "PublishPreRelease", - "PublishRelease", - "Restore", - "RunUnitTests" - ] + "$ref": "#/definitions/ExecutableTarget" } }, - "Solution": { - "type": "string", - "description": "Path to a solution file that is automatically loaded" - }, "Target": { "type": "array", "description": "List of targets to be invoked. Default is '{default_target}'", "items": { - "type": "string", - "enum": [ - "Clean", - "Compile", - "CopyFiles", - "CreatePackage", - "Default", - "Package", - "PrePublish", - "Publish", - "PublishPackage", - "PublishPreRelease", - "PublishRelease", - "Restore", - "RunUnitTests" - ] + "$ref": "#/definitions/ExecutableTarget" } }, "Verbosity": { - "type": "string", "description": "Logging verbosity during build execution. Default is 'Normal'", + "$ref": "#/definitions/Verbosity" + } + } + } + }, + "allOf": [ + { + "properties": { + "Configuration": { + "type": "string", + "description": "Configuration to build - Default is 'Debug' (local) or 'Release' (server)", "enum": [ - "Minimal", - "Normal", - "Quiet", - "Verbose" + "Debug", + "Release" ] + }, + "ReleaseNotesFilePath": { + "type": "string", + "description": "ReleaseNotesFilePath - To determine the SemanticVersion" + }, + "Solution": { + "type": "string", + "description": "Path to a solution file that is automatically loaded" } } + }, + { + "$ref": "#/definitions/NukeBuild" } - } -} \ No newline at end of file + ] +} 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..4ecccc76 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,20 @@ The main idea behind AngleSharp.Css is to expose the CSSOM as it would be in the - Calculated values (i.e., `calc(20px + 50%)`) - Window-based declaration calculations, see `window.GetComputedStyle` +## Benchmarks + +The `AngleSharp.Performance.Css` project uses [BenchmarkDotNet](https://benchmarkdotnet.org/) to compare CSS parsing performance across libraries. Run the benchmarks in Release mode: + +```bash +dotnet run --project src/AngleSharp.Performance.Css/AngleSharp.Performance.Css.csproj -c Release --framework net10.0 +``` + +To run a quick smoke test instead of a full benchmark: + +```bash +dotnet run --project src/AngleSharp.Performance.Css/AngleSharp.Performance.Css.csproj -c Release --framework net10.0 -- --job short +``` + ## Participating Participation in the project is highly welcome. For this project the same rules as for the AngleSharp core project may be applied. @@ -97,12 +111,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/build.cmd b/build.cmd old mode 100644 new mode 100755 diff --git a/build.ps1 b/build.ps1 index 1f264e7f..3cca4ef3 100644 --- a/build.ps1 +++ b/build.ps1 @@ -18,11 +18,10 @@ $TempDirectory = "$PSScriptRoot\\.nuke\temp" $DotNetGlobalFile = "$PSScriptRoot\\global.json" $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1" -$DotNetChannel = "Current" +$DotNetChannel = "STS" -$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1 $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1 -$env:DOTNET_MULTILEVEL_LOOKUP = 0 +$env:DOTNET_NOLOGO = 1 ########################################################################### # EXECUTION @@ -61,9 +60,15 @@ else { ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath } } $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe" + $env:PATH = "$DotNetDirectory;$env:PATH" } Write-Output "Microsoft (R) .NET SDK version $(& $env:DOTNET_EXE --version)" +if (Test-Path env:NUKE_ENTERPRISE_TOKEN) { + & $env:DOTNET_EXE nuget remove source "nuke-enterprise" > $null + & $env:DOTNET_EXE nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password $env:NUKE_ENTERPRISE_TOKEN > $null +} + ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet } ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments } diff --git a/build.sh b/build.sh index 75341232..a226b969 100755 --- a/build.sh +++ b/build.sh @@ -14,11 +14,10 @@ TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp" DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json" DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh" -DOTNET_CHANNEL="Current" +DOTNET_CHANNEL="STS" export DOTNET_CLI_TELEMETRY_OPTOUT=1 -export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -export DOTNET_MULTILEVEL_LOOKUP=0 +export DOTNET_NOLOGO=1 ########################################################################### # EXECUTION @@ -54,9 +53,15 @@ else "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path fi export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet" + export PATH="$DOTNET_DIRECTORY:$PATH" fi echo "Microsoft (R) .NET SDK version $("$DOTNET_EXE" --version)" +if [[ ! -z ${NUKE_ENTERPRISE_TOKEN+x} && "$NUKE_ENTERPRISE_TOKEN" != "" ]]; then + "$DOTNET_EXE" nuget remove source "nuke-enterprise" &>/dev/null || true + "$DOTNET_EXE" nuget add source "https://f.feedz.io/nuke/enterprise/nuget" --name "nuke-enterprise" --username "PAT" --password "$NUKE_ENTERPRISE_TOKEN" --store-password-in-clear-text &>/dev/null || true +fi + "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@" 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..bb7f437c 100644 --- a/nuke/Build.cs +++ b/nuke/Build.cs @@ -14,8 +14,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using static Nuke.Common.IO.FileSystemTasks; -using static Nuke.Common.IO.PathConstruction; using static Nuke.Common.Tools.DotNet.DotNetTasks; using static Nuke.Common.Tools.NuGet.NuGetTasks; using Project = Nuke.Common.ProjectModel.Project; @@ -89,17 +87,17 @@ 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}"; } } Log.Information("Building version: {Version}", Version); - TargetProject = Solution.GetProject(SourceDirectory / TargetProjectName / $"{TargetLibName}.csproj" ); + TargetProject = Solution.GetProject(TargetLibName); TargetProject.NotNull("TargetProject could not be loaded!"); TargetFrameworks = TargetProject.GetTargetFrameworks(); @@ -112,7 +110,7 @@ protected override void OnBuildInitialized() .Before(Restore) .Executes(() => { - SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(DeleteDirectory); + SourceDirectory.GlobDirectories("**/bin", "**/obj").ForEach(x => x.DeleteDirectory()); }); Target Restore => _ => _ @@ -152,14 +150,14 @@ protected override void OnBuildInitialized() var targetDir = NugetDirectory / "lib" / item; var srcDir = BuildDirectory / item; - CopyFile(srcDir / $"{TargetProjectName}.dll", targetDir / $"{TargetProjectName}.dll", FileExistsPolicy.OverwriteIfNewer); - CopyFile(srcDir / $"{TargetProjectName}.pdb", targetDir / $"{TargetProjectName}.pdb", FileExistsPolicy.OverwriteIfNewer); - CopyFile(srcDir / $"{TargetProjectName}.xml", targetDir / $"{TargetProjectName}.xml", FileExistsPolicy.OverwriteIfNewer); + (srcDir / $"{TargetProjectName}.dll").Copy(targetDir / $"{TargetProjectName}.dll", ExistsPolicy.FileOverwriteIfNewer); + (srcDir / $"{TargetProjectName}.pdb").Copy(targetDir / $"{TargetProjectName}.pdb", ExistsPolicy.FileOverwriteIfNewer); + (srcDir / $"{TargetProjectName}.xml").Copy(targetDir / $"{TargetProjectName}.xml", ExistsPolicy.FileOverwriteIfNewer); } - CopyFile(SourceDirectory / $"{TargetProjectName}.nuspec", NugetDirectory / $"{TargetProjectName}.nuspec", FileExistsPolicy.OverwriteIfNewer); - CopyFile(RootDirectory / "logo.png", NugetDirectory / "logo.png", FileExistsPolicy.OverwriteIfNewer); - CopyFile(RootDirectory / "README.md", NugetDirectory / "README.md", FileExistsPolicy.OverwriteIfNewer); + (SourceDirectory / $"{TargetProjectName}.nuspec").Copy(NugetDirectory / $"{TargetProjectName}.nuspec", ExistsPolicy.FileOverwriteIfNewer); + (RootDirectory / "logo.png").Copy(NugetDirectory / "logo.png", ExistsPolicy.FileOverwriteIfNewer); + (RootDirectory / "README.md").Copy(NugetDirectory / "README.md", ExistsPolicy.FileOverwriteIfNewer); }); Target CreatePackage => _ => _ @@ -191,7 +189,7 @@ protected override void OnBuildInitialized() throw new BuildAbortedException("Could not resolve the NuGet API key."); } - foreach (var nupkg in GlobFiles(NugetDirectory, "*.nupkg")) + foreach (var nupkg in NugetDirectory.GlobFiles("*.nupkg")) { NuGetPush(s => s .SetTargetPath(nupkg) 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/nuke/_build.csproj b/nuke/_build.csproj index 7521e1ba..4c5a47af 100644 --- a/nuke/_build.csproj +++ b/nuke/_build.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net10.0 CS0649;CS0169 .. @@ -11,7 +11,7 @@ - + diff --git a/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj b/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj index 8bf7df15..078fa889 100644 --- a/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj +++ b/src/AngleSharp.Css.Tests/AngleSharp.Css.Tests.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 true Key.snk false @@ -16,11 +16,12 @@ - + + \ No newline at end of file diff --git a/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs b/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs index e62c5ee5..5c3d377b 100644 --- a/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs +++ b/src/AngleSharp.Css.Tests/CssConstructionFunctions.cs @@ -1,7 +1,9 @@ +#nullable disable 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 +72,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 +94,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..5ba99278 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() { @@ -707,6 +731,18 @@ public void CssBackgroundImageNotParsed_Issue66() var expected = "linear-gradient(0deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 1), rgba(248, 248, 248, 1), rgba(238, 238, 238, 1))"; Assert.AreEqual(expected, property.Value); } + + [Test] + public void CssBackgroundPositionSlashSizeLegal() + { + var snippet = "background: center / cover"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("background", property.Name); + Assert.IsFalse(property.IsImportant); + Assert.IsFalse(property.IsInherited); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("center / cover", property.Value); + } } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssBorderProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssBorderProperty.cs index 98f667e5..535bf077 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssBorderProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssBorderProperty.cs @@ -586,5 +586,130 @@ public void CssBorderAggregation() style.SetBorderColor("black"); Assert.AreEqual(expectedCss, style.CssText); } + + [Test] + public void CssBorderInheritShouldResolveToParentValues() + { + var source = @" + + + +
Cell
+ +"; + var document = source.ToHtmlDocument(Configuration.Default.WithCss()); + var td = document.QuerySelector("td"); + var style = td.ComputeCurrentStyle(); + Assert.AreEqual("solid", style.GetBorderTopStyle()); + Assert.AreEqual("3px", style.GetBorderTopWidth()); + } + + [Test] + public void CssBorderInheritShouldResolveAllSides() + { + var source = @" + + + +
Content
+ +"; + var document = source.ToHtmlDocument(Configuration.Default.WithCss()); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("dashed", style.GetBorderTopStyle()); + Assert.AreEqual("dashed", style.GetBorderBottomStyle()); + Assert.AreEqual("dashed", style.GetBorderLeftStyle()); + Assert.AreEqual("dashed", style.GetBorderRightStyle()); + Assert.AreEqual("2px", style.GetBorderTopWidth()); + Assert.AreEqual("2px", style.GetBorderBottomWidth()); + } + + [Test] + public void CssBorderStyleInheritShouldResolveToParentValues() + { + var source = @" + + + +
Content
+ +"; + var document = source.ToHtmlDocument(Configuration.Default.WithCss()); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("dotted", style.GetBorderTopStyle()); + Assert.AreEqual("dotted", style.GetBorderRightStyle()); + } + + [Test] + public void CssBorderWidthInheritShouldResolveToParentValues() + { + var source = @" + + + +
Content
+ +"; + var document = source.ToHtmlDocument(Configuration.Default.WithCss()); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("5px", style.GetBorderTopWidth()); + Assert.AreEqual("5px", style.GetBorderLeftWidth()); + } + + [Test] + public void CssBorderInheritThroughMultipleLevels() + { + var source = @" + + + +
Deep
+ +"; + var document = source.ToHtmlDocument(Configuration.Default.WithCss()); + var inner = document.QuerySelector("div.inner"); + var style = inner.ComputeCurrentStyle(); + Assert.AreEqual("solid", style.GetBorderTopStyle()); + Assert.AreEqual("4px", style.GetBorderTopWidth()); + } + + [Test] + public void CssBorderInheritWithExplicitChildOverride() + { + var source = @" + + + +
Content
+ +"; + var document = source.ToHtmlDocument(Configuration.Default.WithCss()); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("dotted", style.GetBorderTopStyle()); + Assert.AreEqual("solid", style.GetBorderBottomStyle()); + Assert.AreEqual("3px", style.GetBorderTopWidth()); + } } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs index 624113ed..16268a4f 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssFlexProperty.cs @@ -95,6 +95,36 @@ public void CssFlexShorthandGrowAndShrinkLegal() Assert.AreEqual("1 2", property.Value); } + [Test] + public void CssFlexShorthandWithZeroPercentBasisLegal() + { + var snippet = "flex: 1 1 0%"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("flex", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("1 1 0%", property.Value); + } + + [Test] + public void CssFlexBasisZeroPercentLegal() + { + var snippet = "flex-basis: 0%"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("flex-basis", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("0%", property.Value); + } + + [Test] + public void CssFlexBasisZeroPxSerializesWithoutUnit() + { + var snippet = "flex-basis: 0px"; + var property = ParseDeclaration(snippet); + Assert.AreEqual("flex-basis", property.Name); + Assert.IsTrue(property.HasValue); + Assert.AreEqual("0", property.Value); + } + [Test] public void CssFlexWrapLegal() { diff --git a/src/AngleSharp.Css.Tests/Declarations/CssFontProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssFontProperty.cs index 80d1c0d0..87c7a0d1 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssFontProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssFontProperty.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Declarations { using AngleSharp.Css.Dom; 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/CssObjectSizing.cs b/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs index 300312dd..d46c868f 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssObjectSizing.cs @@ -131,7 +131,7 @@ public void CssObjectPositionLeft30Legal() Assert.IsTrue(property.IsAnimatable); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 30px", property.Value); + Assert.AreEqual("0% 30px", property.Value); } } } diff --git a/src/AngleSharp.Css.Tests/Declarations/CssPaddingProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssPaddingProperty.cs index 70d7794d..2560c54c 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssPaddingProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssPaddingProperty.cs @@ -1,5 +1,7 @@ namespace AngleSharp.Css.Tests.Declarations { + using AngleSharp.Css.Dom; + using AngleSharp.Dom; using NUnit.Framework; using static CssConstructionFunctions; @@ -139,5 +141,62 @@ public void CssPaddingShouldBeRecombinedCorrectly() var actual = result.CssText; Assert.AreEqual(expected, actual); } + + [Test] + public void CssPaddingImportantShouldNotBeOverriddenByNonImportant() + { + var snippet = "padding: 20px !important; font-size: 20px; padding: 0"; + var style = ParseDeclarations(snippet); + var padding = style.GetPropertyValue("padding"); + Assert.AreEqual("20px", padding); + var paddingProp = style.GetProperty("padding"); + Assert.IsNotNull(paddingProp); + Assert.IsTrue(paddingProp.IsImportant); + var fontSize = style.GetPropertyValue("font-size"); + Assert.AreEqual("20px", fontSize); + } + + [Test] + public void CssPaddingImportantShouldBeOverriddenByImportant() + { + var snippet = "padding: 20px !important; padding: 0 !important"; + var style = ParseDeclarations(snippet); + var padding = style.GetPropertyValue("padding"); + Assert.AreEqual("0", padding); + var paddingProp = style.GetProperty("padding"); + Assert.IsNotNull(paddingProp); + Assert.IsTrue(paddingProp.IsImportant); + } + + [Test] + public void CssPaddingNonImportantShouldBeOverriddenByNonImportant() + { + var snippet = "padding: 20px; padding: 0"; + var style = ParseDeclarations(snippet); + var padding = style.GetPropertyValue("padding"); + Assert.AreEqual("0", padding); + } + + [Test] + public void CssPaddingNonImportantShouldBeOverriddenByImportant() + { + var snippet = "padding: 20px; padding: 0 !important"; + var style = ParseDeclarations(snippet); + var padding = style.GetPropertyValue("padding"); + Assert.AreEqual("0", padding); + var paddingProp = style.GetProperty("padding"); + Assert.IsNotNull(paddingProp); + Assert.IsTrue(paddingProp.IsImportant); + } + + [Test] + public void CssInlineStyleImportantPaddingShouldTakePrecedence() + { + var html = "

Test

"; + var dom = ParseDocument(html); + var p = dom.QuerySelector("p"); + var style = p.GetStyle(); + Assert.AreEqual("padding: 20px !important; font-size: 20px", style.CssText); + } } } 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/CssTransformProperty.cs b/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs index 48a481df..2d22a627 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssTransformProperty.cs @@ -230,7 +230,7 @@ public void CssTransformOriginYOffsetXKeywordLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 2px", property.Value); + Assert.AreEqual("0% 2px", property.Value); } [Test] @@ -242,7 +242,7 @@ public void CssTransformOriginXKeywordYOffsetLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 2px", property.Value); + Assert.AreEqual("0% 2px", property.Value); } [Test] @@ -290,7 +290,7 @@ public void CssTransformOriginYXKeywordZLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 2px 10px", property.Value); + Assert.AreEqual("0% 2px 10px", property.Value); } [Test] @@ -302,7 +302,7 @@ public void CssTransformOriginXKeywordYZLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); - Assert.AreEqual("0 5px -3px", property.Value); + Assert.AreEqual("0% 5px -3px", property.Value); } [Test] @@ -560,6 +560,7 @@ public void CssTransformRotateXLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); + Assert.AreEqual(property.Value, "rotateX(10deg)"); } [Test] @@ -571,6 +572,7 @@ public void CssTransformRotateYLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); + Assert.AreEqual(property.Value, "rotateY(10deg)"); } [Test] @@ -582,6 +584,7 @@ public void CssTransformRotateZLegal() Assert.IsFalse(property.IsImportant); Assert.IsFalse(property.IsInherited); Assert.IsTrue(property.HasValue); + Assert.AreEqual(property.Value, "rotateZ(10deg)"); } [Test] diff --git a/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs b/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs index f3ba1fed..b768456e 100644 --- a/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs +++ b/src/AngleSharp.Css.Tests/Declarations/CssVariables.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Declarations { using AngleSharp.Css.Dom; @@ -59,7 +60,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 +73,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 +114,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..c8f8db28 100644 --- a/src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs +++ b/src/AngleSharp.Css.Tests/Extensions/AnalysisWindow.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Extensions { using AngleSharp.Css.Dom; @@ -279,15 +280,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/AsyncParsing.cs b/src/AngleSharp.Css.Tests/Extensions/AsyncParsing.cs index 711ab4bb..fd068349 100644 --- a/src/AngleSharp.Css.Tests/Extensions/AsyncParsing.cs +++ b/src/AngleSharp.Css.Tests/Extensions/AsyncParsing.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Extensions { using AngleSharp.Css.Parser; 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..91ee01e1 100644 --- a/src/AngleSharp.Css.Tests/Extensions/InnerText.cs +++ b/src/AngleSharp.Css.Tests/Extensions/InnerText.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Extensions { using AngleSharp.Dom; @@ -37,6 +38,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")] @@ -44,6 +48,7 @@ public void SetInnerText(String fixture, String expectedInnerText, String expect [TestCase("test1
test2
test3", "test1\ntest2\ntest3")] // table [TestCase("
12
34
", "1\t2\n3\t4")] + [TestCase("
1 2
3 4
", "1\t2\n3\t4")] [TestCase("
12
34
5
", "1\t2\n\n3\t4\n\t5")] // select [TestCase("", "test1\ntest2")] 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/Functions/CssDocumentFunction.cs b/src/AngleSharp.Css.Tests/Functions/CssDocumentFunction.cs index 49b95970..b2fc62e7 100644 --- a/src/AngleSharp.Css.Tests/Functions/CssDocumentFunction.cs +++ b/src/AngleSharp.Css.Tests/Functions/CssDocumentFunction.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Functions { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css.Tests/Library/ComputedStyle.cs b/src/AngleSharp.Css.Tests/Library/ComputedStyle.cs new file mode 100644 index 00000000..104b7c26 --- /dev/null +++ b/src/AngleSharp.Css.Tests/Library/ComputedStyle.cs @@ -0,0 +1,109 @@ +#nullable disable +namespace AngleSharp.Css.Tests.Library +{ + using AngleSharp.Css.Dom; + using AngleSharp.Dom; + using AngleSharp.Html.Dom; + using NUnit.Framework; + using System.Threading.Tasks; + using static CssConstructionFunctions; + + [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); + } + + [Test] + public void MarginInheritShouldResolveToParentValues() + { + var source = @" + + + +
    Content
    + +"; + var document = ParseDocument(source); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("10px", style.GetMarginTop()); + Assert.AreEqual("20px", style.GetMarginRight()); + Assert.AreEqual("10px", style.GetMarginBottom()); + Assert.AreEqual("20px", style.GetMarginLeft()); + } + + [Test] + public void PaddingInheritShouldResolveToParentValues() + { + var source = @" + + + +
    Content
    + +"; + var document = ParseDocument(source); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("5px", style.GetPaddingTop()); + Assert.AreEqual("15px", style.GetPaddingRight()); + Assert.AreEqual("10px", style.GetPaddingBottom()); + Assert.AreEqual("15px", style.GetPaddingLeft()); + } + + [Test] + public void InheritOnNonInheritablePropertyShouldResolveFromParent() + { + var source = @" + + + +
    Content
    + +"; + var document = ParseDocument(source); + var child = document.QuerySelector("div.child"); + var style = child.ComputeCurrentStyle(); + Assert.AreEqual("solid", style.GetBorderTopStyle()); + Assert.AreEqual("1px", style.GetBorderTopWidth()); + Assert.AreEqual("8px", style.GetPaddingTop()); + } + + [Test] + public void BorderInheritShouldSerializeCorrectly() + { + var html = @""; + var dom = ParseDocument(html); + var styleSheet = dom.StyleSheets[0] as ICssStyleSheet; + var rule = styleSheet.Rules[0] as ICssStyleRule; + Assert.AreEqual("inherit", rule.Style.GetPropertyValue("border-top-style")); + Assert.AreEqual("inherit", rule.Style.GetPropertyValue("border-top-width")); + Assert.AreEqual("inherit", rule.Style.GetPropertyValue("border-top-color")); + } + } +} diff --git a/src/AngleSharp.Css.Tests/Library/ExtractInfos.cs b/src/AngleSharp.Css.Tests/Library/ExtractInfos.cs new file mode 100644 index 00000000..7fcc9df2 --- /dev/null +++ b/src/AngleSharp.Css.Tests/Library/ExtractInfos.cs @@ -0,0 +1,32 @@ +#nullable disable +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..31d97cfd 100644 --- a/src/AngleSharp.Css.Tests/Library/StringRepresentation.cs +++ b/src/AngleSharp.Css.Tests/Library/StringRepresentation.cs @@ -1,10 +1,16 @@ +#nullable disable 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 +33,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 +111,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/Mocks/MockRequester.cs b/src/AngleSharp.Css.Tests/Mocks/MockRequester.cs index 52259d0a..9c10665d 100644 --- a/src/AngleSharp.Css.Tests/Mocks/MockRequester.cs +++ b/src/AngleSharp.Css.Tests/Mocks/MockRequester.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Mocks { using AngleSharp.Io; diff --git a/src/AngleSharp.Css.Tests/Mocks/MockScriptEngine.cs b/src/AngleSharp.Css.Tests/Mocks/MockScriptEngine.cs index d40e014c..96326666 100644 --- a/src/AngleSharp.Css.Tests/Mocks/MockScriptEngine.cs +++ b/src/AngleSharp.Css.Tests/Mocks/MockScriptEngine.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Mocks { using AngleSharp.Io; diff --git a/src/AngleSharp.Css.Tests/Mocks/PageRequester.cs b/src/AngleSharp.Css.Tests/Mocks/PageRequester.cs index a62c3174..9e871879 100644 --- a/src/AngleSharp.Css.Tests/Mocks/PageRequester.cs +++ b/src/AngleSharp.Css.Tests/Mocks/PageRequester.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Mocks { using AngleSharp.Dom; diff --git a/src/AngleSharp.Css.Tests/Mocks/SiteMapping.cs b/src/AngleSharp.Css.Tests/Mocks/SiteMapping.cs index 72d5978e..80c276ce 100644 --- a/src/AngleSharp.Css.Tests/Mocks/SiteMapping.cs +++ b/src/AngleSharp.Css.Tests/Mocks/SiteMapping.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Mocks { using AngleSharp.Dom; diff --git a/src/AngleSharp.Css.Tests/Mocks/TestServerRequester.cs b/src/AngleSharp.Css.Tests/Mocks/TestServerRequester.cs index 46f1fd53..1f83165b 100644 --- a/src/AngleSharp.Css.Tests/Mocks/TestServerRequester.cs +++ b/src/AngleSharp.Css.Tests/Mocks/TestServerRequester.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Mocks { using AngleSharp.Io; diff --git a/src/AngleSharp.Css.Tests/Parsing/LargeValues.cs b/src/AngleSharp.Css.Tests/Parsing/LargeValues.cs index 36d6e9d0..3a41d7ad 100644 --- a/src/AngleSharp.Css.Tests/Parsing/LargeValues.cs +++ b/src/AngleSharp.Css.Tests/Parsing/LargeValues.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Parsing { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css.Tests/Parsing/StyleSheet.cs b/src/AngleSharp.Css.Tests/Parsing/StyleSheet.cs index 7f3b42af..f7ccf797 100644 --- a/src/AngleSharp.Css.Tests/Parsing/StyleSheet.cs +++ b/src/AngleSharp.Css.Tests/Parsing/StyleSheet.cs @@ -150,5 +150,15 @@ public async Task CastingException_Issue83() var newCss = ss.ToCss(formatter); Assert.IsNotNull(newCss); } + + [Test] + public void OneLetterTagWithCarriageReturnDoesNotWorkAsSelector_Issue190() + { + var css = "a\r\n{\r\nheight: 100vh;\r\n}"; + var parser = new CssParser(); + var ss = parser.ParseStyleSheet(css); + Assert.AreEqual(1, ss.Rules.Length); + Assert.AreEqual("a", ((AngleSharp.Css.Dom.ICssStyleRule)ss.Rules[0]).SelectorText); + } } } 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..4bbdc47e 100644 --- a/src/AngleSharp.Css.Tests/Rules/CssMediaList.cs +++ b/src/AngleSharp.Css.Tests/Rules/CssMediaList.cs @@ -1,4 +1,5 @@ -namespace AngleSharp.Css.Tests.Rules +#nullable disable +namespace AngleSharp.Css.Tests.Rules { using AngleSharp.Css.Dom; using NUnit.Framework; @@ -132,7 +133,7 @@ public void FeatureMinWidthMediaList() } [Test] - public void OnlyFeatureWidthMediaList() + public void OnlyFeatureWidthMediaListInvalid() { var source = @"@media only (width: 640px) { h1 { color: green } @@ -141,7 +142,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 +204,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 +410,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/Rules/CssSupports.cs b/src/AngleSharp.Css.Tests/Rules/CssSupports.cs index a4b5bd3d..1a512803 100644 --- a/src/AngleSharp.Css.Tests/Rules/CssSupports.cs +++ b/src/AngleSharp.Css.Tests/Rules/CssSupports.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Rules { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css.Tests/Rules/FontFace.cs b/src/AngleSharp.Css.Tests/Rules/FontFace.cs index 6504bfa6..5470fd9b 100644 --- a/src/AngleSharp.Css.Tests/Rules/FontFace.cs +++ b/src/AngleSharp.Css.Tests/Rules/FontFace.cs @@ -44,5 +44,108 @@ public void FontFaceOpenSansNoSource() Assert.AreEqual("", fontface.Variant); Assert.AreEqual("", fontface.Weight); } + + [Test] + public void FontFaceWeightRangeShouldBePreserved() + { + var src = "@font-face { font-weight: 100 700; }"; + var sheet = ParseStyleSheet(src); + Assert.IsNotNull(sheet); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("100 700", fontface.Weight); + } + + [Test] + public void FontFaceSingleWeightIntegerShouldBePreserved() + { + var src = "@font-face { font-weight: 400; }"; + var sheet = ParseStyleSheet(src); + Assert.IsNotNull(sheet); + Assert.AreEqual(1, sheet.Rules.Length); + Assert.IsInstanceOf(sheet.Rules[0]); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("400", fontface.Weight); + } + + [Test] + public void FontFaceWeightRangeShouldRoundtripViaToCss() + { + var src = "@font-face { font-weight: 100 700; }"; + var sheet = ParseStyleSheet(src); + var css = sheet.ToCss(); + Assert.That(css, Does.Contain("font-weight: 100 700")); + } + + [Test] + public void FontFaceWeightBoldKeywordShouldBePreserved() + { + var src = "@font-face { font-weight: bold; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("bold", fontface.Weight); + } + + [Test] + public void FontFaceWeightNormalKeywordShouldBePreserved() + { + var src = "@font-face { font-weight: normal; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("normal", fontface.Weight); + } + + [Test] + public void FontFaceWeightMinBoundary100ShouldBePreserved() + { + var src = "@font-face { font-weight: 100; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("100", fontface.Weight); + } + + [Test] + public void FontFaceWeightMaxBoundary900ShouldBePreserved() + { + var src = "@font-face { font-weight: 900; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("900", fontface.Weight); + } + + [Test] + public void FontFaceWeightOutOfRange1ShouldBeInvalid() + { + var src = "@font-face { font-weight: 1; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("", fontface.Weight); + } + + [Test] + public void FontFaceWeightOutOfRange1000ShouldBeInvalid() + { + var src = "@font-face { font-weight: 1000; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("", fontface.Weight); + } + + [Test] + public void FontFaceWeightThreeValuesShouldBeInvalid() + { + var src = "@font-face { font-weight: 100 400 700; }"; + var sheet = ParseStyleSheet(src); + Assert.AreEqual(1, sheet.Rules.Length); + var fontface = (ICssFontFaceRule)sheet.Rules[0]; + Assert.AreEqual("", fontface.Weight); + } } } diff --git a/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs b/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs index 9af111fd..14faf1c4 100644 --- a/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs +++ b/src/AngleSharp.Css.Tests/Styling/BasicStyling.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Styling { using AngleSharp.Css.Dom; @@ -210,5 +211,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/ContextLoading.cs b/src/AngleSharp.Css.Tests/Styling/ContextLoading.cs index 1c8dc86f..381e2258 100644 --- a/src/AngleSharp.Css.Tests/Styling/ContextLoading.cs +++ b/src/AngleSharp.Css.Tests/Styling/ContextLoading.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Styling { using AngleSharp.Css.Tests.Mocks; diff --git a/src/AngleSharp.Css.Tests/Styling/CssCases.cs b/src/AngleSharp.Css.Tests/Styling/CssCases.cs index cf46959b..d67a5454 100644 --- a/src/AngleSharp.Css.Tests/Styling/CssCases.cs +++ b/src/AngleSharp.Css.Tests/Styling/CssCases.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Styling { using AngleSharp.Css.Dom; @@ -15,6 +16,7 @@ private static ICssStyleSheet ParseSheet(String text) { IsIncludingUnknownDeclarations = true, IsIncludingUnknownRules = true, + IsExcludingNesting = true, IsToleratingInvalidSelectors = false, }); } @@ -319,9 +321,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 +348,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/Styling/CssSheet.cs b/src/AngleSharp.Css.Tests/Styling/CssSheet.cs index 76348930..26a37a83 100644 --- a/src/AngleSharp.Css.Tests/Styling/CssSheet.cs +++ b/src/AngleSharp.Css.Tests/Styling/CssSheet.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Styling { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css.Tests/Styling/HtmlCssIntegration.cs b/src/AngleSharp.Css.Tests/Styling/HtmlCssIntegration.cs index 754a36f5..91933938 100644 --- a/src/AngleSharp.Css.Tests/Styling/HtmlCssIntegration.cs +++ b/src/AngleSharp.Css.Tests/Styling/HtmlCssIntegration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Styling { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css.Tests/TestExtensions.cs b/src/AngleSharp.Css.Tests/TestExtensions.cs index 2a4a187d..caade98c 100644 --- a/src/AngleSharp.Css.Tests/TestExtensions.cs +++ b/src/AngleSharp.Css.Tests/TestExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests { using AngleSharp.Css.Tests.Mocks; 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..59ef376d 100644 --- a/src/AngleSharp.Css.Tests/Values/Gradient.cs +++ b/src/AngleSharp.Css.Tests/Values/Gradient.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Tests.Values { using AngleSharp.Css.Converters; @@ -29,8 +30,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 +42,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 +62,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 +86,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 +104,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 +122,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 +141,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 +164,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 +187,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 +209,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 +235,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 +257,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 +279,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 +301,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 +323,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 +346,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 +371,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 +394,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 +419,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 +436,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 +459,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.sln b/src/AngleSharp.Css.sln index ce2248b2..a46c8c7c 100644 --- a/src/AngleSharp.Css.sln +++ b/src/AngleSharp.Css.sln @@ -16,10 +16,11 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{988B2C40-782B-4128-9551-7AE65B60F903}" ProjectSection(SolutionItems) = preProject AngleSharp.Css.nuspec = AngleSharp.Css.nuspec - ..\build.cake = ..\build.cake Directory.Build.props = Directory.Build.props EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "..\nuke\_build.csproj", "{B82798E2-2766-4C1E-860C-136F4B3B1185}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +47,8 @@ Global {37F366DB-78D6-485F-9D8C-7C65A4B0186D}.Debug|Any CPU.Build.0 = Debug|Any CPU {37F366DB-78D6-485F-9D8C-7C65A4B0186D}.Release|Any CPU.ActiveCfg = Release|Any CPU {37F366DB-78D6-485F-9D8C-7C65A4B0186D}.Release|Any CPU.Build.0 = Release|Any CPU + {B82798E2-2766-4C1E-860C-136F4B3B1185}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B82798E2-2766-4C1E-860C-136F4B3B1185}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/AngleSharp.Css/AngleSharp.Css.csproj b/src/AngleSharp.Css/AngleSharp.Css.csproj index 5422d00f..d947e988 100644 --- a/src/AngleSharp.Css/AngleSharp.Css.csproj +++ b/src/AngleSharp.Css/AngleSharp.Css.csproj @@ -1,9 +1,9 @@ - + AngleSharp.Css AngleSharp.Css - netstandard2.0;net6.0;net7.0 - netstandard2.0;net461;net472;net6.0;net7.0 + netstandard2.0;net8.0;net10.0 + $(TargetFrameworks);net462;net472 true Key.snk true @@ -21,7 +21,8 @@ - + + diff --git a/src/AngleSharp.Css/BrowsingContextExtensions.cs b/src/AngleSharp.Css/BrowsingContextExtensions.cs index 29e502cd..9b6249c3 100644 --- a/src/AngleSharp.Css/BrowsingContextExtensions.cs +++ b/src/AngleSharp.Css/BrowsingContextExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; 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..f62941b3 100644 --- a/src/AngleSharp.Css/Constants/InitialValues.cs +++ b/src/AngleSharp.Css/Constants/InitialValues.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; @@ -10,204 +11,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..bbab2a3a 100644 --- a/src/AngleSharp.Css/Converters/CounterValueConverter.cs +++ b/src/AngleSharp.Css/Converters/CounterValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; @@ -9,18 +10,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 +37,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..2fb831aa 100644 --- a/src/AngleSharp.Css/Converters/DictionaryValueConverter.cs +++ b/src/AngleSharp.Css/Converters/DictionaryValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; @@ -24,7 +25,7 @@ public ICssValue Convert(StringSource source) if (ident != null && _values.TryGetValue(ident, out mode)) { - return new Constant(ident.ToLowerInvariant(), mode); + return new CssConstantValue(ident.ToLowerFast(), 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..6586eef2 --- /dev/null +++ b/src/AngleSharp.Css/Converters/FlowRelativeValueConverter.cs @@ -0,0 +1,45 @@ +#nullable disable +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..554db356 100644 --- a/src/AngleSharp.Css/Converters/IdentifierValueConverter.cs +++ b/src/AngleSharp.Css/Converters/IdentifierValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; @@ -21,7 +22,7 @@ public ICssValue Convert(StringSource source) if (result != null) { - return new Identifier(result); + return new CssIdentifierValue(result); } return null; @@ -43,7 +44,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/Converters/ListValueConverter.cs b/src/AngleSharp.Css/Converters/ListValueConverter.cs index 7f7e0210..7ed5a4d9 100644 --- a/src/AngleSharp.Css/Converters/ListValueConverter.cs +++ b/src/AngleSharp.Css/Converters/ListValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/OneOrMoreValueConverter.cs b/src/AngleSharp.Css/Converters/OneOrMoreValueConverter.cs index cc59b6f5..d27193b3 100644 --- a/src/AngleSharp.Css/Converters/OneOrMoreValueConverter.cs +++ b/src/AngleSharp.Css/Converters/OneOrMoreValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/OrValueConverter.cs b/src/AngleSharp.Css/Converters/OrValueConverter.cs index 06876741..465c791e 100644 --- a/src/AngleSharp.Css/Converters/OrValueConverter.cs +++ b/src/AngleSharp.Css/Converters/OrValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/PeriodicValueConverter.cs b/src/AngleSharp.Css/Converters/PeriodicValueConverter.cs index 3d718eb1..74f571bd 100644 --- a/src/AngleSharp.Css/Converters/PeriodicValueConverter.cs +++ b/src/AngleSharp.Css/Converters/PeriodicValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; @@ -33,6 +34,11 @@ public ICssValue Convert(StringSource source) if (length > 0) { + if (length == 4) + { + return new CssPeriodicValue(options); + } + var values = new ICssValue[length]; Array.Copy(options, values, length); return new CssPeriodicValue(values); diff --git a/src/AngleSharp.Css/Converters/RadiusValueConverter.cs b/src/AngleSharp.Css/Converters/RadiusValueConverter.cs index 985229e7..fefdacda 100644 --- a/src/AngleSharp.Css/Converters/RadiusValueConverter.cs +++ b/src/AngleSharp.Css/Converters/RadiusValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/SeparatedEnumsConverter.cs b/src/AngleSharp.Css/Converters/SeparatedEnumsConverter.cs index 13dff4ed..248be906 100644 --- a/src/AngleSharp.Css/Converters/SeparatedEnumsConverter.cs +++ b/src/AngleSharp.Css/Converters/SeparatedEnumsConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/SeparatorConverter.cs b/src/AngleSharp.Css/Converters/SeparatorConverter.cs index 7b7a0235..aa1dc183 100644 --- a/src/AngleSharp.Css/Converters/SeparatorConverter.cs +++ b/src/AngleSharp.Css/Converters/SeparatorConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/StandardValueConverter.cs b/src/AngleSharp.Css/Converters/StandardValueConverter.cs index 79021014..bdd3efbd 100644 --- a/src/AngleSharp.Css/Converters/StandardValueConverter.cs +++ b/src/AngleSharp.Css/Converters/StandardValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Converters/StructValueConverter.cs b/src/AngleSharp.Css/Converters/StructValueConverter.cs index f6875cd8..7975369c 100644 --- a/src/AngleSharp.Css/Converters/StructValueConverter.cs +++ b/src/AngleSharp.Css/Converters/StructValueConverter.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/CssConfigurationExtensions.cs b/src/AngleSharp.Css/CssConfigurationExtensions.cs index f18f89d6..ffba2120 100644 --- a/src/AngleSharp.Css/CssConfigurationExtensions.cs +++ b/src/AngleSharp.Css/CssConfigurationExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp { using AngleSharp.Css; diff --git a/src/AngleSharp.Css/CssDefaultStyleSheetProvider.cs b/src/AngleSharp.Css/CssDefaultStyleSheetProvider.cs index 1afae9b4..8ef700ac 100644 --- a/src/AngleSharp.Css/CssDefaultStyleSheetProvider.cs +++ b/src/AngleSharp.Css/CssDefaultStyleSheetProvider.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/CssStylingService.cs b/src/AngleSharp.Css/CssStylingService.cs index f685d223..4bea528e 100644 --- a/src/AngleSharp.Css/CssStylingService.cs +++ b/src/AngleSharp.Css/CssStylingService.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/DeclarationInfo.cs b/src/AngleSharp.Css/DeclarationInfo.cs index 9391ac78..c393bc0e 100644 --- a/src/AngleSharp.Css/DeclarationInfo.cs +++ b/src/AngleSharp.Css/DeclarationInfo.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; @@ -21,7 +22,7 @@ public class DeclarationInfo public DeclarationInfo(String name, IValueConverter converter, PropertyFlags flags = PropertyFlags.None, ICssValue initialValue = null, String[] shorthands = null, String[] longhands = null) { Name = name; - Converter = initialValue != null ? Or(converter, AssignInitial(initialValue)) : converter; + Converter = initialValue != null || longhands?.Length > 0 ? Or(AssignInitial(initialValue), converter) : converter; Aggregator = converter as IValueAggregator; Flags = flags; InitialValue = initialValue; diff --git a/src/AngleSharp.Css/Declarations/AnimationDeclaration.cs b/src/AngleSharp.Css/Declarations/AnimationDeclaration.cs index c103c634..58aa516c 100644 --- a/src/AngleSharp.Css/Declarations/AnimationDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/AnimationDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs b/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs index 52afec96..cfdeadd0 100644 --- a/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BackgroundDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -102,7 +103,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); @@ -126,6 +127,7 @@ public ICssValue Convert(StringSource source) if (c == Symbols.Solidus && size == null) { + c = source.Next(); c = source.SkipSpacesAndComments(); size = source.ParseSize(); c = source.SkipSpacesAndComments(); @@ -230,7 +232,7 @@ public ICssValue[] Split(ICssValue value) return null; } - + private static ICssValue CreateLayers(CssListValue image, CssListValue attachment, CssListValue clip, CssListValue position, CssListValue origin, CssListValue repeat, CssListValue size) { var count = GetCount(image, attachment, clip, position, size, repeat, origin); diff --git a/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs b/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs index 3f344d29..e644a20d 100644 --- a/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BackgroundPositionDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -47,7 +48,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 +61,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/BackgroundRepeatDeclaration.cs b/src/AngleSharp.Css/Declarations/BackgroundRepeatDeclaration.cs index ab6ba7ef..93c2039d 100644 --- a/src/AngleSharp.Css/Declarations/BackgroundRepeatDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BackgroundRepeatDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/BorderBottomDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderBottomDeclaration.cs index b67d754d..cd5e05bb 100644 --- a/src/AngleSharp.Css/Declarations/BorderBottomDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderBottomDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/BorderColorDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderColorDeclaration.cs index c32160fc..ab0f38dc 100644 --- a/src/AngleSharp.Css/Declarations/BorderColorDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderColorDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/BorderDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderDeclaration.cs index 6acc9163..ed419b64 100644 --- a/src/AngleSharp.Css/Declarations/BorderDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs index 750be019..636076f0 100644 --- a/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderImageDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -146,7 +147,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/BorderLeftDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderLeftDeclaration.cs index f23d3fa0..1d0a0af6 100644 --- a/src/AngleSharp.Css/Declarations/BorderLeftDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderLeftDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/BorderRadiusDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderRadiusDeclaration.cs index 55f47a4b..2fd23a81 100644 --- a/src/AngleSharp.Css/Declarations/BorderRadiusDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderRadiusDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/BorderRightDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderRightDeclaration.cs index 427ebe3a..5a308508 100644 --- a/src/AngleSharp.Css/Declarations/BorderRightDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderRightDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/BorderStyleDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderStyleDeclaration.cs index 32471f72..b816f88a 100644 --- a/src/AngleSharp.Css/Declarations/BorderStyleDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderStyleDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/BorderTopDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderTopDeclaration.cs index 76c5bb3f..6a4afce0 100644 --- a/src/AngleSharp.Css/Declarations/BorderTopDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderTopDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/BorderWidthDeclaration.cs b/src/AngleSharp.Css/Declarations/BorderWidthDeclaration.cs index 7d8cf6b6..0fda198d 100644 --- a/src/AngleSharp.Css/Declarations/BorderWidthDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/BorderWidthDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/ColumnRuleDeclaration.cs b/src/AngleSharp.Css/Declarations/ColumnRuleDeclaration.cs index 9d240f26..d7726658 100644 --- a/src/AngleSharp.Css/Declarations/ColumnRuleDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/ColumnRuleDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/ColumnsDeclaration.cs b/src/AngleSharp.Css/Declarations/ColumnsDeclaration.cs index 100efc54..d32cceb3 100644 --- a/src/AngleSharp.Css/Declarations/ColumnsDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/ColumnsDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/ContentDeclaration.cs b/src/AngleSharp.Css/Declarations/ContentDeclaration.cs index 89255772..6ebc16a4 100644 --- a/src/AngleSharp.Css/Declarations/ContentDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/ContentDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -8,6 +9,7 @@ namespace AngleSharp.Css.Declarations using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Linq; static class ContentDeclaration { @@ -21,7 +23,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 +109,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 +119,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 +230,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 +250,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 +278,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 +287,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 +307,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 +316,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..fcc08646 100644 --- a/src/AngleSharp.Css/Declarations/CursorDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/CursorDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -21,39 +22,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/FlexDeclaration.cs b/src/AngleSharp.Css/Declarations/FlexDeclaration.cs index 01ee6cbe..feda1865 100644 --- a/src/AngleSharp.Css/Declarations/FlexDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/FlexDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/FlexFlowDeclaration.cs b/src/AngleSharp.Css/Declarations/FlexFlowDeclaration.cs index a9e56bdf..d9cd5be5 100644 --- a/src/AngleSharp.Css/Declarations/FlexFlowDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/FlexFlowDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/FontDeclaration.cs b/src/AngleSharp.Css/Declarations/FontDeclaration.cs index 42f8eb8d..462c40e5 100644 --- a/src/AngleSharp.Css/Declarations/FontDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/FontDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -121,7 +122,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 +135,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/FontWeightDeclaration.cs b/src/AngleSharp.Css/Declarations/FontWeightDeclaration.cs index 869b5923..d047027b 100644 --- a/src/AngleSharp.Css/Declarations/FontWeightDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/FontWeightDeclaration.cs @@ -1,5 +1,6 @@ namespace AngleSharp.Css.Declarations { + using AngleSharp.Css.Converters; using AngleSharp.Css.Dom; using System; using static ValueConverters; @@ -13,7 +14,7 @@ static class FontWeightDeclaration PropertyNames.Font, }; - public static IValueConverter Converter = Or(FontWeightConverter, WeightIntegerConverter); + public static IValueConverter Converter = Or(FontWeightConverter, WeightIntegerConverter.Many(1, 2)); public static ICssValue InitialValue = InitialValues.FontWeightDecl; diff --git a/src/AngleSharp.Css/Declarations/GapDeclaration.cs b/src/AngleSharp.Css/Declarations/GapDeclaration.cs index 1512f71c..656997d2 100644 --- a/src/AngleSharp.Css/Declarations/GapDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GapDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -30,8 +31,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..1bc79868 100644 --- a/src/AngleSharp.Css/Declarations/GridAreaDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridAreaDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -37,7 +38,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 +73,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 +85,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 +101,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..26c720c2 100644 --- a/src/AngleSharp.Css/Declarations/GridColumnDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridColumnDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -64,7 +65,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/GridColumnEndDeclaration.cs b/src/AngleSharp.Css/Declarations/GridColumnEndDeclaration.cs index 43a656b2..22a34cee 100644 --- a/src/AngleSharp.Css/Declarations/GridColumnEndDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridColumnEndDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/GridColumnStartDeclaration.cs b/src/AngleSharp.Css/Declarations/GridColumnStartDeclaration.cs index 18bf2cbd..e709132a 100644 --- a/src/AngleSharp.Css/Declarations/GridColumnStartDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridColumnStartDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/GridDeclaration.cs b/src/AngleSharp.Css/Declarations/GridDeclaration.cs index d5053f4f..2e4a2ac6 100644 --- a/src/AngleSharp.Css/Declarations/GridDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -6,6 +7,7 @@ namespace AngleSharp.Css.Declarations using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Linq; static class GridDeclaration { @@ -37,11 +39,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 +155,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..0f39ba40 100644 --- a/src/AngleSharp.Css/Declarations/GridGapDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridGapDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -30,8 +31,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..76550c87 100644 --- a/src/AngleSharp.Css/Declarations/GridRowDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridRowDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -70,7 +71,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/GridRowEndDeclaration.cs b/src/AngleSharp.Css/Declarations/GridRowEndDeclaration.cs index 1cadd95b..1ff0176b 100644 --- a/src/AngleSharp.Css/Declarations/GridRowEndDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridRowEndDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/GridRowStartDeclaration.cs b/src/AngleSharp.Css/Declarations/GridRowStartDeclaration.cs index 5f92a618..161cdf0e 100644 --- a/src/AngleSharp.Css/Declarations/GridRowStartDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridRowStartDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; 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..b8f5b384 100644 --- a/src/AngleSharp.Css/Declarations/GridTemplateDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/GridTemplateDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; @@ -67,7 +68,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/ListStyleDeclaration.cs b/src/AngleSharp.Css/Declarations/ListStyleDeclaration.cs index c753e530..271b9174 100644 --- a/src/AngleSharp.Css/Declarations/ListStyleDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/ListStyleDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/MarginBlockDeclaration.cs b/src/AngleSharp.Css/Declarations/MarginBlockDeclaration.cs new file mode 100644 index 00000000..27f015fd --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginBlockDeclaration.cs @@ -0,0 +1,57 @@ +#nullable disable +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..cc5157f7 100644 --- a/src/AngleSharp.Css/Declarations/MarginDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/MarginDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -27,7 +28,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..8707fa39 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/MarginInlineDeclaration.cs @@ -0,0 +1,57 @@ +#nullable disable +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/OrderDeclaration.cs b/src/AngleSharp.Css/Declarations/OrderDeclaration.cs index a4ed999f..101252ac 100644 --- a/src/AngleSharp.Css/Declarations/OrderDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/OrderDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/OutlineDeclaration.cs b/src/AngleSharp.Css/Declarations/OutlineDeclaration.cs index 7b467cbc..2edb68c4 100644 --- a/src/AngleSharp.Css/Declarations/OutlineDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/OutlineDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/PaddingBlockDeclaration.cs b/src/AngleSharp.Css/Declarations/PaddingBlockDeclaration.cs new file mode 100644 index 00000000..f759c171 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingBlockDeclaration.cs @@ -0,0 +1,57 @@ +#nullable disable +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..f4053fd3 100644 --- a/src/AngleSharp.Css/Declarations/PaddingDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/PaddingDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; @@ -27,7 +28,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..872189c2 --- /dev/null +++ b/src/AngleSharp.Css/Declarations/PaddingInlineDeclaration.cs @@ -0,0 +1,57 @@ +#nullable disable +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/Declarations/SrcDeclaration.cs b/src/AngleSharp.Css/Declarations/SrcDeclaration.cs index 2461c578..544c2ea4 100644 --- a/src/AngleSharp.Css/Declarations/SrcDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/SrcDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/StrokeDeclaration.cs b/src/AngleSharp.Css/Declarations/StrokeDeclaration.cs index 0fd39c07..acae4535 100644 --- a/src/AngleSharp.Css/Declarations/StrokeDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/StrokeDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Declarations/TextDecorationDeclaration.cs b/src/AngleSharp.Css/Declarations/TextDecorationDeclaration.cs index fe62075e..b6876925 100644 --- a/src/AngleSharp.Css/Declarations/TextDecorationDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/TextDecorationDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/TransitionDeclaration.cs b/src/AngleSharp.Css/Declarations/TransitionDeclaration.cs index 30e345e0..da41b036 100644 --- a/src/AngleSharp.Css/Declarations/TransitionDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/TransitionDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Converters; diff --git a/src/AngleSharp.Css/Declarations/UnicodeRangeDeclaration.cs b/src/AngleSharp.Css/Declarations/UnicodeRangeDeclaration.cs index 12279b56..c04045f9 100644 --- a/src/AngleSharp.Css/Declarations/UnicodeRangeDeclaration.cs +++ b/src/AngleSharp.Css/Declarations/UnicodeRangeDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Declarations { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Dom/CssHelpers.cs b/src/AngleSharp.Css/Dom/CssHelpers.cs index 228412b6..927ca01b 100644 --- a/src/AngleSharp.Css/Dom/CssHelpers.cs +++ b/src/AngleSharp.Css/Dom/CssHelpers.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Attributes; diff --git a/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs b/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs index a6cab36f..d37adfaa 100644 --- a/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs +++ b/src/AngleSharp.Css/Dom/ElementCssInlineStyleExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Attributes; @@ -14,7 +15,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/Events/MediaQueryListEvent.cs b/src/AngleSharp.Css/Dom/Events/MediaQueryListEvent.cs index e900f0ea..07139b14 100644 --- a/src/AngleSharp.Css/Dom/Events/MediaQueryListEvent.cs +++ b/src/AngleSharp.Css/Dom/Events/MediaQueryListEvent.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom.Events { using AngleSharp.Attributes; diff --git a/src/AngleSharp.Css/Dom/ICssProperties .cs b/src/AngleSharp.Css/Dom/ICssProperties .cs index 24671af2..463b2f68 100644 --- a/src/AngleSharp.Css/Dom/ICssProperties .cs +++ b/src/AngleSharp.Css/Dom/ICssProperties .cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Attributes; 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..a8144ffa 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssMedium.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssMedium.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Text; @@ -18,6 +19,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 +32,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 +43,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 +67,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 +85,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 +96,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 +153,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..9092dbed 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssProperty.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssProperty.cs @@ -1,7 +1,9 @@ +#nullable disable 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 +29,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.ToLowerFast(); _converter = converter; _flags = flags; _value = value; @@ -52,6 +54,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 +88,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/CssPseudoElement.cs b/src/AngleSharp.Css/Dom/Internal/CssPseudoElement.cs index df336580..2f91b96b 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssPseudoElement.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssPseudoElement.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Dom; diff --git a/src/AngleSharp.Css/Dom/Internal/CssPseudoElementList.cs b/src/AngleSharp.Css/Dom/Internal/CssPseudoElementList.cs index a099ccdf..82ab7d3d 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssPseudoElementList.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssPseudoElementList.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using System; diff --git a/src/AngleSharp.Css/Dom/Internal/CssRule.cs b/src/AngleSharp.Css/Dom/Internal/CssRule.cs index d1e5b93d..0d97fce7 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; diff --git a/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs b/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs index 05c51c0b..ac6c1c5a 100644 --- a/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs +++ b/src/AngleSharp.Css/Dom/Internal/CssStyleDeclaration.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css; @@ -18,6 +19,7 @@ sealed class CssStyleDeclaration : ICssStyleDeclaration #region Fields private readonly List _declarations; + private readonly Dictionary _declarationIndex; private readonly IBrowsingContext _context; private ICssRule _parent; private Boolean _updating; @@ -35,6 +37,7 @@ sealed class CssStyleDeclaration : ICssStyleDeclaration public CssStyleDeclaration(IBrowsingContext context) { _declarations = new List(); + _declarationIndex = new Dictionary(StringComparer.OrdinalIgnoreCase); _context = context; } @@ -76,9 +79,9 @@ public String CssText public ICssProperty GetProperty(String name) { - for (var i = 0; i < _declarations.Count; i++) + if (_declarationIndex.TryGetValue(name, out var index) && index < _declarations.Count) { - var declaration = _declarations[i]; + var declaration = _declarations[index]; if (declaration.Name.Isi(name)) { @@ -99,6 +102,7 @@ public void Update(String value) if (!_updating) { _declarations.Clear(); + _declarationIndex.Clear(); if (!String.IsNullOrEmpty(value)) { @@ -108,6 +112,7 @@ public void Update(String value) if (decl != null) { _declarations.AddRange(decl); + RebuildIndex(); } } } @@ -134,7 +139,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 +180,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 +283,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 +306,18 @@ public void SetProperty(String propertyName, String propertyValue, String priori } } + public void AddProperty(ICssProperty declaration) + { + _declarationIndex[declaration.Name] = _declarations.Count; + _declarations.Add(declaration); + } + + public void RemoveProperty(ICssProperty declaration) + { + _declarations.Remove(declaration); + RebuildIndex(); + } + #endregion #region Internal Methods @@ -318,8 +335,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) { @@ -338,14 +365,24 @@ private void RemovePropertyByName(String propertyName) var info = _context.GetDeclarationInfo(propertyName); var longhands = info.Longhands; - for (var i = 0; i < _declarations.Count; i++) + if (_declarationIndex.TryGetValue(propertyName, out var index) && index < _declarations.Count && + _declarations[index].Name.Is(propertyName)) { - var declaration = _declarations[i]; - - if (declaration.Name.Is(propertyName)) + _declarations.RemoveAt(index); + RebuildIndex(); + } + else + { + for (var i = 0; i < _declarations.Count; i++) { - _declarations.RemoveAt(i); - break; + var declaration = _declarations[i]; + + if (declaration.Name.Is(propertyName)) + { + _declarations.RemoveAt(i); + RebuildIndex(); + break; + } } } @@ -357,28 +394,50 @@ 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) { var skip = defaultSkip(newdecl); - for (var i = 0; i < _declarations.Count; i++) + if (_declarationIndex.TryGetValue(newdecl.Name, out var idx) && idx < _declarations.Count && + _declarations[idx].Name.Is(newdecl.Name)) { - var olddecl = _declarations[i]; - - if (olddecl.Name.Is(newdecl.Name)) + if (removeExisting.Invoke(_declarations[idx], newdecl)) { - if (removeExisting.Invoke(olddecl, newdecl)) - { - _declarations.RemoveAt(i); - } - else + _declarations.RemoveAt(idx); + skip = false; + } + else + { + skip = true; + } + } + else + { + for (var i = 0; i < _declarations.Count; i++) + { + var olddecl = _declarations[i]; + + if (olddecl.Name.Is(newdecl.Name)) { - skip = true; - } + if (removeExisting.Invoke(olddecl, newdecl)) + { + _declarations.RemoveAt(i); + skip = false; + } + else + { + skip = true; + } - break; + break; + } } } @@ -389,21 +448,28 @@ private void ChangeDeclarations(IEnumerable decls, Predicate _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 +67,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..d13e328a 100644 --- a/src/AngleSharp.Css/Dom/Internal/MediaList.cs +++ b/src/AngleSharp.Css/Dom/Internal/MediaList.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; @@ -6,6 +7,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 +19,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 +60,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..b8c284b4 100644 --- a/src/AngleSharp.Css/Dom/Internal/PseudoElement.cs +++ b/src/AngleSharp.Css/Dom/Internal/PseudoElement.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Dom; @@ -53,6 +54,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..37660d67 --- /dev/null +++ b/src/AngleSharp.Css/Dom/Internal/ReferencedNestedSelector.cs @@ -0,0 +1,30 @@ +#nullable disable +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/CssCounterStyleRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssCounterStyleRule.cs index 170b0249..8c8a1f31 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssCounterStyleRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssCounterStyleRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssDeclarationRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssDeclarationRule.cs index 8dffb90e..83e166c0 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssDeclarationRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssDeclarationRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssDocumentRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssDocumentRule.cs index 17d3b7dc..f20a1287 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssDocumentRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssDocumentRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; 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/CssFontFeatureValuesRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssFontFeatureValuesRule.cs index b5b1f0e6..cf72e642 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssFontFeatureValuesRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssFontFeatureValuesRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssGroupingRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssGroupingRule.cs index 017bc32e..b1a07dbe 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssGroupingRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssGroupingRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using System; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssImportRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssImportRule.cs index 29306db0..d2159795 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssImportRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssImportRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using System; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframeRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframeRule.cs index b1a7c405..95eda6c9 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframeRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframeRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframesRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframesRule.cs index 0821c14d..6afd25eb 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframesRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssKeyframesRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Text; diff --git a/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs index c422d079..21d92c0f 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssPageRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Dom; @@ -60,7 +61,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..ab6798cd 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssStyleRule.cs @@ -1,21 +1,28 @@ +#nullable enable annotations +#nullable disable warnings 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 +32,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 +88,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 +149,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/CssSupportsRule.cs b/src/AngleSharp.Css/Dom/Internal/Rules/CssSupportsRule.cs index 70636ee2..fe526808 100644 --- a/src/AngleSharp.Css/Dom/Internal/Rules/CssSupportsRule.cs +++ b/src/AngleSharp.Css/Dom/Internal/Rules/CssSupportsRule.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Parser; 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/StyleUtilityExtensions.cs b/src/AngleSharp.Css/Dom/StyleUtilityExtensions.cs index 8b983da0..e5044e2d 100644 --- a/src/AngleSharp.Css/Dom/StyleUtilityExtensions.cs +++ b/src/AngleSharp.Css/Dom/StyleUtilityExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Attributes; 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/CssApiExtensions.cs b/src/AngleSharp.Css/Extensions/CssApiExtensions.cs index 272520df..c56a1098 100644 --- a/src/AngleSharp.Css/Extensions/CssApiExtensions.cs +++ b/src/AngleSharp.Css/Extensions/CssApiExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Dom { using AngleSharp.Common; diff --git a/src/AngleSharp.Css/Extensions/CssOmExtensions.cs b/src/AngleSharp.Css/Extensions/CssOmExtensions.cs index e9fce611..c2f05b58 100644 --- a/src/AngleSharp.Css/Extensions/CssOmExtensions.cs +++ b/src/AngleSharp.Css/Extensions/CssOmExtensions.cs @@ -1,6 +1,9 @@ +#nullable disable 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 +13,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 +81,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..8573b96e 100644 --- a/src/AngleSharp.Css/Extensions/CssValueExtensions.cs +++ b/src/AngleSharp.Css/Extensions/CssValueExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Dom { using AngleSharp.Css.Values; @@ -16,14 +17,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 +74,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 +101,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 +128,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 +155,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 +175,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 +209,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 +236,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 +249,7 @@ public static Int32 AsRgba(this ICssValue value) return special.Value.AsRgba(); } - return Color.Black.Value; + return CssColorValue.Black.Value; } /// @@ -245,7 +321,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 +345,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/DeclarationInfoExtensions.cs b/src/AngleSharp.Css/Extensions/DeclarationInfoExtensions.cs index e840bd35..5355535a 100644 --- a/src/AngleSharp.Css/Extensions/DeclarationInfoExtensions.cs +++ b/src/AngleSharp.Css/Extensions/DeclarationInfoExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; @@ -16,12 +17,14 @@ public static ICssValue Collapse(this DeclarationInfo info, IDeclarationFactory var initial = true; var unset = true; var child = true; + var inherit = true; foreach (var longhand in longhands) { initial = initial && longhand is CssInitialValue; unset = unset && longhand is CssUnsetValue; child = child && longhand is CssChildValue; + inherit = inherit && longhand is CssInheritValue; } if (initial) @@ -32,6 +35,10 @@ public static ICssValue Collapse(this DeclarationInfo info, IDeclarationFactory { return new CssUnsetValue(info.InitialValue); } + else if (inherit) + { + return CssInheritValue.Instance; + } else if (child) { return ((CssChildValue)longhands[0]).Parent; @@ -58,6 +65,19 @@ public static ICssValue[] Expand(this DeclarationInfo info, IDeclarationFactory .OfType() .ToArray(); } + else if (value is CssInheritValue) + { + return Enumerable + .Repeat(value, longhands.Length) + .ToArray(); + } + else if (value is CssUnsetValue) + { + return longhands + .Select(name => new CssUnsetValue(factory.Create(name)?.InitialValue)) + .OfType() + .ToArray(); + } return info.Aggregator?.Split(value); } diff --git a/src/AngleSharp.Css/Extensions/ElementExtensions.cs b/src/AngleSharp.Css/Extensions/ElementExtensions.cs index 7e687f21..5c54304b 100644 --- a/src/AngleSharp.Css/Extensions/ElementExtensions.cs +++ b/src/AngleSharp.Css/Extensions/ElementExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Dom { using AngleSharp.Attributes; @@ -47,9 +48,17 @@ public static String GetInnerText(this IElement element) hidden = true; } + // Build the style collection once and cache computed styles for the + // entire traversal to avoid redundant stylesheet enumeration and + // repeated selector matching for the same elements. + var document = element.Owner; + var window = document?.DefaultView; + var styleCollection = window != null ? window.GetStyleCollection() : null; + var styleCache = new Dictionary(); + if (!hidden.HasValue) { - var css = element.ComputeCurrentStyle(); + var css = ComputeCachedStyle(element, styleCollection, styleCache); if (!String.IsNullOrEmpty(css?.GetDisplay())) { @@ -67,7 +76,10 @@ public static String GetInnerText(this IElement element) var offset = 0; var sb = StringBuilderPool.Obtain(); var requiredLineBreakCounts = new Dictionary(); - InnerTextCollection(element, sb, requiredLineBreakCounts, element.ParentElement?.ComputeCurrentStyle()); + var parentStyle = element.ParentElement != null + ? ComputeCachedStyle(element.ParentElement, styleCollection, styleCache) + : null; + InnerTextCollection(element, sb, requiredLineBreakCounts, parentStyle, styleCollection, styleCache); // Remove any runs of consecutive required line break count items at the start or end of results. requiredLineBreakCounts.Remove(0); @@ -87,6 +99,36 @@ public static String GetInnerText(this IElement element) return element.TextContent; } + private static ICssStyleDeclaration ComputeCachedStyle(IElement element, IStyleCollection styleCollection, Dictionary cache) + { + if (styleCollection == null) + { + return null; + } + + if (cache.TryGetValue(element, out var cached)) + { + return cached; + } + + ICssStyleDeclaration style; + var parent = element.ParentElement; + + if (parent != null && cache.TryGetValue(parent, out var parentStyle)) + { + // Parent already computed — use incremental computation that + // skips the O(depth) ancestor walk. + style = styleCollection.ComputeDeclarationsWithParent(element, parentStyle); + } + else + { + style = styleCollection.ComputeDeclarations(element); + } + + cache[element] = style; + return style; + } + /// /// Sets the innerText of an element. /// @@ -149,21 +191,21 @@ public static void SetInnerText(this IElement element, String value) } } - private static void InnerTextCollection(INode node, StringBuilder sb, Dictionary requiredLineBreakCounts, ICssStyleDeclaration parentStyle) + private static void InnerTextCollection(INode node, StringBuilder sb, Dictionary requiredLineBreakCounts, ICssStyleDeclaration parentStyle, IStyleCollection styleCollection, Dictionary styleCache) { if (HasCssBox(node)) { var element = node as IElement; - var elementCss = element?.ComputeCurrentStyle(); - ItcInCssBox(elementCss, parentStyle, node, sb, requiredLineBreakCounts); + var elementCss = element != null ? ComputeCachedStyle(element, styleCollection, styleCache) : null; + ItcInCssBox(elementCss, parentStyle, node, sb, requiredLineBreakCounts, styleCollection, styleCache); } } - private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDeclaration parentStyle, INode node, StringBuilder sb, Dictionary requiredLineBreakCounts) + private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDeclaration parentStyle, INode node, StringBuilder sb, Dictionary requiredLineBreakCounts, IStyleCollection styleCollection, Dictionary styleCache) { var elementHidden = new Nullable(); - if (elementStyle != null) + if (elementStyle is not null) { if (!String.IsNullOrEmpty(elementStyle.GetDisplay())) { @@ -188,7 +230,7 @@ private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDecl foreach (var child in node.ChildNodes) { - InnerTextCollection(child, sb, requiredLineBreakCounts, elementStyle); + InnerTextCollection(child, sb, requiredLineBreakCounts, elementStyle, styleCollection, styleCache); } if (node is IText textElement) @@ -196,17 +238,17 @@ 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) + if (((IElement)node).NextElementSibling is IElement nextSibling) { - var nextSiblingCss = nextSibling.ComputeCurrentStyle(); + var nextSiblingCss = ComputeCachedStyle(nextSibling, styleCollection, styleCache); if (nextSibling is IHtmlTableCellElement && String.IsNullOrEmpty(nextSiblingCss.GetDisplay()) || nextSiblingCss.GetDisplay() == CssKeywords.TableCell) { @@ -214,11 +256,11 @@ 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) + if (((IElement)node).NextElementSibling is IElement nextSibling) { - var nextSiblingCss = nextSibling.ComputeCurrentStyle(); + var nextSiblingCss = ComputeCachedStyle(nextSibling, styleCollection, styleCache); if (nextSibling is IHtmlTableRowElement && String.IsNullOrEmpty(nextSiblingCss.GetDisplay()) || nextSiblingCss.GetDisplay() == CssKeywords.TableRow) { @@ -243,7 +285,7 @@ private static void ItcInCssBox(ICssStyleDeclaration elementStyle, ICssStyleDecl } } - if (elementStyle != null) + if (elementStyle is not null) { if (IsBlockLevelDisplay(elementStyle.GetDisplay())) { @@ -297,105 +339,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/RenderNodeExtensions.cs b/src/AngleSharp.Css/Extensions/RenderNodeExtensions.cs index b381b982..0176bbd6 100644 --- a/src/AngleSharp.Css/Extensions/RenderNodeExtensions.cs +++ b/src/AngleSharp.Css/Extensions/RenderNodeExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.RenderTree { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Extensions/StringExtensions.cs b/src/AngleSharp.Css/Extensions/StringExtensions.cs index b5f45fd5..e899dfa9 100644 --- a/src/AngleSharp.Css/Extensions/StringExtensions.cs +++ b/src/AngleSharp.Css/Extensions/StringExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Common; @@ -18,9 +19,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 +27,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 +67,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); } @@ -66,5 +87,24 @@ public static String CssUrl(this String value) var argument = value.CssString(); return Keywords.Url.CssFunction(argument); } + + /// + /// Converts to lowercase, returning the original string if it is already lowercase. + /// Avoids allocation for the common case of already-lowercase CSS identifiers. + /// + internal static String ToLowerFast(this String value) + { + for (var i = 0; i < value.Length; i++) + { + var c = value[i]; + + if (c >= 'A' && c <= 'Z') + { + return value.ToLowerInvariant(); + } + } + + return value; + } } } diff --git a/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs b/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs index 91570714..5c3f2976 100644 --- a/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs +++ b/src/AngleSharp.Css/Extensions/StyleCollectionExtensions.cs @@ -1,6 +1,8 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; + using AngleSharp.Css.Values; using AngleSharp.Dom; using AngleSharp.Html.Dom; using AngleSharp.Svg.Dom; @@ -36,30 +38,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 +90,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 +111,7 @@ public static ICssStyleDeclaration ComputeCascadedStyle(this IEnumerable + /// Computes the declarations for the given element using a pre-computed + /// parent style for inheritance, avoiding the O(depth) ancestor walk. + /// + /// The styles to use. + /// The element that is questioned. + /// The parent's already-computed style. + /// The style declaration containing all the declarations. + internal static ICssStyleDeclaration ComputeDeclarationsWithParent(this IStyleCollection styles, IElement element, ICssStyleDeclaration parentComputedStyle) + { + var ctx = element.Owner?.Context; + var computedStyle = new CssStyleDeclaration(ctx); + + // Element's own cascaded style (CSS rule matching + inline style). + computedStyle.SetDeclarations(styles.ComputeCascadedStyle(element)); + + // Inherit from the parent's already-computed style instead of walking + // all ancestors individually. The parent style already includes the + // full ancestor inheritance chain. + computedStyle.UpdateDeclarations(parentComputedStyle); + + var context = new CssComputeContext(styles.Device, ctx, computedStyle); + return computedStyle.Compute(context); + } + #endregion #region Helpers - private static 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/StyleSheetExtensions.cs b/src/AngleSharp.Css/Extensions/StyleSheetExtensions.cs index 8fdcca1f..bcc6e655 100644 --- a/src/AngleSharp.Css/Extensions/StyleSheetExtensions.cs +++ b/src/AngleSharp.Css/Extensions/StyleSheetExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Dom { using AngleSharp.Css; diff --git a/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs b/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs index 17b59636..6238c792 100644 --- a/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs +++ b/src/AngleSharp.Css/Extensions/ValueConverterExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Converters { using AngleSharp.Css.Dom; @@ -37,6 +38,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/Extensions/WindowExtensions.cs b/src/AngleSharp.Css/Extensions/WindowExtensions.cs index 48415249..48de3a67 100644 --- a/src/AngleSharp.Css/Extensions/WindowExtensions.cs +++ b/src/AngleSharp.Css/Extensions/WindowExtensions.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Dom { using AngleSharp.Attributes; diff --git a/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs b/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs index 2cdcb0f7..feae9899 100644 --- a/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultDeclarationFactory.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Declarations; @@ -9,7 +10,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 +771,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 +834,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 +1389,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 +1413,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..6636a2f4 100644 --- a/src/AngleSharp.Css/Factories/DefaultDocumentFunctionFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultDocumentFunctionFactory.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; @@ -17,7 +18,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..a062df5f 100644 --- a/src/AngleSharp.Css/Factories/DefaultFeatureValidatorFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultFeatureValidatorFactory.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.FeatureValidators; @@ -15,7 +16,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..54a760b0 100644 --- a/src/AngleSharp.Css/Factories/DefaultPseudoElementFactory.cs +++ b/src/AngleSharp.Css/Factories/DefaultPseudoElementFactory.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; @@ -17,7 +18,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/Factories/StyleAttributeObserver.cs b/src/AngleSharp.Css/Factories/StyleAttributeObserver.cs index 15337de2..7fcf95c5 100644 --- a/src/AngleSharp.Css/Factories/StyleAttributeObserver.cs +++ b/src/AngleSharp.Css/Factories/StyleAttributeObserver.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Dom; 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..687789b0 100644 --- a/src/AngleSharp.Css/Parser/CssBuilder.cs +++ b/src/AngleSharp.Css/Parser/CssBuilder.cs @@ -1,10 +1,13 @@ +#nullable disable namespace AngleSharp.Css.Parser { + using AngleSharp.Common; using AngleSharp.Css.Dom; using AngleSharp.Css.Parser.Tokens; using AngleSharp.Dom; using AngleSharp.Text; using System; + using System.Collections.Generic; /// /// See http://dev.w3.org/csswg/css-syntax/#parsing for details. @@ -13,6 +16,22 @@ sealed class CssBuilder { #region Fields + private static readonly Dictionary AtRuleMap = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { RuleNames.Media, 1 }, + { RuleNames.FontFace, 2 }, + { RuleNames.Keyframes, 3 }, + { RuleNames.Import, 4 }, + { RuleNames.Charset, 5 }, + { RuleNames.Namespace, 6 }, + { RuleNames.Page, 7 }, + { RuleNames.Supports, 8 }, + { RuleNames.ViewPort, 9 }, + { RuleNames.Document, 10 }, + { RuleNames.CounterStyle, 11 }, + { RuleNames.FontFeatureValues, 12 }, + }; + private readonly CssTokenizer _tokenizer; private readonly CssParserOptions _options; private readonly IBrowsingContext _context; @@ -66,67 +85,26 @@ private ICssRule CreateStyleRule(ICssStyleSheet sheet, CssToken token) private ICssRule CreateAtRule(ICssStyleSheet sheet, CssToken token) { - if (token.Data.Is(RuleNames.Media)) - { - var rule = new CssMediaRule(sheet); - return CreateMedia(rule, token); - } - else if (token.Data.Is(RuleNames.FontFace)) - { - var rule = new CssFontFaceRule(sheet); - return CreateFontFace(rule, token); - } - else if (token.Data.Is(RuleNames.Keyframes)) - { - var rule = new CssKeyframesRule(sheet); - return CreateKeyframes(rule, token); - } - else if (token.Data.Is(RuleNames.Import)) - { - var rule = new CssImportRule(sheet); - return CreateImport(rule, token); - } - else if (token.Data.Is(RuleNames.Charset)) - { - var rule = new CssCharsetRule(sheet); - return CreateCharset(rule, token); - } - else if (token.Data.Is(RuleNames.Namespace)) + if (AtRuleMap.TryGetValue(token.Data, out var ruleId)) { - var rule = new CssNamespaceRule(sheet); - return CreateNamespace(rule, token); - } - else if (token.Data.Is(RuleNames.Page)) - { - var rule = new CssPageRule(sheet); - return CreatePage(rule, token); - } - else if (token.Data.Is(RuleNames.Supports)) - { - var rule = new CssSupportsRule(sheet); - return CreateSupports(rule, token); - } - else if (token.Data.Is(RuleNames.ViewPort)) - { - var rule = new CssViewportRule(sheet); - return CreateViewport(rule, token); - } - else if (token.Data.Is(RuleNames.Document)) - { - var rule = new CssDocumentRule(sheet); - return CreateDocument(rule, token); - } - else if (token.Data.Is(RuleNames.CounterStyle)) - { - var rule = new CssCounterStyleRule(sheet); - return CreateCounterStyle(rule, token); - } - else if (token.Data.Is(RuleNames.FontFeatureValues)) - { - var rule = new CssFontFeatureValuesRule(sheet); - return CreateFontFeatureValues(rule, token); + switch (ruleId) + { + case 1: return CreateMedia(new CssMediaRule(sheet), token); + case 2: return CreateFontFace(new CssFontFaceRule(sheet), token); + case 3: return CreateKeyframes(new CssKeyframesRule(sheet), token); + case 4: return CreateImport(new CssImportRule(sheet), token); + case 5: return CreateCharset(new CssCharsetRule(sheet), token); + case 6: return CreateNamespace(new CssNamespaceRule(sheet), token); + case 7: return CreatePage(new CssPageRule(sheet), token); + case 8: return CreateSupports(new CssSupportsRule(sheet), token); + case 9: return CreateViewport(new CssViewportRule(sheet), token); + case 10: return CreateDocument(new CssDocumentRule(sheet), token); + case 11: return CreateCounterStyle(new CssCounterStyleRule(sheet), token); + case 12: return CreateFontFeatureValues(new CssFontFeatureValuesRule(sheet), token); + } } - else if (_options.IsIncludingUnknownRules) + + if (_options.IsIncludingUnknownRules) { return CreateUnknownAtRule(sheet, token); } @@ -491,7 +469,7 @@ public TextPosition CreateRules(CssStyleSheet sheet) token = NextToken(); CollectTrivia(ref token); - if (rule != null) + if (rule is not null) { sheet.Add(rule); } @@ -524,15 +502,49 @@ 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)) { var name = token.Data; - while (token.Type == CssTokenType.Delim) + if (token.Type == CssTokenType.Delim) { - token = NextToken(); - name += token.Data; + var sb = StringBuilderPool.Obtain(); + sb.Append(name); + + while (token.Type == CssTokenType.Delim) + { + token = NextToken(); + sb.Append(token.Data); + } + + name = sb.ToPool(); } token = NextToken(); diff --git a/src/AngleSharp.Css/Parser/CssParser.cs b/src/AngleSharp.Css/Parser/CssParser.cs index afa35d2c..f917a370 100644 --- a/src/AngleSharp.Css/Parser/CssParser.cs +++ b/src/AngleSharp.Css/Parser/CssParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -203,7 +204,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..649e0721 100644 --- a/src/AngleSharp.Css/Parser/CssTokenizer.cs +++ b/src/AngleSharp.Css/Parser/CssTokenizer.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Common; @@ -64,39 +65,67 @@ public String ContentFrom(Int32 position) var sb = StringBuilderPool.Obtain(); Back(Position - position); var current = Current; - var spaced = 0; + var trailingWhitespace = 0; + var previous = Symbols.EndOfFile; - while (!(current is Symbols.EndOfFile or Symbols.Semicolon or Symbols.CurlyBracketOpen or Symbols.CurlyBracketClose)) + while (current != Symbols.EndOfFile) { - var token = Data(current); - - if (token.Type == CssTokenType.Whitespace) + if (current is Symbols.DoubleQuote or Symbols.SingleQuote && previous != Symbols.ReverseSolidus) { - spaced++; + trailingWhitespace = 0; + var quote = current; sb.Append(current); current = GetNext(); + + while (current != Symbols.EndOfFile && current != quote) + { + if (current == Symbols.ReverseSolidus) + { + sb.Append(current); + current = GetNext(); + + if (current == Symbols.EndOfFile) + { + break; + } + } + + sb.Append(current); + current = GetNext(); + } + + if (current != Symbols.EndOfFile) + { + sb.Append(current); + previous = current; + current = GetNext(); + } + } + else if (current is Symbols.Semicolon or Symbols.CurlyBracketOpen or Symbols.CurlyBracketClose) + { + break; } else { - var length = Position - position; - Back(length++); - current = Current; - spaced = 0; + sb.Append(current); - while (length > 0) + if (current.IsSpaceCharacter()) { - sb.Append(current); - --length; - current = GetNext(); + trailingWhitespace++; } + else + { + trailingWhitespace = 0; + } + + previous = current; + current = GetNext(); } - - position = Position; } - if (spaced > 0) + if (trailingWhitespace > 0) { - sb.Remove(sb.Length - spaced, spaced); + sb.Remove(sb.Length - trailingWhitespace, trailingWhitespace); } Back(); @@ -1333,7 +1362,7 @@ private CssToken NewOpenRound() private CssToken NewString(String value, Boolean bad = false) { - return new CssStringToken(value, bad) { Position = _position }; + return new CssToken(CssTokenType.String, value) { Position = _position }; } private CssToken NewHash(String data) @@ -1343,7 +1372,7 @@ private CssToken NewHash(String data) private CssToken NewComment(String data, Boolean bad = false) { - return new CssCommentToken(data, bad) { Position = _position }; + return new CssToken(CssTokenType.Comment, data) { Position = _position }; } private CssToken NewAtKeyword(String data) @@ -1373,7 +1402,7 @@ private CssToken NewDimension(String data) private CssToken NewUrl(String data, Boolean bad = false) { - return new CssUrlToken(data, bad) { Position = _position }; + return new CssToken(CssTokenType.Url, data) { Position = _position }; } private CssToken NewRange(String data) @@ -1383,7 +1412,7 @@ private CssToken NewRange(String data) private CssToken NewWhitespace(Char c) { - return new CssToken(CssTokenType.Whitespace, c.ToString()) { Position = _position }; + return new CssToken(CssTokenType.Whitespace, CachedCharString(c)) { Position = _position }; } private CssToken NewNumber(String data) @@ -1393,7 +1422,26 @@ private CssToken NewNumber(String data) private CssToken NewDelimiter(Char c) { - return new CssToken(CssTokenType.Delim, c.ToString()) { Position = _position }; + return new CssToken(CssTokenType.Delim, CachedCharString(c)) { Position = _position }; + } + + private static readonly String[] CharStringCache = CreateCharStringCache(); + + private static String[] CreateCharStringCache() + { + var cache = new String[128]; + + for (var i = 0; i < cache.Length; i++) + { + cache[i] = ((Char)i).ToString(); + } + + return cache; + } + + private static String CachedCharString(Char c) + { + return c < 128 ? CharStringCache[c] : c.ToString(); } private CssToken NewEof() diff --git a/src/AngleSharp.Css/Parser/Micro/CalcParser.cs b/src/AngleSharp.Css/Parser/Micro/CalcParser.cs index 05b9085f..49bbd36d 100644 --- a/src/AngleSharp.Css/Parser/Micro/CalcParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/CalcParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -141,8 +142,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 +155,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..033accc3 100644 --- a/src/AngleSharp.Css/Parser/Micro/ColorParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ColorParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Values; @@ -11,7 +12,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 +21,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 +46,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 +68,7 @@ static class ColorParser return null; } - return Color.FromName(ident); + return CssColorValue.FromName(ident); } return null; @@ -72,7 +77,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 +88,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 +96,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 +155,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 +165,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 +173,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 +186,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 +195,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 +203,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 +214,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 +367,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 +377,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 +385,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 +479,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..194a0a9e 100644 --- a/src/AngleSharp.Css/Parser/Micro/CompoundParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/CompoundParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -30,7 +31,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 +42,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 +74,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 +90,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/ConditionParser.cs b/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs index da138abc..9fa66f65 100644 --- a/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ConditionParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs b/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs index 3c0c3802..57b52236 100644 --- a/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/CssUriParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Values; @@ -19,23 +20,14 @@ public static CssUrlValue ParseUri(this StringSource source) { var current = source.SkipSpacesAndComments(); - switch (current) + return current switch { - 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); - } + 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/DocumentFunctionParser.cs b/src/AngleSharp.Css/Parser/Micro/DocumentFunctionParser.cs index 2fe574a6..30cde83d 100644 --- a/src/AngleSharp.Css/Parser/Micro/DocumentFunctionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/DocumentFunctionParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs b/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs index 5ba83e1d..72d269f6 100644 --- a/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/FunctionParser.cs @@ -1,5 +1,7 @@ +#nullable disable namespace AngleSharp.Css.Parser { + using AngleSharp.Css.Dom; using AngleSharp.Css.Values; using AngleSharp.Text; using System; @@ -93,15 +95,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 +111,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 +169,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 +197,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 +209,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..4244900a 100644 --- a/src/AngleSharp.Css/Parser/Micro/GradientParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/GradientParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -11,7 +12,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 +126,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 +303,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 +446,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..f240e20e 100644 --- a/src/AngleSharp.Css/Parser/Micro/GridParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/GridParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -20,7 +21,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 +61,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 +88,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 +110,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 +134,7 @@ public static ICssValue ParseGridTemplate(this StringSource source) if (current == Symbols.SquareBracketClose) { source.Next(); - return new LineNames(names); + return new CssLineNamesValue(names); } } } @@ -166,7 +167,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 +256,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..49799a07 100644 --- a/src/AngleSharp.Css/Parser/Micro/IdentParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/IdentParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -18,7 +19,7 @@ public static class IdentParser public static String ParseNormalizedIdent(this StringSource source) { var result = source.ParseIdent(); - return result != null ? result.ToLowerInvariant() : result; + return result != null ? result.ToLowerFast() : result; } /// @@ -48,6 +49,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 +75,7 @@ public static ICssValue ParseConstant(this StringSource source, IDictionary(ident.ToLowerInvariant(), mode); + return mode as ICssValue ?? new CssConstantValue(ident.ToLowerFast(), mode); } source.BackTo(pos); @@ -69,13 +85,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.ToLowerFast(), mode); } return null; @@ -155,13 +171,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 +307,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/KeyframeParser.cs b/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs index f6ebe328..ca6bc7fa 100644 --- a/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/KeyframeParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; diff --git a/src/AngleSharp.Css/Parser/Micro/MediaParser.cs b/src/AngleSharp.Css/Parser/Micro/MediaParser.cs index d549bf53..4dc331f5 100644 --- a/src/AngleSharp.Css/Parser/Micro/MediaParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/MediaParser.cs @@ -1,9 +1,12 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; using AngleSharp.Text; using System; using System.Collections.Generic; + using System.Diagnostics.Tracing; + using System.Linq; /// /// Represents extensions to for media values. @@ -31,20 +34,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..a8b594dd 100644 --- a/src/AngleSharp.Css/Parser/Micro/MediumParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/MediumParser.cs @@ -1,9 +1,12 @@ +#nullable disable 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 +25,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..cc2a932b 100644 --- a/src/AngleSharp.Css/Parser/Micro/PointParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/PointParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -28,7 +29,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 +70,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 +92,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 +102,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 +118,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 +127,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 +168,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 +176,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 +191,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..fcc5959e 100644 --- a/src/AngleSharp.Css/Parser/Micro/ShadowParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ShadowParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -20,7 +21,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/ShapeParser.cs b/src/AngleSharp.Css/Parser/Micro/ShapeParser.cs index 10277f35..4a84bf94 100644 --- a/src/AngleSharp.Css/Parser/Micro/ShapeParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/ShapeParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Values; diff --git a/src/AngleSharp.Css/Parser/Micro/StringParser.cs b/src/AngleSharp.Css/Parser/Micro/StringParser.cs index 9015f7f4..e6314918 100644 --- a/src/AngleSharp.Css/Parser/Micro/StringParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/StringParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Text; @@ -16,13 +17,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..b67ae68b --- /dev/null +++ b/src/AngleSharp.Css/Parser/Micro/SymbolsParser.cs @@ -0,0 +1,64 @@ +#nullable disable +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..96e88e0f 100644 --- a/src/AngleSharp.Css/Parser/Micro/TimingFunctionParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/TimingFunctionParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Values; @@ -10,7 +11,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..b0e251f5 100644 --- a/src/AngleSharp.Css/Parser/Micro/TransformParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/TransformParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -11,7 +12,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 +70,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 +149,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 +185,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 +215,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 +224,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 +233,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 +264,7 @@ private static CssScaleValue ParseScale2d(StringSource source) if (x.HasValue) { - var y = default(Double?); + var y = default(ICssValue); if (f == Symbols.Comma) { @@ -273,7 +274,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 +292,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 +332,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 +349,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 +366,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..cc0b0740 100644 --- a/src/AngleSharp.Css/Parser/Micro/UnitParser.cs +++ b/src/AngleSharp.Css/Parser/Micro/UnitParser.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css.Parser { using AngleSharp.Css.Dom; @@ -32,7 +33,7 @@ public static ICssValue ParseAutoLength(this StringSource source) { if (source.IsIdentifier(CssKeywords.Auto)) { - return Length.Auto; + return CssLengthValue.Auto; } return null; @@ -41,11 +42,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 +57,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 +72,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 +96,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 +144,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 +174,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 +217,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 +233,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 +253,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 +276,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 +300,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 +324,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 +350,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..9aa6a766 100644 --- a/src/AngleSharp.Css/RenderTree/RenderTreeBuilder.cs +++ b/src/AngleSharp.Css/RenderTree/RenderTreeBuilder.cs @@ -1,12 +1,17 @@ +#nullable enable annotations +#nullable disable warnings 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 +20,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 +30,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..9210c435 100644 --- a/src/AngleSharp.Css/ValueConverters.cs +++ b/src/AngleSharp.Css/ValueConverters.cs @@ -1,3 +1,4 @@ +#nullable disable namespace AngleSharp.Css { using AngleSharp.Css.Converters; @@ -61,12 +62,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 +85,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