From a7beb2e816dfd1c46cbd887e08db4bbb12d51a57 Mon Sep 17 00:00:00 2001 From: ldm0 Date: Tue, 29 Sep 2026 09:42:05 +0800 Subject: [PATCH] fix(html): reflect link and meta metadata attributes Expose link.rev, link.type and meta.scheme through the existing native DOMString reflection infrastructure. Regression coverage includes live and windowless documents, owner-prototype descriptors, valid non-ASCII and astral Unicode, attribute mutations and receiver validation before value conversion. Validation: all branch checks passed. Summary [ 191.579s] 18922 tests run: 18922 passed (3 slow), 16 skipped --- .../src/native_bridge/element/reflection.rs | 30 ++++++++++++++ .../element/resource_elements.rs | 21 ++++++++++ .../live_document/element_interfaces.rs | 41 +++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/moli-renderer-v8/src/native_bridge/element/reflection.rs b/moli-renderer-v8/src/native_bridge/element/reflection.rs index 82b95ecda0..08ce014c2e 100644 --- a/moli-renderer-v8/src/native_bridge/element/reflection.rs +++ b/moli-renderer-v8/src/native_bridge/element/reflection.rs @@ -212,11 +212,14 @@ pub(super) enum DomStringReflection { LinkIntegrity, LinkMedia, LinkReferrerPolicy, + LinkRev, + LinkType, LiType, MarqueeBgColor, MarqueeHeight, MarqueeWidth, MetaMedia, + MetaScheme, ModDateTime, ObjectArchive, ObjectCode, @@ -560,6 +563,24 @@ const DOM_STRING_REFLECTION_DESCRIPTORS: &[(DomStringReflection, DomStringReflec "referrerPolicy", ), ), + ( + DomStringReflection::LinkRev, + DomStringReflectionDescriptor::new_html_element( + "HTMLLinkElement", + "link", + "rev", + "rev", + ), + ), + ( + DomStringReflection::LinkType, + DomStringReflectionDescriptor::new_html_element( + "HTMLLinkElement", + "link", + "type", + "type", + ), + ), ( DomStringReflection::LiType, DomStringReflectionDescriptor::new("HTMLLIElement", "type", "type"), @@ -580,6 +601,15 @@ const DOM_STRING_REFLECTION_DESCRIPTORS: &[(DomStringReflection, DomStringReflec DomStringReflection::MetaMedia, DomStringReflectionDescriptor::new("HTMLMetaElement", "media", "media"), ), + ( + DomStringReflection::MetaScheme, + DomStringReflectionDescriptor::new_html_element( + "HTMLMetaElement", + "meta", + "scheme", + "scheme", + ), + ), ( DomStringReflection::ModDateTime, DomStringReflectionDescriptor::new("HTMLModElement", "datetime", "dateTime"), diff --git a/moli-renderer-v8/src/native_bridge/element/resource_elements.rs b/moli-renderer-v8/src/native_bridge/element/resource_elements.rs index 5579dfb7cd..9ab772b1d7 100644 --- a/moli-renderer-v8/src/native_bridge/element/resource_elements.rs +++ b/moli-renderer-v8/src/native_bridge/element/resource_elements.rs @@ -2190,6 +2190,20 @@ pub(super) struct HtmlLinkElementUrlPrototypeDeclaration { data = DomStringReflection::LinkIntegrity )] integrity: (), + #[webapi( + accessor_property, + getter = dom_string_reflection_getter_function, + setter = dom_string_reflection_setter_function, + data = DomStringReflection::LinkRev + )] + rev: (), + #[webapi( + accessor_property, + getter = dom_string_reflection_getter_function, + setter = dom_string_reflection_setter_function, + data = DomStringReflection::LinkType + )] + r#type: (), } #[derive(WebApiFunctionTemplate)] @@ -2423,6 +2437,13 @@ pub(super) struct HtmlMetaElementPrototypeDeclaration { setter = meta_http_equiv_setter_function )] http_equiv: (), + #[webapi( + accessor_property, + getter = dom_string_reflection_getter_function, + setter = dom_string_reflection_setter_function, + data = DomStringReflection::MetaScheme + )] + scheme: (), } #[derive(WebApiFunctionTemplate)] diff --git a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/element_interfaces.rs b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/element_interfaces.rs index fbc3bae16e..cd822269fd 100644 --- a/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/element_interfaces.rs +++ b/moli-renderer-v8/src/script_vm/tests/dom_elements/live_document/element_interfaces.rs @@ -727,3 +727,44 @@ fn legacy_boolean_accessors_live_on_owner_prototypes() { assert_eq!(result, "ok"); } + +#[test] +fn link_and_meta_legacy_metadata_reflect_on_native_owner_prototypes() { + let mut vm = new_parsed_test_vm( + "https://metadata-reflection.test/", + "", + ); + let result = vm.eval(r#" +(() => { + const assert = (condition, message) => { if (!condition) throw new Error(message); }; + for (const owner of [document, document.implementation.createHTMLDocument('')]) { + for (const [tag, property, prototype] of [ + ['link', 'rev', HTMLLinkElement.prototype], + ['link', 'type', HTMLLinkElement.prototype], + ['meta', 'scheme', HTMLMetaElement.prototype] + ]) { + const element = owner.createElement(tag); + owner.head.appendChild(element); + const descriptor = Object.getOwnPropertyDescriptor(prototype, property); + assert(descriptor && descriptor.enumerable && descriptor.configurable, `${tag}.${property} descriptor`); + assert(typeof descriptor.get === 'function' && typeof descriptor.set === 'function', 'accessors'); + assert(element[property] === '', 'missing value'); + element[property] = 'MiXeD\u00E9\u{1F642}'; + assert(element.getAttribute(property) === 'MiXeD\u00E9\u{1F642}', 'DOMString reflection'); + element.setAttribute(property, 'updated'); + assert(element[property] === 'updated', 'attribute mutation'); + element.removeAttribute(property); + assert(element[property] === '', 'attribute removal'); + let converted = false; + try { + descriptor.set.call({}, { toString() { converted = true; return 'forged'; } }); + throw new Error('accepted forged receiver'); + } catch (error) { assert(error instanceof TypeError, 'receiver brand'); } + assert(!converted, 'receiver validation precedes conversion'); + } + } + return 'ok'; +})() +"#).expect("link and meta metadata reflection probe should evaluate"); + assert_eq!(result, "ok"); +}