From a72a2a68e2fec39ac09bea7aaf9acefffb8886a3 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Sun, 30 Aug 2026 18:44:26 +0800 Subject: [PATCH] fix(svg): expose current element constructors --- .../src/native/element/interface_names.rs | 4 ++ moli-dom/src/native/element/tests.rs | 4 ++ .../bridge_descriptor/descriptors.rs | 4 ++ .../tests/dom_elements/live_document.rs | 50 +++++++++++++++++++ moli-renderer-v8/src/web_api_interfaces.rs | 4 ++ 5 files changed, 66 insertions(+) diff --git a/moli-dom/src/native/element/interface_names.rs b/moli-dom/src/native/element/interface_names.rs index e33c7841eb..b3e9cce400 100644 --- a/moli-dom/src/native/element/interface_names.rs +++ b/moli-dom/src/native/element/interface_names.rs @@ -92,14 +92,17 @@ pub fn svg_element_interface_name(local_name: &str) -> &'static str { match local_name { "a" => "SVGAElement", "circle" => "SVGCircleElement", + "clipPath" => "SVGClipPathElement", "defs" => "SVGDefsElement", "desc" => "SVGDescElement", "ellipse" => "SVGEllipseElement", + "filter" => "SVGFilterElement", "foreignObject" => "SVGForeignObjectElement", "g" => "SVGGElement", "image" => "SVGImageElement", "line" => "SVGLineElement", "linearGradient" => "SVGLinearGradientElement", + "mask" => "SVGMaskElement", "metadata" => "SVGMetadataElement", "path" => "SVGPathElement", "pattern" => "SVGPatternElement", @@ -115,6 +118,7 @@ pub fn svg_element_interface_name(local_name: &str) -> &'static str { "title" => "SVGTitleElement", "tspan" => "SVGTSpanElement", "use" => "SVGUseElement", + "view" => "SVGViewElement", _ => "SVGElement", } } diff --git a/moli-dom/src/native/element/tests.rs b/moli-dom/src/native/element/tests.rs index ac9b3fc5a5..351f6ae04e 100644 --- a/moli-dom/src/native/element/tests.rs +++ b/moli-dom/src/native/element/tests.rs @@ -231,9 +231,11 @@ fn html_element_interface_name_covers_replay_tags() { fn svg_element_interface_name_specializes_standard_elements() { assert_eq!(svg_element_interface_name("a"), "SVGAElement"); assert_eq!(svg_element_interface_name("circle"), "SVGCircleElement"); + assert_eq!(svg_element_interface_name("clipPath"), "SVGClipPathElement"); assert_eq!(svg_element_interface_name("defs"), "SVGDefsElement"); assert_eq!(svg_element_interface_name("desc"), "SVGDescElement"); assert_eq!(svg_element_interface_name("ellipse"), "SVGEllipseElement"); + assert_eq!(svg_element_interface_name("filter"), "SVGFilterElement"); assert_eq!( svg_element_interface_name("foreignObject"), "SVGForeignObjectElement" @@ -246,6 +248,7 @@ fn svg_element_interface_name_specializes_standard_elements() { "SVGLinearGradientElement" ); assert_eq!(svg_element_interface_name("metadata"), "SVGMetadataElement"); + assert_eq!(svg_element_interface_name("mask"), "SVGMaskElement"); assert_eq!(svg_element_interface_name("path"), "SVGPathElement"); assert_eq!(svg_element_interface_name("pattern"), "SVGPatternElement"); assert_eq!(svg_element_interface_name("polygon"), "SVGPolygonElement"); @@ -262,6 +265,7 @@ fn svg_element_interface_name_specializes_standard_elements() { assert_eq!(svg_element_interface_name("title"), "SVGTitleElement"); assert_eq!(svg_element_interface_name("tspan"), "SVGTSpanElement"); assert_eq!(svg_element_interface_name("use"), "SVGUseElement"); + assert_eq!(svg_element_interface_name("view"), "SVGViewElement"); assert_eq!(svg_element_interface_name("style"), "SVGStyleElement"); assert_eq!(svg_element_interface_name("custom"), "SVGElement"); } diff --git a/moli-renderer-v8/src/context_bootstrap/bridge_descriptor/descriptors.rs b/moli-renderer-v8/src/context_bootstrap/bridge_descriptor/descriptors.rs index ce526a3ad0..94610d209b 100644 --- a/moli-renderer-v8/src/context_bootstrap/bridge_descriptor/descriptors.rs +++ b/moli-renderer-v8/src/context_bootstrap/bridge_descriptor/descriptors.rs @@ -109,12 +109,14 @@ const NODE_BRIDGE_DESCRIPTORS: &[BridgeDescriptor] = &[ ELEMENT_GROUPS, ), descriptor(web_api_interfaces::SVGAElement::DESCRIPTOR, ELEMENT_GROUPS), + descriptor(web_api_interfaces::SVGClipPathElement::DESCRIPTOR, ELEMENT_GROUPS), specialized_descriptor( web_api_interfaces::SVGCircleElement::DESCRIPTOR, ELEMENT_GROUPS, SpecializedTemplateInstaller::None, SVG_GEOMETRY_RUNTIME_INSTALL_GROUPS, ), + descriptor(web_api_interfaces::SVGFilterElement::DESCRIPTOR, ELEMENT_GROUPS), descriptor( web_api_interfaces::SVGDefsElement::DESCRIPTOR, ELEMENT_GROUPS, @@ -172,6 +174,7 @@ const NODE_BRIDGE_DESCRIPTORS: &[BridgeDescriptor] = &[ web_api_interfaces::SVGStyleElement::DESCRIPTOR, ELEMENT_GROUPS, ), + descriptor(web_api_interfaces::SVGMaskElement::DESCRIPTOR, ELEMENT_GROUPS), specialized_descriptor( web_api_interfaces::SVGPathElement::DESCRIPTOR, ELEMENT_GROUPS, @@ -262,6 +265,7 @@ const NODE_BRIDGE_DESCRIPTORS: &[BridgeDescriptor] = &[ web_api_interfaces::SVGUseElement::DESCRIPTOR, ELEMENT_GROUPS, ), + descriptor(web_api_interfaces::SVGViewElement::DESCRIPTOR, ELEMENT_GROUPS), specialized_descriptor( web_api_interfaces::SVGRectElement::DESCRIPTOR, ELEMENT_GROUPS, diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document.rs index e0458e8688..5da9a39e1c 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document.rs @@ -1518,6 +1518,56 @@ fn svg_owner_svg_element_tracks_the_nearest_svg_fragment_root() { assert_eq!(result, "ok"); } +#[test] +fn svg_historical_interfaces_keep_current_element_constructors_only() { + let mut vm = new_parsed_test_vm( + "https://svg-historical-interfaces.test/", + "", + ); + + let result = vm + .eval( + r#" + (() => { + const assert = (condition, message) => { + if (!condition) throw new Error(message); + }; + const ns = "http://www.w3.org/2000/svg"; + const interfaces = [ + ["clipPath", "SVGClipPathElement"], + ["filter", "SVGFilterElement"], + ["mask", "SVGMaskElement"], + ["view", "SVGViewElement"], + ]; + const removedUnitTypeConstants = [ + "SVG_UNIT_TYPE_UNKNOWN", + "SVG_UNIT_TYPE_USERSPACEONUSE", + "SVG_UNIT_TYPE_OBJECTBOUNDINGBOX", + ]; + + for (const [localName, interfaceName] of interfaces) { + const constructor = globalThis[interfaceName]; + assert(typeof constructor === "function", `${interfaceName} constructor`); + assert(Object.getPrototypeOf(constructor.prototype) === SVGElement.prototype, + `${interfaceName} prototype parent`); + assert(document.createElementNS(ns, localName) instanceof constructor, + `${localName} interface`); + for (const constant of removedUnitTypeConstants) { + assert(!(constant in constructor), `${interfaceName}.${constant} removed`); + } + } + + assert(!("viewTarget" in SVGViewElement.prototype), + "SVGViewElement.prototype.viewTarget removed"); + return "ok"; + })() + "#, + ) + .expect("SVG historical interface probe should evaluate"); + + assert_eq!(result, "ok"); +} + #[test] fn svg_geometry_queries_use_computed_paths_live_tree_and_kurbo_bounds() { let mut vm = new_parsed_test_vm( diff --git a/moli-renderer-v8/src/web_api_interfaces.rs b/moli-renderer-v8/src/web_api_interfaces.rs index 127e8ea3fe..51485e6060 100644 --- a/moli-renderer-v8/src/web_api_interfaces.rs +++ b/moli-renderer-v8/src/web_api_interfaces.rs @@ -376,6 +376,10 @@ interfaces! { SVGCircleElement: SVGGeometryElement; SVGDefsElement: SVGGraphicsElement; SVGDescElement: SVGElement; + SVGClipPathElement: SVGElement; + SVGFilterElement: SVGElement; + SVGMaskElement: SVGElement; + SVGViewElement: SVGElement; SVGElement: Element; SVGEllipseElement: SVGGeometryElement; SVGForeignObjectElement: SVGGraphicsElement;