feat(svg): expose remaining filter element interfaces

This commit is contained in:
ldm0
2026-10-03 02:58:43 +08:00
committed by Donough Liu
parent 4ca389adb2
commit 90ffad4035
5 changed files with 85 additions and 0 deletions
@@ -95,6 +95,12 @@ pub fn svg_element_interface_name(local_name: &str) -> &'static str {
"defs" => "SVGDefsElement",
"desc" => "SVGDescElement",
"ellipse" => "SVGEllipseElement",
"feComponentTransfer" => "SVGFEComponentTransferElement",
"feFlood" => "SVGFEFloodElement",
"feImage" => "SVGFEImageElement",
"feMerge" => "SVGFEMergeElement",
"feMergeNode" => "SVGFEMergeNodeElement",
"feTile" => "SVGFETileElement",
"foreignObject" => "SVGForeignObjectElement",
"g" => "SVGGElement",
"image" => "SVGImageElement",
@@ -129,6 +129,30 @@ const NODE_BRIDGE_DESCRIPTORS: &[BridgeDescriptor] = &[
SpecializedTemplateInstaller::None,
SVG_GEOMETRY_RUNTIME_INSTALL_GROUPS,
),
descriptor(
web_api_interfaces::SVGFEComponentTransferElement::DESCRIPTOR,
ELEMENT_GROUPS,
),
descriptor(
web_api_interfaces::SVGFEFloodElement::DESCRIPTOR,
ELEMENT_GROUPS,
),
descriptor(
web_api_interfaces::SVGFEImageElement::DESCRIPTOR,
ELEMENT_GROUPS,
),
descriptor(
web_api_interfaces::SVGFEMergeElement::DESCRIPTOR,
ELEMENT_GROUPS,
),
descriptor(
web_api_interfaces::SVGFEMergeNodeElement::DESCRIPTOR,
ELEMENT_GROUPS,
),
descriptor(
web_api_interfaces::SVGFETileElement::DESCRIPTOR,
ELEMENT_GROUPS,
),
descriptor(
web_api_interfaces::SVGForeignObjectElement::DESCRIPTOR,
ELEMENT_GROUPS,
@@ -2161,6 +2161,7 @@ mod queue_microtask;
mod rendering_update;
mod script_terminal_completion;
mod streams;
mod svg_filter_interfaces;
mod url_components;
mod webgl_interfaces;
mod webidl_collections;
@@ -0,0 +1,48 @@
use super::*;
#[test]
fn svg_filter_interfaces_preserve_native_inheritance_and_realm_contracts() {
let mut vm = new_storage_page_task_executor_test_vm("https://svg-filter-interfaces.test/");
vm.eval("document.body.innerHTML = '<iframe id=child></iframe>'")
.unwrap();
assert_eq!(vm.eval(r#"(() => {
const assert = (ok, message) => { if (!ok) throw Error(message); };
const ns = 'http://www.w3.org/2000/svg';
const child = document.getElementById('child').contentWindow;
const detached = document.implementation.createHTMLDocument('');
const xml = new DOMParser().parseFromString('<svg xmlns="' + ns + '"/>', 'image/svg+xml');
for (const [name, parentName] of [["SVGFEComponentTransferElement","SVGElement"],["SVGFEFloodElement","SVGElement"],["SVGFEImageElement","SVGElement"],["SVGFEMergeElement","SVGElement"],["SVGFEMergeNodeElement","SVGElement"],["SVGFETileElement","SVGElement"]]) {
for (const w of [window, child]) {
const C = w[name], parent = w[parentName];
assert(typeof C === 'function' && C.name === name, name + ' exposed');
assert(Object.getPrototypeOf(C.prototype) === parent.prototype, name + ' prototype inheritance');
assert(Object.getPrototypeOf(C) === parent, name + ' interface inheritance');
let error;
try { new C(); } catch (caught) { error = caught; }
assert(error instanceof w.TypeError, name + ' illegal constructor realm');
}
}
const nodeType = Object.getOwnPropertyDescriptor(Node.prototype, 'nodeType').get;
for (const [tag, name] of [["feComponentTransfer","SVGFEComponentTransferElement"],["feFlood","SVGFEFloodElement"],["feImage","SVGFEImageElement"],["feMerge","SVGFEMergeElement"],["feMergeNode","SVGFEMergeNodeElement"],["feTile","SVGFETileElement"]]) {
for (const [doc, w] of [[document, window], [child.document, child], [detached, window], [xml, window]]) {
const element = doc.createElementNS(ns, tag), C = w[name];
assert(element instanceof C && element instanceof w.SVGElement && element instanceof w.Node, tag + ' native factory brand');
assert(Object.getPrototypeOf(element) === C.prototype && element.constructor === C, tag + ' factory prototype');
assert(Object.prototype.toString.call(element) === '[object ' + name + ']', tag + ' native tag');
assert(element.cloneNode(false) instanceof C, tag + ' cloned brand');
assert(doc.importNode(element, false) instanceof C, tag + ' imported brand');
element.setAttribute('data-test', 'value');
assert(element.getAttribute('data-test') === 'value' && nodeType.call(element) === 1, tag + ' inherited DOM behavior');
for (const receiver of [Object.create(element), new Proxy(element, {})]) {
let error;
try { nodeType.call(receiver); } catch (caught) { error = caught; }
assert(error instanceof TypeError, tag + ' rejects forged native identity');
}
assert(!(doc.createElement(tag) instanceof C), tag + ' namespace-sensitive factory');
}
const parsed = new DOMParser().parseFromString('<svg xmlns="' + ns + '"><' + tag + '/></svg>', 'image/svg+xml');
assert(parsed.documentElement.firstElementChild instanceof window[name], tag + ' parsed XML brand');
}
return true;
})()"#).unwrap(), "true");
}
@@ -392,6 +392,12 @@ interfaces! {
SVGDescElement: SVGElement;
SVGElement: Element;
SVGEllipseElement: SVGGeometryElement;
SVGFEComponentTransferElement: SVGElement;
SVGFEFloodElement: SVGElement;
SVGFEImageElement: SVGElement;
SVGFEMergeElement: SVGElement;
SVGFEMergeNodeElement: SVGElement;
SVGFETileElement: SVGElement;
SVGForeignObjectElement: SVGGraphicsElement;
SVGGElement: SVGGraphicsElement;
SVGGeometryElement: SVGGraphicsElement;