Repository navigation
Get the inner text from a partial render tree - #267
Merged
FlorianRappl merged 4 commits intoNov 4, 2026
Merged
FlorianRappl merged 4 commits into
FlorianRappl merged 4 commits into
Conversation
GetInnerText computed the styles itself, apart from the render tree, so both had to be optimized (AngleSharp#262). It now walks a render tree of the element, which is only built as far as it is read. - Build the children of a partial render node on first read, and its ::before and ::after only when all children are read - Walk the element's own children, as the spec does: the shadow tree of a host is left out, and its light children count if they are slotted - Add GetInnerText for a render node, e.g. of an existing render tree - Add tests for shadow hosts, generated content, completing a partial tree, and concurrent first reads
FlorianRappl
reviewed
Nov 4, 2026
FlorianRappl
pushed a commit
that referenced
this pull request
Nov 5, 2026
A complete render node is back to the fields it had before #267: the children field holds either the children or, in a partial render tree, what builds them on first read.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Types of Changes
Prerequisites
Please make sure you can check the following two boxes:
Contribution Type
What types of changes does your code introduce? Put an
xin all the boxes that apply:Description
The second step of #262, after #266:
GetInnerTextis computed from a render tree whose children are only built when they are read, so the content of hidden elements is never styled. It replaces the separate style walkGetInnerTexthad.The change
RenderTreeBuilder.RenderPartialreturns a node that builds its children on first read.Contentgives the children without::before/::after, whichGetInnerTextreads;Childrenadds the pseudo-elements. Without this split, the default sheet'sbr::before { content: '\A' }would cascade two pseudo-elements for every<br>. Children are published withInterlocked.CompareExchange, so concurrent first reads get the same ones.Render()is unchanged and still builds the complete tree.GetInnerText(IElement)is the walk's own, so once an element is walked its children are dropped (its siblings stay, as the next sibling decides on a tab or line feed). Otherwise the whole tree stays reachable until the end and every garbage collection has to copy it: without this, the same change was 18–22% slower, all of it in GC pauses.GetInnerText(this ElementRenderNode node)gives the text of a rendered element, from the tree it is given.ElementExtensionsto an internalRenderTree/InnerText.cs.Render(), also whenContentis read first; concurrent first reads (synchronised with aBarrier).The text is the same as before on both
InnerTextBenchmarksdocuments (68,868 and 757,568 characters, same SHA-256).Benchmarks
Paired builds of the same benchmark code against
devel(a1cb93c), alternating which goes first each round. BenchmarkDotNet 0.15.8, net10.0, AngleSharp 1.9.0 for both, pinned to the performance cores of an Intel Core Ultra 7 165H. Median of 6 rounds:InnerTextBenchmarks.GetInnerText_RealWorld_LengthInnerTextBenchmarks.GetInnerText_SelectorHeavy_LengthDeepTreeBenchmarks.InnerText, 100 / 500 levelsDeepTreeBenchmarks.IsVisible, 100 / 500 levelsCssCascadeBenchmarks.RenderTreeCssCascadeBenchmarks.ComputedStyleSelectorHeavyis faster in every round (−6.5% to −12.1%); the real-world document stays within ±3% in every round. The extra allocation is the render nodes and their lists, which are released while walking.RenderTreeandComputedStylechange sign between rounds, within the ±5% that single rounds of the same build differ by.I'll add the CHANGELOG lines under 1.4.0 in a separate commit once this PR has its number: this one, and the one for #266, which was merged before its CHANGELOG commit reached the branch.
🤖 Generated with Claude Code