| Commit-Queue | +1 |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
The existing WPT coverage for `setPathData()` only exercises M/L segments and the empty-array case. Consider adding tests for:
- All segment types: H, V, C, S, Q, T, A, Z (both abs and rel)
- Invalid inputs: unknown type string, wrong values count, NaN/Infinity values, very large values
- Round-trip fidelity: setPathData(getPathData()) preserves geometry
Invalid segments (unknown type, wrong values count) are silently
skipped. Empty array clears the path. `NaN/Infinity` pass through
per SVG's unrestricted float model.By silently skipping invalid segments, arent we producing a different valid path from what the caller intended.
For example, [{type:"M", values:[0,0]}, {type:"BOGUS", values:[]}, {type:"L", values:[10,10]}] silently becomes "M 0 0 L 10 10" rather than signaling that segment 2 was invalid.
Fix options:
- (a) Serialize all segments as-is and let the d attribute parser handle errors per SVG error-handling rules (it will stop at the first invalid token).
- (b) If filtering is intentional for web-compat with other implementations, add a spec reference or comment justifying it, and ensure WPT tests pin this behavior.
Measured via UseCounter kSVGPathElementSetPathData.should we consider adding tests for the newly added usecounter too?
[1] https://svgwg.org/specs/paths/#DOMInterfacessame comment as earlier CL, probably a good idea to link the design doc you created
CORE_EXPORT Stringis `CORE_EXPORT` needed if this function is only used by `svg_path_element.cc` within the same component?
if (ch == 'z') {
return kPathSegClosePath;
}cant we add this in the lookup itself?
int expected_count = ValuesCountForSegType(type);should this be const?
builder.AppendNumber(value);`unrestricted float` allows NaN/Infinity through WebIDL. `AppendNumber` serializes these as "nan" / "inf", not valid SVG path numeric tokens. The subsequent `setAttribute` to parse round-trip will fail at those tokens.
Should we consider replacing with non-finite values with 0 or skip the segment if any value is non-finite?
This is a testharness.js-based test.
[FAIL] SVGPathSegment interface exists
assert_not_equals: got disallowed value undefinedwe plan to fix this too?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
will this api require serialization related unit tests?
There are failures in your CQ dry run, please consider fixing them before raising for review
There are failures in your CQ dry run, please consider fixing them before raising for review
Done
The existing WPT coverage for `setPathData()` only exercises M/L segments and the empty-array case. Consider adding tests for:
- All segment types: H, V, C, S, Q, T, A, Z (both abs and rel)
- Invalid inputs: unknown type string, wrong values count, NaN/Infinity values, very large values
- Round-trip fidelity: setPathData(getPathData()) preserves geometry
Done
will this api require serialization related unit tests?
Done
Invalid segments (unknown type, wrong values count) are silently
skipped. Empty array clears the path. `NaN/Infinity` pass through
per SVG's unrestricted float model.By silently skipping invalid segments, arent we producing a different valid path from what the caller intended.
For example, [{type:"M", values:[0,0]}, {type:"BOGUS", values:[]}, {type:"L", values:[10,10]}] silently becomes "M 0 0 L 10 10" rather than signaling that segment 2 was invalid.
Fix options:
- (a) Serialize all segments as-is and let the d attribute parser handle errors per SVG error-handling rules (it will stop at the first invalid token).
- (b) If filtering is intentional for web-compat with other implementations, add a spec reference or comment justifying it, and ensure WPT tests pin this behavior.
Good catch, thanks. Switched to valid-prefix semantics: the first invalid segment and everything after it is dropped, matching the d-attribute parser's behavior. Pinned in `setPathData-malformed.html` for unknown type, wrong arity, NaN/Infinity, and non-binary arc flag.
should we consider adding tests for the newly added usecounter too?
Done
same comment as earlier CL, probably a good idea to link the design doc you created
Done
is `CORE_EXPORT` needed if this function is only used by `svg_path_element.cc` within the same component?
Done
cant we add this in the lookup itself?
Updated the code.
int expected_count = ValuesCountForSegType(type);Virali Purbeyshould this be const?
Done
`unrestricted float` allows NaN/Infinity through WebIDL. `AppendNumber` serializes these as "nan" / "inf", not valid SVG path numeric tokens. The subsequent `setAttribute` to parse round-trip will fail at those tokens.
Should we consider replacing with non-finite values with 0 or skip the segment if any value is non-finite?
Done
This is a testharness.js-based test.
[FAIL] SVGPathSegment interface exists
assert_not_equals: got disallowed value undefinedwe plan to fix this too?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Set Ready For Review
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
some of the test case are failing in the new patchset, PTAL
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Should we consider adding a <use> invalidation regression test since setAttribute("d") is the mechanism for propagation
</script>Missing test cases:
1- malformed first moveto. If the first segment is `{type:"M", values:[0]}` (wrong arity), the byte stream should be empty and d should reset. This is a distinct code path from "leading non-moveto".
2- invalid sweep flag. Only `largeArcFlag` `(values[3])` invalid is tested. The `sweepFlag` `(values[4])` invalid branch is untested.
];Missing test case -> single moveto only. No test for the minimal valid non-empty path.
// SVG Paths §9.7). A leading non-moveto yields an empty stream.nit: probably a good idea to mention the link itself
some of the test case are failing in the new patchset, PTAL
Resolved.
Should we consider adding a <use> invalidation regression test since setAttribute("d") is the mechanism for propagation
I don't think this is required here since we didn't add that flow. Will follow up in another CL if needed.
// SVG Paths §9.7). A leading non-moveto yields an empty stream.nit: probably a good idea to mention the link itself
Done
Missing test cases:
1- malformed first moveto. If the first segment is `{type:"M", values:[0]}` (wrong arity), the byte stream should be empty and d should reset. This is a distinct code path from "leading non-moveto".
2- invalid sweep flag. Only `largeArcFlag` `(values[3])` invalid is tested. The `sweepFlag` `(values[4])` invalid branch is untested.
Added these tests.
Missing test case -> single moveto only. No test for the minimal valid non-empty path.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Should this file just contain a class that implements the "normal" path source interface:
```
class PathSourceInterface {
public:
bool HasMoreData() const;
PathSegmentData ParseSegment();
};
```
and then use the regular "path parsing driver" (`ParsePath()`) instead?
It also feels like the name of this file should be `svg_path_segments_source.{cc,h}` to match up with the builder.
bool BuildPathSegmentData(SVGPathSegType type,I'd suggest splitting this into a "validate" and a "build". `ParseSegmentType` could be split in a similar way. With a table the "validate" function could be very simple.
for (float value : values) {
if (!std::isfinite(value)) {
return false;
}
}```
if (!std::ranges::all_of(values, std::isfinite)) {
return false;
}
```
if (values[3] != 0.0f && values[3] != 1.0f) {I'd perhaps expect this to be more lax, so more towards `ToBoolean()` in ECMAScript, so zero is false and everything else is true. That would eliminate the need for this validation.
return false;The `NOTREACHED()` should be here.
I think we can drop this blank line. Having `getPathData()` right next to/grouped with `setPathData` feels reasonable?
// TODO(crbug.com/40441025): Write the byte stream directly into SVGPath
// once lazy attribute sync fires Will/DidModifyAttribute.I don't think this should be blocked on this - it's a pre-existing issue.
? g_empty_atomI guess this needs to be defined. I.e if setting an empty list should set the attribute to an empty string or remove it. For list-valued attributes I believe we have the latter behavior, and this is sort of that.
// https://svgwg.org/specs/paths/#InterfaceSVGPathElementRestore.
// TODO(crbug.com/40441025): getPathSegmentAtLength() is not yet
// implemented in Chromium; this assertion currently fails and is
// baselined in SVGPathSegment-expected.txt.This shouldn't go in the test. Remove.
assert_equals(path.getAttribute("d"), "M 0 0 L 10 10",How the path data should be serialized when `setPathData()` is called needs to be specified.
function waitForAnimationFrame() {
return new Promise(resolve => requestAnimationFrame(() => {
requestAnimationFrame(resolve);
}));
}There's `waitForAtLeastOneFrame()` in `/common/rendering-utils.js`.
{ type: "BOGUS", values: [] },Maybe also test a single invalid character
assert_equals(segs.length, 2);
assert_equals(segs[0].type, "M");
assert_equals(segs[1].type, "L");Could these tests use the helper that was added in the previous CL? (Here and below.)
const path = createPath();
// Arc flags must be exactly 0 or 1.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "A", values: [25, 25, 0, 2, 1, 50, 50] }
]);
const segs = path.getPathData();
assert_equals(segs.length, 1, "non-binary arc flag is invalid");
assert_equals(segs[0].type, "M");
}, "setPathData() with a non-binary arc flag truncates at that segment");
test(() => {
const path = createPath();
// Sweep flag (values[4]) must be 0 or 1, like the large-arc flag.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "A", values: [25, 25, 0, 1, 2, 50, 50] }
]);
const segs = path.getPathData();
assert_equals(segs.length, 1, "non-binary sweep flag is invalid");
assert_equals(segs[0].type, "M");Per the above, I disagree with this (and the spec says nothing?). It'd probably be good to use `false` and `true` in tests for these unless testing specific values.
// Bearing (B/b) is defined in SVG Paths but not implemented by any browser.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "B", values: [45] },
{ type: "L", values: [10, 10] }
]);
const segs = path.getPathData();
assert_equals(segs.length, 1, "B is treated as unknown");
}, "setPathData() treats Bearing (B) as an unknown command");
test(() => {
const path = createPath();
// Catmull-Rom (R/r) is also defined but not implemented.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "r", values: [10, 10, 20, 20, 30, 30] },
{ type: "L", values: [40, 40] }
]);
const segs = path.getPathData();I think it would be better to avoid testing these "specified but not implemented" segments. It's not really the business of this API to care.
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");Isn't this the same as `createPath()`?
test(() => {
const path = createPath(input);
const original = path.getPathData();
const path2 = createPath();
path2.setPathData(original);
assert_path_data_equals(path2.getPathData(), original);Maybe this test could just be merged into the `...-segments-...` test? That would give coverage of more segment types as well.
const path = createPath();
// Minimal valid non-empty path.
path.setPathData([
{ type: "M", values: [0, 0] }
]);
assert_equals(path.getAttribute("d"), "M 0 0");
}, "setPathData() with a single moveto");This could just be another entry in the array above?
test(() => {
const path = createPath();
path.setAttribute("d", "M 5 5 L 10 10");
path.setPathData([]);
assert_equals(path.getAttribute("d"), "",
"Empty sequence sets d to the empty string");
}, "setPathData() with an empty sequence");This test looks a bit out of place here, maybe move it somewhere else?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Should this file just contain a class that implements the "normal" path source interface:
```
class PathSourceInterface {
public:
bool HasMoreData() const;
PathSegmentData ParseSegment();
};
```
and then use the regular "path parsing driver" (`ParsePath()`) instead?It also feels like the name of this file should be `svg_path_segments_source.{cc,h}` to match up with the builder.
Done
I'd suggest splitting this into a "validate" and a "build". `ParseSegmentType` could be split in a similar way. With a table the "validate" function could be very simple.
Done
for (float value : values) {
if (!std::isfinite(value)) {
return false;
}
}```
if (!std::ranges::all_of(values, std::isfinite)) {
return false;
}
```
Done, switched to `std::ranges::all_of`. Wrapped `std::isfinite` in a small lambda since it's an overload set and can't be passed directly as a function object.
I'd perhaps expect this to be more lax, so more towards `ToBoolean()` in ECMAScript, so zero is false and everything else is true. That would eliminate the need for this validation.
Done. Flags now follow `ToBoolean` (0 = false, any other value = true), so the explicit validation is gone.
The `NOTREACHED()` should be here.
Done. `BuildByteStreamFromSegments` now skips out at the first unknown segment type, so `BuildPathSegmentData` only ever sees known types and the unknown case falls through to `NOTREACHED()`.
I think we can drop this blank line. Having `getPathData()` right next to/grouped with `setPathData` feels reasonable?
Done
// TODO(crbug.com/40441025): Write the byte stream directly into SVGPath
// once lazy attribute sync fires Will/DidModifyAttribute.I don't think this should be blocked on this - it's a pre-existing issue.
Agreed, it's pre-existing. I'll leave the TODO and not block this CL on it.
I guess this needs to be defined. I.e if setting an empty list should set the attribute to an empty string or remove it. For list-valued attributes I believe we have the latter behavior, and this is sort of that.
Done. An empty result now removes the `d` attribute (rather than setting it to ""), matching the behavior of other list-valued attributes. Updated the tests accordingly.
// https://svgwg.org/specs/paths/#InterfaceSVGPathElementVirali PurbeyRestore.
Done
// TODO(crbug.com/40441025): getPathSegmentAtLength() is not yet
// implemented in Chromium; this assertion currently fails and is
// baselined in SVGPathSegment-expected.txt.This shouldn't go in the test. Remove.
Done
assert_equals(path.getAttribute("d"), "M 0 0 L 10 10",How the path data should be serialized when `setPathData()` is called needs to be specified.
Agreed. Added a `TODO(crbug.com/40441025)` flagging that the `d` serialization from `setPathData()` is unspecified; the assertion documents Chromium's current output for now. I'll tighten it once I get it clarified from the WG.
function waitForAnimationFrame() {
return new Promise(resolve => requestAnimationFrame(() => {
requestAnimationFrame(resolve);
}));
}There's `waitForAtLeastOneFrame()` in `/common/rendering-utils.js`.
Done
Maybe also test a single invalid character
Done
assert_equals(segs.length, 2);
assert_equals(segs[0].type, "M");
assert_equals(segs[1].type, "L");Could these tests use the helper that was added in the previous CL? (Here and below.)
Done
const path = createPath();
// Arc flags must be exactly 0 or 1.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "A", values: [25, 25, 0, 2, 1, 50, 50] }
]);
const segs = path.getPathData();
assert_equals(segs.length, 1, "non-binary arc flag is invalid");
assert_equals(segs[0].type, "M");
}, "setPathData() with a non-binary arc flag truncates at that segment");
test(() => {
const path = createPath();
// Sweep flag (values[4]) must be 0 or 1, like the large-arc flag.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "A", values: [25, 25, 0, 1, 2, 50, 50] }
]);
const segs = path.getPathData();
assert_equals(segs.length, 1, "non-binary sweep flag is invalid");
assert_equals(segs[0].type, "M");Per the above, I disagree with this (and the spec says nothing?). It'd probably be good to use `false` and `true` in tests for these unless testing specific values.
Done. Dropped the non-binary-flag tests and switched the arc tests to `false/true`; added a positive case showing a non-0/1 value coerces to 1.
// Bearing (B/b) is defined in SVG Paths but not implemented by any browser.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "B", values: [45] },
{ type: "L", values: [10, 10] }
]);
const segs = path.getPathData();
assert_equals(segs.length, 1, "B is treated as unknown");
}, "setPathData() treats Bearing (B) as an unknown command");
test(() => {
const path = createPath();
// Catmull-Rom (R/r) is also defined but not implemented.
path.setPathData([
{ type: "M", values: [0, 0] },
{ type: "r", values: [10, 10, 20, 20, 30, 30] },
{ type: "L", values: [40, 40] }
]);
const segs = path.getPathData();I think it would be better to avoid testing these "specified but not implemented" segments. It's not really the business of this API to care.
Done, removed the B/r cases - agreed it's not this API's concern.
const path = document.createElementNS("http://www.w3.org/2000/svg", "path");Isn't this the same as `createPath()`?
Done
test(() => {
const path = createPath(input);
const original = path.getPathData();
const path2 = createPath();
path2.setPathData(original);
assert_path_data_equals(path2.getPathData(), original);Maybe this test could just be merged into the `...-segments-...` test? That would give coverage of more segment types as well.
Done
const path = createPath();
// Minimal valid non-empty path.
path.setPathData([
{ type: "M", values: [0, 0] }
]);
assert_equals(path.getAttribute("d"), "M 0 0");
}, "setPathData() with a single moveto");This could just be another entry in the array above?
Done
test(() => {
const path = createPath();
path.setAttribute("d", "M 5 5 L 10 10");
path.setPathData([]);
assert_equals(path.getAttribute("d"), "",
"Empty sequence sets d to the empty string");
}, "setPathData() with an empty sequence");This test looks a bit out of place here, maybe move it somewhere else?
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
// TODO(crbug.com/40441025): Write the byte stream directly into SVGPath
// once lazy attribute sync fires Will/DidModifyAttribute.Virali PurbeyI don't think this should be blocked on this - it's a pre-existing issue.
Agreed, it's pre-existing. I'll leave the TODO and not block this CL on it.
I didn't mean that it would block this CL, but block the use of the more efficient method of changing the `SVGPath` directly. (Even just doing a lazy-sync explicitly should be faster than the current approach because it avoids passing through a string.)
? g_empty_atomVirali PurbeyI guess this needs to be defined. I.e if setting an empty list should set the attribute to an empty string or remove it. For list-valued attributes I believe we have the latter behavior, and this is sort of that.
Done. An empty result now removes the `d` attribute (rather than setting it to ""), matching the behavior of other list-valued attributes. Updated the tests accordingly.
Just changing `g_empty_atom` to `g_null_atom` would've been enough.
assert_not_equals(track.getPathSegmentAtLength, undefined);Why are you removing this line?
It seems like this test could be merged into a relevant one of those you added. This is now just a duplicate.
// TODO(crbug.com/40441025): The exact d serialization produced by
// setPathData() is not yet specified; this pins Chromium's current output.Don't add "our" TODOs in WPTs. You can mark the test with `.tentative.` for the time being. The existing test seems to already rely on a certain serialization though, so it seems reasonable to follow suit.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
assert_not_equals(track.getPathSegmentAtLength, undefined);Why are you removing this line?
It seems like this test could be merged into a relevant one of those you added. This is now just a duplicate.
Removed the test from here.
// TODO(crbug.com/40441025): The exact d serialization produced by
// setPathData() is not yet specified; this pins Chromium's current output.Don't add "our" TODOs in WPTs. You can mark the test with `.tentative.` for the time being. The existing test seems to already rely on a certain serialization though, so it seems reasonable to follow suit.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Exportable changes to web-platform-tests were detected in this CL and a pull request in the upstream repo has been made: https://github.com/web-platform-tests/wpt/pull/60835.
When this CL lands, the bot will automatically merge the PR on GitHub if the required GitHub checks pass; otherwise, ecosystem-infra@ team will triage the failures and may contact you.
WPT Export docs:
https://chromium.googlesource.com/chromium/src/+/main/docs/testing/web_platform_tests.md#Automatic-export-process
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Commit-Queue | +2 |
[SVG] Implement `setPathData()` on `SVGPathElement`
Implements `setPathData(sequence<SVGPathSegment> pathData)` from the
SVG Paths spec [1] on `SVGPathElement`.
Valid segments are built into an SVGPathByteStream (via
SVGPathByteStreamBuilder), then applied through `setAttribute("d", …)`
so the existing mutation pipeline (invalidation, <use> instances,
MutationObservers) fires. A leading non-moveto yields an empty path;
on the first invalid segment (unknown type, wrong arity, non-finite
value, non-binary arc flag) that segment and all after it are dropped.
Gated behind the existing experimental runtime flag `SVGPathDataAPI`.
Instrumented via the `SVGPathElementSetPathData` use counter.
[1] https://svgwg.org/specs/paths/#__svg__SVGPathData__setPathData
Design: https://docs.google.com/document/d/1uCVUqYGKgsCCu7LEtEdO3Yuea-MT-I2XsU6Eoe3mlQI/edit?usp=sharing
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
The WPT PR for this CL has been merged upstream! https://github.com/web-platform-tests/wpt/pull/60835
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |