add implementation for Path2D addPath method (#37838)

Add implementation for Path2D addPath method
Spec:
https://html.spec.whatwg.org/multipage/canvas.html#dom-path2d-addpath

Testing: WPT test -
`tests/wpt/tests/css/geometry/DOMMatrix2DInit-validate-fixup.html`
Fixes: #37695

---------

Signed-off-by: Lloyd Massiah <artmis9@protonmail.com>
Signed-off-by: arthmis <artmis9@protonmail.com>
This commit is contained in:
arthmis 2025-08-09 01:06:20 -04:00 committed by GitHub
parent 6471587fb4
commit 86d7f4c793
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 57 additions and 83 deletions

1
Cargo.lock generated
View file

@ -7154,6 +7154,7 @@ dependencies = [
"itertools 0.14.0",
"jstraceable_derive",
"keyboard-types",
"kurbo",
"layout_api",
"libc",
"log",

View file

@ -81,6 +81,7 @@ itertools = { workspace = true }
js = { workspace = true }
jstraceable_derive = { path = "../jstraceable_derive" }
keyboard-types = { workspace = true }
kurbo = { workspace = true }
layout_api = { workspace = true }
libc = { workspace = true }
log = { workspace = true }

View file

@ -7,12 +7,15 @@ use std::cell::RefCell;
use canvas_traits::canvas::Path;
use dom_struct::dom_struct;
use js::rust::HandleObject;
use script_bindings::codegen::GenericBindings::DOMMatrixBinding::DOMMatrix2DInit;
use script_bindings::error::ErrorResult;
use script_bindings::str::DOMString;
use crate::dom::bindings::codegen::Bindings::CanvasRenderingContext2DBinding::Path2DMethods;
use crate::dom::bindings::error::{Error, Fallible};
use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
use crate::dom::bindings::root::DomRoot;
use crate::dom::dommatrixreadonly::dommatrix2dinit_to_matrix;
use crate::dom::globalscope::GlobalScope;
use crate::script_runtime::CanGc;
@ -48,14 +51,60 @@ impl Path2D {
pub(crate) fn segments(&self) -> Path {
self.path.borrow().clone()
}
pub(crate) fn is_path_empty(&self) -> bool {
self.path.borrow().0.is_empty()
}
fn add_path(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
// Step 1. If the Path2D object path has no subpaths, then return.
if other.is_path_empty() {
return Ok(());
}
// Step 2 Let matrix be the result of creating a DOMMatrix from the 2D dictionary transform.
let matrix = dommatrix2dinit_to_matrix(transform)?;
// Step 3. If one or more of matrix's m11 element, m12 element, m21
// element, m22 element, m41 element, or m42 element are infinite or
// NaN, then return.
if !matrix.m11.is_finite() ||
!matrix.m12.is_finite() ||
!matrix.m21.is_finite() ||
!matrix.m22.is_finite() ||
!matrix.m31.is_finite() ||
!matrix.m32.is_finite()
{
return Ok(());
}
// Step 4. Create a copy of all the subpaths in path. Let c be this copy.
let mut c = other.segments();
// Step 5. Transform all the coordinates and lines in c by the transform matrix `matrix`.
c.transform(matrix);
let mut path = self.path.borrow_mut();
// Step 6. Let (x, y) be the last point in the last subpath of c
let last_point = path.last_point();
// Step 7. Add all the subpaths in c to a.
path.0.extend(c.0);
// Step 8. Create a new subpath in `a` with (x, y) as the only point in the subpath.
if let Some(last_point) = last_point {
path.move_to(last_point.x, last_point.y);
}
Ok(())
}
}
impl Path2DMethods<crate::DomTypeHolder> for Path2D {
/// <https://html.spec.whatwg.org/multipage/#dom-path2d-addpath>
fn AddPath(&self, other: &Path2D) {
let other = other.segments();
// Step 7. Add all the subpaths in c to a.
self.path.borrow_mut().0.extend(other.0);
fn AddPath(&self, other: &Path2D, transform: &DOMMatrix2DInit) -> ErrorResult {
self.add_path(other, transform)
}
/// <https://html.spec.whatwg.org/multipage/#dom-context-2d-closepath>

View file

@ -287,6 +287,7 @@ interface Path2D {
constructor();
constructor(Path2D other);
constructor(DOMString pathString);
undefined addPath(Path2D path/*, SVGMatrix? transformation*/);
[Throws]
undefined addPath(Path2D path, optional DOMMatrix2DInit transform = {});
};
Path2D includes CanvasPath;

View file

@ -1,78 +0,0 @@
[DOMMatrix2DInit-validate-fixup.html]
[addPath({a: 1, m11: 2}) (invalid)]
expected: FAIL
[addPath({b: 0, m12: -1}) (invalid)]
expected: FAIL
[addPath({c: Infinity, m21: -Infinity}) (invalid)]
expected: FAIL
[addPath({d: 0, m22: NaN}) (invalid)]
expected: FAIL
[addPath({e: 1, m41: 1.00000001}) (invalid)]
expected: FAIL
[addPath({f: 0, m42: 5e-324}) (invalid)]
expected: FAIL
[addPath({d: Infinity, m22: Infinity})]
expected: FAIL
[addPath({e: -Infinity, m41: -Infinity})]
expected: FAIL
[addPath({f: NaN, m42: NaN})]
expected: FAIL
[addPath({f: NaN, m42: NaN, is2D: true})]
expected: FAIL
[addPath({a: 2})]
expected: FAIL
[addPath({b: 2})]
expected: FAIL
[addPath({c: 2})]
expected: FAIL
[addPath({d: 2})]
expected: FAIL
[addPath({e: 2})]
expected: FAIL
[addPath({f: 2})]
expected: FAIL
[addPath({a: -0, b: -0, c: -0, d: -0, e: -0, f: -0})]
expected: FAIL
[addPath({a: -0, b: -0, c: -0, d: -0, e: -0, f: -0, is2D: true})]
expected: FAIL
[addPath({m11: 2})]
expected: FAIL
[addPath({m12: 2})]
expected: FAIL
[addPath({m21: 2})]
expected: FAIL
[addPath({m22: 2})]
expected: FAIL
[addPath({m41: 2})]
expected: FAIL
[addPath({m42: 2})]
expected: FAIL
[addPath({m11: -0, m12: -0, m21: -0, m22: -0, m41: -0, m42: -0})]
expected: FAIL
[addPath({m11: -0, m12: -0, m21: -0, m22: -0, m41: -0, m42: -0, is2D: true})]
expected: FAIL