diff --git a/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs b/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs index e3e5549401..17891d233e 100644 --- a/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs +++ b/moli-renderer-v8/src/context_bootstrap/svg_runtime/bindings.rs @@ -968,6 +968,12 @@ struct SvgElementPrototypeAccessorsDeclaration { getter = svg_element_class_name_getter )] class_name: (), + + #[webapi( + accessor_property = "ownerSVGElement", + getter = svg_element_owner_svg_element_getter + )] + owner_svg_element: (), } #[derive(WebApiFunctionTemplate)] diff --git a/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs b/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs index cb3183503a..b9b50904f0 100644 --- a/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs +++ b/moli-renderer-v8/src/context_bootstrap/svg_runtime/callbacks.rs @@ -136,6 +136,63 @@ pub(super) fn svg_element_class_name_getter<'s>( svg_animated_string_attribute_getter(scope, args, rv, SVG_ELEMENT_CLASS_NAME_SLOT, "class"); } +pub(super) fn svg_element_owner_svg_element_getter<'s>( + scope: &mut v8::PinScope<'s, '_>, + args: v8::FunctionCallbackArguments<'s>, + mut rv: v8::ReturnValue<'s, v8::Value>, +) { + let Ok((runtime_ptr, handle)) = + crate::native_bridge::node_runtime_and_handle_from_object_or_detached(scope, args.this()) + else { + webidl::throw_type_error( + scope, + "SVGElement.ownerSVGElement called on incompatible receiver.", + ); + return; + }; + let owner_handle = { + let runtime = unsafe { &*runtime_ptr }; + let Some(node) = runtime.dom_host().node(handle) else { + rv.set_null(); + return; + }; + if node.namespace() != Some(crate::native_bridge::document::SVG_NS) { + webidl::throw_type_error( + scope, + "SVGElement.ownerSVGElement called on incompatible receiver.", + ); + return; + } + + let mut current = node.parent_node_id(); + let mut owner = None; + while let Some(candidate) = current { + let Some(ancestor) = runtime.dom_host().node(candidate) else { + break; + }; + if ancestor.namespace() != Some(crate::native_bridge::document::SVG_NS) + || ancestor.local_name() == Some("foreignObject") + { + break; + } + if ancestor.local_name() == Some("svg") { + owner = Some(candidate); + break; + } + current = ancestor.parent_node_id(); + } + owner + }; + + let Some(owner) = owner_handle.and_then(|owner| { + crate::native_bridge::document::detached_native_object_for_handle(scope, runtime_ptr, owner) + }) else { + rv.set_null(); + return; + }; + rv.set(owner.into()); +} + pub(super) fn svg_uri_href_getter<'s>( scope: &mut v8::PinScope<'s, '_>, args: v8::FunctionCallbackArguments<'s>, diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/geometry_and_svg.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/geometry_and_svg.rs index 40ccb20cb3..153764eafa 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/geometry_and_svg.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/geometry_and_svg.rs @@ -284,6 +284,77 @@ fn svg_historical_interfaces_keep_current_element_constructors_only() { assert_eq!(result, "ok"); } +#[test] +fn svg_owner_svg_element_tracks_the_nearest_svg_fragment_root() { + let mut vm = new_parsed_test_vm( + "https://svg-owner-element.test/", + r#"