|
|
Donner 0.5.1
Embeddable browser-grade SVG2 engine
|
Donner supports loading and rendering external .svg files referenced by <image>, <use>, and <feImage> elements. Each element type uses a different rendering strategy:
Sub-documents are parsed in ProcessingMode::SecureStatic mode (SVG2 §2.7.1), which prevents them from loading their own external resources or executing scripts. This enforces a strict document boundary and prevents infinite recursion.
flowchart TD
A["Element with external href"] --> B[ResourceManagerContext::loadResources]
B --> C{MIME type?}
C -->|image/svg+xml| D[SubDocumentCache::getOrParse]
C -->|image/png, etc.| E[ImageLoader / stb_image]
D --> F[SVGParser::ParseSVG in SecureStatic mode]
F --> G[Cached SVGDocumentHandle]
G --> H{Element type?}
H -->|image| I[LoadedSVGImageComponent]
H -->|use| J[ExternalUseComponent]
E --> K[LoadedImageComponent]
I --> L[RendererDriver::drawSubDocument]
J --> M[RendererDriver: drawSubDocument or drawSubDocumentElement]
K --> N[RendererDriver::drawImage]
SubDocumentCache (donner/svg/components/resources/SubDocumentCache.h)
Stored on Registry::ctx(). Caches parsed sub-documents by resolved URL and guards against circular loads via a loading_ set. When getOrParse is called for a URL that is already in the loading set, it returns std::nullopt.
Reference (donner/svg/graph/Reference.h)
Parses href strings into their document URL and fragment components. Supports same-document (#id), external whole-document (file.svg), and external fragment (file.svg#id) references. Data URLs are treated as non-external.
ProcessingMode (donner/svg/core/ProcessingMode.h)
Controls feature availability per SVG2 §2.7.1. Sub-documents use SecureStatic, which disables external resource loading, script execution, and animations.
ResourceManagerContext (donner/svg/components/resources/ResourceManagerContext.h)
Detects image/svg+xml content during resource loading (by MIME type or .svg file extension) and routes SVG content to SubDocumentCache instead of raster image decoding.
RendererDriver (donner/svg/renderer/RendererDriver.cc)
Handles three sub-document rendering paths:
Context paint (context-fill/context-stroke) is passed from the <use> element into the sub-document's rendering context via RenderingContext::setInitialContextPaint.
| Component | Stored on | Purpose |
|---|---|---|
| LoadedSVGImageComponent | <image> entity | Holds SVGDocumentHandle for an external SVG image |
| ExternalUseComponent | <use> entity | Holds SVGDocumentHandle + optional fragment ID |
| LoadedImageComponent | <image> entity | Holds decoded raster image (existing, unchanged) |
flowchart TB
subgraph "Trusted: Parent Document"
A[SVG Parser] --> B[Document Tree]
B --> C[RendererDriver]
end
subgraph "Untrusted: External SVG File"
D[File bytes from ResourceLoader]
D --> E[SVG Parser in SecureStatic mode]
E --> F[Sub-document Tree]
end
C -.->|"Render into parent viewport"| F
B -.->|"href URL"| D
| Threat | Mitigation |
|---|---|
| Path traversal (../../etc/passwd) | SandboxedFileResourceLoader rejects escaping paths |
| Infinite recursion (A→B→A) | SubDocumentCache::isLoading() recursion guard |
| Resource exhaustion | Sub-documents inherit existing parser and loader limits |
| Information leak via sub-document | SecureStatic mode blocks nested external resource loading |
| Cross-origin escalation | Only local file loading is supported; no network fetching |
| Test file | Coverage |
|---|---|
| image-external-svg-basic.svg | Basic external SVG image rendering |
| image-external-svg-viewbox.svg | External SVG with viewBox scaling |
| image-external-svg-par.svg | External SVG with preserveAspectRatio |
| use-external-svg.svg | Whole-document external <use> |
| use-external-svg-fragment.svg | Fragment reference through <use> |
| use-external-context-paint.svg | context-fill/context-stroke inheritance |
| feimage-external-svg.svg | External SVG through <feImage> |
To add a new golden test: create an SVG test file in testdata/, add a reference SVG in testdata/ for the external resource, render the expected output to testdata/golden/, and add an INSTANTIATE_TEST_SUITE_P entry in Renderer_tests.cc.