Repository navigation
Conversation
Inline <svg> subtrees can stay live DOM nodes styled by stylo and be painted from the DOM instead of being serialized and re-parsed with usvg. Off by default; the usvg path is unchanged when the switch is off. - stylo: namespace-aware is_html_element / is_svg_element, SVG UA sheet - presentation attributes parsed with stylo's property parser; geometry attributes only on their elements, d via SVGPathData::parse_bytes, transform lists composed into a CSS matrix() - native <svg> root: intrinsic size from width/height/viewBox, no box construction for descendants, child damage stops at the root - blitz-paint: paint basic shapes, <g>, nested <svg> from computed styles
ALLOW_ALL_NUMERIC_VALUES is for SMIL; a negative rx/r must be dropped (computing to auto), not accepted.
Contributor
|
I'll fix CI failures and address comments from users with write access that start with 'Devin'.
|
The group opacity layer was pushed with an identity transform while its bounds were the root's padding box in the pre-transform coordinate space, so groups scrolled or offset from the origin were clipped away. The `@namespace svg` declaration sat after the first style rule in the UA stylesheet, which makes it invalid, so every `svg|...` UA rule was dropped and nested <svg> viewports never got `overflow: hidden`. Move it to the top with the other namespace declarations, and clip nested viewports in the outer coordinate system. The screenshot example honours `BLITZ_NATIVE_SVG=1` like the WPT runner.
svg_presentation_hint returned a fresh Vec<PropertyDeclaration> for every attribute. It now parses straight into a SourcePropertyDeclaration (stylo's stack-allocated per-declaration buffer) owned by the caller and reused across all attributes of an element; the caller drains it into the element's PropertyDeclarationBlock.
Per the scope discussion, each SVG backend gets its own cargo feature. `svg-usvg` is the existing usvg path for inline <svg> and SVG images, with `svg` kept as an alias; `svg-native` is the live-DOM path from this PR and compiles without usvg (`SvgIntrinsicDimensions` now uses its own `SvgViewBox` / `SvgTransform` types instead of usvg's). With both compiled, `DocumentConfig::native_svg` picks the inline renderer per document (default usvg); with one compiled the flag is ignored. dioxus-native, the WPT runner, blitz-tests and the examples forward the new feature, and CI gets clippy passes over blitz-dom and blitz-paint with only `svg-native` so usvg types cannot creep back into the native path. Also fixes the `needless_bool` lints the current stable clippy raises on main, and two lints the svg-native-only build exposed.
CI's WPT run (default usvg backend) regressed in three ways once both
backends were compiled in:
- SVG presentation hints applied in usvg mode too, so the outer <svg>'s
`transform` attribute became a CSS transform on the box on top of usvg's
own transform of the content. The hints are now only synthesized when the
document renders SVG natively (the flag is mirrored on `NodeTree`, which
is all styling has access to).
- The UA sheet's `svg:not(:root) { overflow: hidden }` finally took effect
(the `@namespace` fix) and clipped the outer <svg>'s box, cutting off
content usvg had transformed outside it. That rule moves to
`NATIVE_SVG_CSS`, a UA sheet added only to native-SVG documents.
- `document.querySelector('animateMotion')` now finds SVG elements (type
selectors match them case-sensitively), so three SMIL tests wait for
events Blitz never fires instead of throwing: quarantined as
`animation-events`.
svg_path_to_bezpath mapped stylo's ArcSweep::Cw (the SVG sweep flag 1) to kurbo's sweep = false, so every arc in a path() clip-path or a native SVG <path> was drawn on the wrong side. Found by the resvg suite (shapes/path/M-A*).
- Presentation attributes use the SVG 2 allowlist instead of mapping any attribute that names a CSS property (padding, background, position, ...). - A path arc with coincident end points is omitted and one with a zero radius is a line, instead of dropping the whole path. - The native UA rule for the outer <svg> is `overflow: clip`, so a replaced element is not a scroll container and keeps its automatic minimum size as a flex item. - Group opacity layers are only bounded by the root box when the root clips. - <switch> paints its first child element. - <line> x1/y1/x2/y2 resolve percentages and units. - The damage boundary tests run in an svg-native-only build too.
This branch has not been deployed
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.
Milestone 1 (foundations) of native SVG rendering. Each SVG backend is now its own cargo feature:
svg-usvgis the existing path (inline<svg>serialized and re-parsed by usvg, plus<img src="x.svg">and CSS background SVGs), withsvgkept as an alias;svg-nativekeeps an inline<svg>subtree in the DOM as live nodes, stylo styles them, and blitz-paint draws the shapes from their computed styles. The native feature compiles without usvg (SvgIntrinsicDimensionshas its ownSvgViewBox/SvgTransformtypes, and CI clippy-checks blitz-dom and blitz-paint with onlysvg-native). When both features are compiled,DocumentConfig { native_svg: Some(true), .. }picks the native renderer per document; the default is usvg, so nothing changes for anyone until the flag is set. With one backend compiled the flag is ignored.<img>/background SVGs stay on usvg either way.dioxus-nativeforwardssvg-native; the WPT runner and thescreenshotexample compile both backends and switch withBLITZ_NATIVE_SVG=1. Nothing about the usvg workaround PRs (#587, #1267, #1320, #1266, #1510) is touched.What the native path does:
is_html_element/is_svg_element/is_html_element_in_html_documentnow check the element namespace (ns!(html)/ns!(svg)) instead of always answering "HTML". The UA sheet getssvg|rules foroverflowon inner viewports,transform-origin, and non-renderedtitle/desc/metadata/script/style. (The@namespace svgdeclaration has to sit with the other@namespacerules at the top of the sheet: declared after a style rule it is invalid and everysvg|rule silently drops, which is what the first version of this PR did.) Resource containers (defs, gradients,clipPath,mask,marker,pattern,symbol,filter) are deliberately notdisplay: none: stylo wouldn't style their children, and later milestones need those computed styles. The painter skips them by tag instead.stylo/svg_attrs.rs), so the cascade order is right for free (attribute < author CSS). Each attribute is parsed into one stack-allocatedSourcePropertyDeclarationthat is reused across the element's attributes and drained into its declaration block, so there is no per-attribute allocation. Geometry attributes are mapped only on the elements they belong to (cx/cyon circle/ellipse,ron circle,rx/ryon rect/ellipse,x/y/width/heighton rect/image/foreignObject/svg,don path); elsewhere they stay plain attributes.dgoes throughSVGPathData::parse_bytes, so a malformed suffix keeps the valid prefix as SVG requires.transform="..."is composed into a singlematrix(...)declaration. Out-of-range values (negativerx,r) are dropped so the property staysauto/initial rather than clamped.<svg>root is a replaced box withSpecialElementData::SvgRoot(SvgIntrinsicDimensions)computed fromwidth/height/viewBox/preserveAspectRatio(the same struct the usvg path uses, now built from attributes instead of an xmltree). No boxes are constructed for descendants.<svg>root is a boundary. Descendant damage is reduced toREPAINTwhen it reaches the root (the HTML page is never relaid out because a circle moved); only the root's own damage (sizing attributes, style) is layout damage for the ancestors. Inrestyle_for_attribute_change, only the root'swidth/height/viewBox/preserveAspectRatioare layout-read in native mode; other SVG attribute changes mark repaint damage. The old "any SVG attribute reconstructs the whole SVG" behaviour is kept for usvg mode.blitz-paint/src/render/svg.rswalks the live subtree and paintsrect/circle/ellipse/line/polyline/polygon/path,g/a, and nested<svg>viewports, honouringtransform,fill/fill-rule/fill-opacity,strokewith width/caps/joins/dasharray/offset/opacity, groupopacity,visibility/display,currentColor, andoverflowclipping on inner viewports.Known gaps in what is here:
transform-originpercentages and keywords on SVG content resolve against an empty reference box (there is notransform-boxsupport yet), sotransform-origin: centerrotates about the user-space origin while lengths work; presentation-hint URLs (fill="url(#g)") are parsed against a placeholder base URL until paint servers land;<switch>paints its first child without evaluating conditional attributes. The namespace-awareis_svg_element/is_html_elementand the shared UA rules (svg|titleetc. hidden,transform-origin: 0 0on SVG content) apply to computed styles in usvg mode too; CI's WPT diff shows no rendering change from that.Not in this PR (later milestones; these render as nothing in native mode, so leave the flag off if you need them):
<use>, paint servers (gradients/patterns), markers,clip-path/maskon SVG content, filters,<text>,<image>,<foreignObject>content, hit testing of SVG shapes andpathLength. There is no per-<svg>fallback to usvg (decided against in the scope discussion).WPT
svg/**(1520 tests, 15,881 subtests, PR head): main (usvg) 6036 subtests pass, this PR with usvg 6045, this PR native 6281. On the shapes/styling/painting/render/coordinate-systems/geometry/struct-reftests subset (561 tests, 2115 subtests): 944 / 950 / 1180, native +269 / −39 subtests vs usvg on the same build. The native losses (35 tests) are in features listed above (markers,<use>, text, clip-path/mask,pathLength) plussvg/styling/nested-svg-sizing-*(nested<svg>width/heightgiven asauto,calc(),em/rem,%or sizing keywords: the native painter only resolves plain lengths and percentages for nested viewports). CI runs the default (usvg) mode, so the diff below is the usvg-mode change; a second native pass over the SVG directories in CI is a follow-up.The usvg backend's rendering is unchanged: presentation hints are only synthesized for documents that render SVG natively (otherwise the outer
<svg>'stransformattribute would apply twice), and the SVG UA sheet'ssvg:not(:root) { overflow: hidden }lives in a separateNATIVE_SVG_CSSUA sheet added only to those documents (asoverflow: clip, so the replaced<svg>is not a scroll container and keeps its automatic minimum size as a flex item), since usvg transforms the outer<svg>'s content rather than its CSS box. Threesvg/animationsSMIL tests are quarantined (animation-events): now that type selectors match SVG elements, they wait forendEventinstead of throwing.Tests:
tests/blitz-tests/tests/native_svg.rs(both features compiled, switched per document) covers toggle/fallback behaviour, namespace matching, presentation attributes (incl. element-specific geometry and partiald), transforms, intrinsic sizing,currentColor, group opacity, nested-viewport clipping, resource containers (styled but not painted) and repaint-on-attribute-change;layout/damage.rshas unit tests for the root boundary. Also includes theneedless_boolfixes the current stable clippy raises onmain.cargo fmt, the clippy invocations (incl. the two newsvg-native-only ones),cargo test --workspace,cargo docandcargo deny ... check licensespass.Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/5b57528a8ccb4275ab16b1320f65bea5
Link to Bot: https://dioxus.staging.devinenterprise.com/org/dioxus-engineering/bots/3904245e951a4a7fa4631b37b308d89b
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/5b57528a8ccb4275ab16b1320f65bea5?variant=devin-insiders
Painting opened this PR for Nico
Implemented in Build native SVG foundations in blitz-dom · Review with Devin
WPT results
Subtests: 15 newly passing, 0 newly failing (net +15).
Subtest changes by area (11 areas)
Full diff (14 changed tests)
Generated by the WPT workflow.