Skip to content

Release 1.3.2 - #265

Merged
FlorianRappl merged 12 commits into
mainfrom
devel
Nov 3, 2026
Merged

FlorianRappl merged 12 commits into
mainfrom
devel

Conversation

@FlorianRappl

Copy link
Copy Markdown
Contributor

Types of Changes

Prerequisites

Please make sure you can check the following two boxes:

  • I have read the CONTRIBUTING document
  • My code follows the code style of this project

Contribution Type

What types of changes does your code introduce? Put an x in all the boxes that apply:

  • Bug fix (non-breaking change which fixes an issue, please reference the issue id)
  • New feature (non-breaking change which adds functionality, make sure to open an associated issue first)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • My change requires a change to the documentation
  • I have updated the documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed

Description

This pull request introduces several improvements and fixes to the AngleSharp.Css library, focusing on parser robustness, standards compliance, and default styling. The most notable changes include the introduction of a configurable nesting limit for value and condition parsing, enhanced support for CSS functions and properties, updates to default form control styles to better match browsers, and several bug fixes and standards alignments.

These changes collectively improve the reliability, standards compliance, and usability of the AngleSharp.Css library.

michaelthyregod and others added 12 commits November 2, 2026 02:51
A value or condition nesting parentheses deeply ended the process with a
stack overflow, which no caller can catch: the parser recurses once per level
of parentheses and math functions in values (calc(), min(), max(), clamp())
and of parentheses in @media and @supports conditions, also when a value is
set through the CSSOM. On a 256 KB thread stack, about 430 parentheses in
calc() were enough (.NET 8, before the JIT optimizes); on a 1 MB stack about
1,900.

- Add CssParserOptions.MaxValueNestingDepth (default 64). A value or
  condition nested deeper is invalid. The value parsers run inside the
  converters, which do not see the options, so a parser makes its limit
  current while it builds (ValueNesting); values set through the CSSOM
  without a parser get the default
- Add tests at depths that crashed before and at the limit, parsing on a
  256 KB thread stack, and for the option per parser, per browsing context
  and its scope
- Add calc() and @supports shapes to the nesting benchmark

This is a mitigation, not a fix: parsing still recurses once per level, and
only the limit stops it. Raising the limit brings the crash back.
Indexing a rule for the cascade looks into the arguments of :is(),
:where() and :matches(), parsing them again at every level, so the cost
grows quadratically with the nesting: computing a style with 75 nested
:is() took 45 us, 35 us with the cap (allocating 132 KB, 82 KB with it).

- Look into at most 8 levels; a rule nested deeper stays ungrouped, i.e.,
  it is a candidate for every element, which is always correct
- Add tests for the boundary and for matching beyond it
- Add DeepSelectorBenchmarks with 10, 50 and 75 levels
Limit how deep the rule index looks into :is()
A computed style looks inherited declarations up through the snapshots of
its parents. A property that no ancestor declares, e.g. visibility, was
looked up through every ancestor for every element, so GetInnerText and
IsVisible on the render tree grew quadratically with the depth (#262).

- Keep the nearest declaration found for a name, or that there is none,
  on the snapshots passed; they never change, so each one is walked once
  per name. The entries are copied on write and published atomically
- Leave the lookup of explicitly inherited declarations as it was
- Add tests for deep chains, read deepest first and twice
- Add DeepTreeBenchmarks for GetInnerText and IsVisible at 100 and 500
  levels
The cascade allocated a new list for the matching rules of every element
and grew the explicit style one declaration at a time; on a real document
this growth and the garbage it leaves dominate the render tree.

- Reuse the list of matching rules from element to element; it is taken
  and given back atomically, so a concurrent cascade gets its own
- Size the explicit style for the declarations of the matching rules
…ache

Remember inherited lookups on the way up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants