Skip to content

Commit 85d6098

Browse files
author
Virali Purbey
committed
Address review feedback on SVG Path Data API explainer
- Fix security claim: setPathData() operates on structured data, no string parsing needed (does not go through setAttribute) - Add arc-to-cubic normalization precision note - Document empty path behavior: getPathData() returns [], setPathData([]) clears the path - Document getPathSegmentAtLength edge cases: null for empty/NaN, clamp negative to 0 - Split before/after example into separate code blocks to avoid variable redeclaration - Fix Chrome 48 date from 2015 to Jan 2016 (stable release date) - Fix spec PR status wording: 'to be filed' instead of 'pending'
1 parent bfc74b0 commit 85d6098

File tree

1 file changed

+23
-4
lines changed

1 file changed

+23
-4
lines changed

SVG/SVGPathDataAPI/explainer.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ The API is specified in the [SVG Paths](https://svgwg.org/specs/paths/#DOMInterf
4747

4848
SVG `<path>` elements define their shape through a `d` attribute string. Today in Chrome, the only way to inspect or modify individual path segments is to parse this raw string manually or include a polyfill. This can result in slower interactions, larger page loads, and degraded UX - especially on low-end devices.
4949

50-
Chromium removed the old `SVGPathSegList` API in **Chrome 48** (2015) because it was overly complex and poorly specified. The SVG WG specified a cleaner replacement, but it has not yet been implemented in Chrome - a gap that has persisted for over 10 years.
50+
Chromium removed the old `SVGPathSegList` API in **Chrome 48** (Jan 2016) because it was overly complex and poorly specified. The SVG WG specified a cleaner replacement, but it has not yet been implemented in Chrome - a gap that has persisted for over 10 years.
5151

5252
| Engine | Old API (`SVGPathSegList`) | New API (`getPathData`/`setPathData`) |
5353
|--------|---------------------------|---------------------------------------|
54-
| Chrome | ❌ Removed 2015 | ❌ Not yet |
54+
| Chrome | ❌ Removed Jan 2016 | ❌ Not yet |
5555
| Firefox | ❌ Removed 2018 | ✅ Shipped Jan 2025 |
5656
| Safari | ✅ Still supported | ❌ Not yet |
5757

@@ -102,6 +102,9 @@ const segments = path.getPathData();
102102

103103
// Normalize: all segments converted to absolute M, L, C, Z
104104
const normalized = path.getPathData({normalize: true});
105+
106+
// Empty or missing d attribute returns an empty array
107+
emptyPath.getPathData(); // → []
105108
```
106109

107110
#### `setPathData(pathData)` - write segments (accepts POJOs)
@@ -113,19 +116,33 @@ path.setPathData([
113116
{type: "L", values: [50, 100]},
114117
{type: "Z", values: []}
115118
]);
119+
120+
// Passing an empty array clears the path (equivalent to setAttribute('d', ''))
121+
path.setPathData([]);
116122
```
117123

118124
#### `getPathSegmentAtLength(distance)` - segment at distance
119125

120126
```js
121127
path.getPathSegmentAtLength(50);
122128
// → {type: "C", values: [40, 10, 65, 10, 95, 80]}
129+
130+
// Returns null if the path is empty or has no length
131+
emptyPath.getPathSegmentAtLength(10); // → null
132+
133+
// Negative distances clamp to 0 (returns the first segment), matching getPointAtLength() behavior
134+
path.getPathSegmentAtLength(-10); // → {type: "M", values: [10, 80]}
135+
136+
// NaN returns null
137+
path.getPathSegmentAtLength(NaN); // → null
123138
```
124139

125140
All 20 SVG path commands (M, m, L, l, H, h, V, v, C, c, S, s, Q, q, T, t, A, a, Z, z) are supported. See the [spec](https://svgwg.org/specs/paths/#DOMInterfaces) for the full type/values mapping.
126141

127142
**Normalization** (`{normalize: true}`) converts all segments to absolute **M, L, C, Z** only - relative to absolute, H/V to L, Q/T to C, S to C, A to C. Consumers need only handle 4 command types.
128143

144+
> **Note:** Arc-to-cubic conversion (A → C) is an approximation using midpoint subdivision and is inherently lossy. The precision matches the existing `getTotalLength()`/`getPointAtLength()` code path in Blink. For most use cases the approximation error is sub-pixel.
145+
129146
### Before and after
130147

131148
```js
@@ -134,7 +151,9 @@ const d = path.getAttribute('d');
134151
const segments = myCustomParser(d); // or load ~4KB polyfill
135152
segments[1].values[0] = 50;
136153
path.setAttribute('d', myCustomSerializer(segments));
154+
```
137155

156+
```js
138157
// AFTER: native, zero dependencies
139158
const segments = path.getPathData();
140159
segments[1].values[0] = 50;
@@ -186,7 +205,7 @@ The formal WebIDL is in the [Appendix](#appendix-webidl).
186205
- **Accessibility:** No impact. Programmatic API only - no new visual content, interaction patterns, or ARIA roles. Indirectly benefits a11y by making it easier to build well-structured SVG.
187206
- **Internationalization:** No impact. Path data uses single-character Latin commands and numbers only.
188207
- **Privacy:** No new concerns. Returns the same data available via `getAttribute('d')` - purely a convenience API over existing capabilities. No fingerprinting surface, no network requests.
189-
- **Security:** No new concerns. Operates entirely within the renderer, no IPC, no untrusted data. `setPathData()` goes through the existing hardened `setAttribute("d")` code path. Gated behind a feature flag.
208+
- **Security:** No new concerns. Operates entirely within the renderer on already-structured data. No string parsing is needed (segments are pre-typed), reducing attack surface compared to `setAttribute('d')`. No IPC. Gated behind a feature flag.
190209

191210
---
192211

@@ -235,4 +254,4 @@ partial interface SVGPathElement {
235254
};
236255
```
237256

238-
**Spec text updates (PR pending):** `dictionary` instead of `[NoInterfaceObject] interface` (accepts POJOs natively); `unrestricted float` instead of `float` (matches SVG error model); `required` keywords added (prevents `setPathData([{}])`).
257+
**Spec text updates (spec PR to be filed):** `dictionary` instead of `[NoInterfaceObject] interface` (accepts POJOs natively); `unrestricted float` instead of `float` (matches SVG error model); `required` keywords added (prevents `setPathData([{}])`).

0 commit comments

Comments
 (0)