fix(html): complete obsolete element reflections

This commit is contained in:
ldm0
2026-09-28 00:51:51 +08:00
parent a47b30ae5e
commit ccf736eba0
4 changed files with 276 additions and 11 deletions
+51 -9
View File
@@ -430,14 +430,15 @@ pub(super) use global_attributes::{
html_label_getter_function, html_long_desc_getter_function, html_lowsrc_getter_function,
html_margin_height_getter_function, html_margin_width_getter_function,
html_media_getter_function, html_name_getter_function, html_name_setter_function,
html_no_href_getter_function, html_no_shade_getter_function, html_no_shade_setter_function,
html_ping_getter_function, html_rel_getter_function, html_rel_setter_function,
html_scrolling_getter_function, html_shape_getter_function, html_size_getter_function,
html_sizes_getter_function, html_type_getter_function, html_use_map_getter_function,
html_value_getter_function, html_value_type_getter_function, html_version_getter_function,
html_vspace_getter_function, html_width_getter_function, image_decoding_setter_function,
image_long_desc_setter_function, image_lowsrc_setter_function, link_target_getter_function,
link_target_setter_function, node_access_key_getter_function,
html_no_href_getter_function, html_no_resize_getter_function, html_no_resize_setter_function,
html_no_shade_getter_function, html_no_shade_setter_function, html_ping_getter_function,
html_rel_getter_function, html_rel_setter_function, html_scrolling_getter_function,
html_shape_getter_function, html_size_getter_function, html_sizes_getter_function,
html_true_speed_getter_function, html_true_speed_setter_function, html_type_getter_function,
html_use_map_getter_function, html_value_getter_function, html_value_type_getter_function,
html_version_getter_function, html_vspace_getter_function, html_width_getter_function,
image_decoding_setter_function, image_long_desc_setter_function, image_lowsrc_setter_function,
link_target_getter_function, link_target_setter_function, node_access_key_getter_function,
node_access_key_label_getter_function, node_access_key_setter_function,
node_allow_fullscreen_getter_function, node_allow_fullscreen_setter_function,
node_autocapitalize_getter_function, node_autocapitalize_setter_function,
@@ -1808,6 +1809,25 @@ struct HtmlBodyElementLegacyPrototypeDeclaration {
bg_color: (),
}
#[derive(WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::HTMLFrameSetElement, enumerable)]
struct HtmlFrameSetElementLegacyPrototypeDeclaration {
#[webapi(
accessor_property,
getter = dom_string_reflection_getter_function,
setter = dom_string_reflection_setter_function,
data = DomStringReflection::FrameSetCols
)]
cols: (),
#[webapi(
accessor_property,
getter = dom_string_reflection_getter_function,
setter = dom_string_reflection_setter_function,
data = DomStringReflection::FrameSetRows
)]
rows: (),
}
#[derive(WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::HTMLHRElement, enumerable)]
struct HtmlHrElementLegacyPrototypeDeclaration {
@@ -1843,6 +1863,13 @@ struct HtmlHrElementLegacyPrototypeDeclaration {
#[derive(WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::HTMLFontElement, enumerable)]
struct HtmlFontElementLegacyPrototypeDeclaration {
#[webapi(
accessor_property,
getter = dom_string_reflection_getter_function,
setter = dom_string_reflection_setter_function,
data = DomStringReflection::FontFace
)]
face: (),
#[webapi(
accessor_property,
getter = html_size_getter_function,
@@ -1862,6 +1889,12 @@ struct HtmlFontElementLegacyPrototypeDeclaration {
#[derive(WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::HTMLMarqueeElement, enumerable)]
struct HtmlMarqueeElementLegacyPrototypeDeclaration {
#[webapi(
accessor_property = "trueSpeed",
getter = html_true_speed_getter_function,
setter = html_true_speed_setter_function
)]
true_speed: (),
#[webapi(
accessor_property = "loop",
getter = marquee_loop_getter_function,
@@ -3924,6 +3957,12 @@ struct HtmlEmbedElementUrlPrototypeDeclaration {
#[derive(WebApiFunctionTemplate)]
#[webapi(interface = web_api_interfaces::HTMLFrameElement, enumerable)]
struct HtmlFrameElementLegacyPrototypeDeclaration {
#[webapi(
accessor_property = "noResize",
getter = html_no_resize_getter_function,
setter = html_no_resize_setter_function
)]
no_resize: (),
#[webapi(
accessor_property,
getter = generic_src_getter_function,
@@ -7120,7 +7159,10 @@ pub(crate) fn install_element_template_bindings<'s>(
HtmlBodyElementLegacyPrototypeDeclaration,
),
"HTMLFrameSetElement" => {
install!(HtmlBodyOrFrameSetEventHandlersPrototypeDeclaration)
install!(
HtmlBodyOrFrameSetEventHandlersPrototypeDeclaration,
HtmlFrameSetElementLegacyPrototypeDeclaration,
);
}
"HTMLHRElement" => install!(HtmlHrElementLegacyPrototypeDeclaration),
"HTMLFontElement" => install!(HtmlFontElementLegacyPrototypeDeclaration),
@@ -20,8 +20,8 @@ use super::reflection::{
use super::{
attribute_property_getter_from_object_or_detached,
boolean_attribute_property_getter_from_object_or_detached, element_attribute,
form_associated_form_owner, html_element_getter_receiver, html_element_setter_receiver,
property_dom_string_value, resolve_url_like_attribute,
element_has_attribute, form_associated_form_owner, html_element_getter_receiver,
html_element_setter_receiver, property_dom_string_value, resolve_url_like_attribute,
set_attribute_property_on_object_or_detached,
set_boolean_attribute_property_on_object_or_detached,
set_dom_string_attribute_property_on_object, set_reflected_attribute,
@@ -1219,6 +1219,117 @@ pub(in crate::native_bridge) fn html_no_shade_setter_function<'s>(
rv.set_undefined();
}
fn html_boolean_attribute_getter<'s>(
scope: &mut v8::PinScope<'s, '_>,
receiver: v8::Local<'s, v8::Object>,
interface: &'static str,
member: &'static str,
local_name: &'static str,
attribute: &'static str,
mut rv: v8::ReturnValue<'s, v8::Value>,
) {
let Some((runtime_ptr, handle)) =
html_element_getter_receiver(scope, receiver, interface, member, local_name)
else {
rv.set_bool(false);
return;
};
rv.set_bool(element_has_attribute(
unsafe { &*runtime_ptr },
handle,
attribute,
));
}
fn html_boolean_attribute_setter<'s>(
scope: &mut v8::PinScope<'s, '_>,
receiver: v8::Local<'s, v8::Object>,
value: v8::Local<'s, v8::Value>,
interface: &'static str,
member: &'static str,
local_name: &'static str,
attribute: &'static str,
) {
let Some((runtime_ptr, handle)) =
html_element_setter_receiver(scope, receiver, interface, member, local_name)
else {
return;
};
set_reflected_boolean_attribute(
scope,
runtime_ptr,
handle,
attribute,
value.boolean_value(scope),
);
}
pub(in crate::native_bridge) fn html_no_resize_getter_function<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
html_boolean_attribute_getter(
scope,
args.this(),
"HTMLFrameElement",
"noResize",
"frame",
"noresize",
rv,
);
}
pub(in crate::native_bridge) fn html_no_resize_setter_function<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'_, v8::Value>,
) {
html_boolean_attribute_setter(
scope,
args.this(),
args.get(0),
"HTMLFrameElement",
"noResize",
"frame",
"noresize",
);
rv.set_undefined();
}
pub(in crate::native_bridge) fn html_true_speed_getter_function<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
rv: v8::ReturnValue<'s, v8::Value>,
) {
html_boolean_attribute_getter(
scope,
args.this(),
"HTMLMarqueeElement",
"trueSpeed",
"marquee",
"truespeed",
rv,
);
}
pub(in crate::native_bridge) fn html_true_speed_setter_function<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
mut rv: v8::ReturnValue<'_, v8::Value>,
) {
html_boolean_attribute_setter(
scope,
args.this(),
args.get(0),
"HTMLMarqueeElement",
"trueSpeed",
"marquee",
"truespeed",
);
rv.set_undefined();
}
pub(in crate::native_bridge) fn node_allow_fullscreen_getter_function<'s>(
scope: &mut v8::PinScope<'s, '_>,
args: v8::FunctionCallbackArguments<'s>,
@@ -180,9 +180,12 @@ pub(super) enum DomStringReflection {
EmbedHeight,
EmbedType,
EmbedWidth,
FontFace,
FontSize,
FrameFrameBorder,
FrameScrolling,
FrameSetCols,
FrameSetRows,
HrColor,
HrSize,
HrWidth,
@@ -382,6 +385,15 @@ const DOM_STRING_REFLECTION_DESCRIPTORS: &[(DomStringReflection, DomStringReflec
DomStringReflection::EmbedWidth,
DomStringReflectionDescriptor::new("HTMLEmbedElement", "width", "width"),
),
(
DomStringReflection::FontFace,
DomStringReflectionDescriptor::new_html_element(
"HTMLFontElement",
"font",
"face",
"face",
),
),
(
DomStringReflection::FontSize,
DomStringReflectionDescriptor::new("HTMLFontElement", "size", "size"),
@@ -394,6 +406,24 @@ const DOM_STRING_REFLECTION_DESCRIPTORS: &[(DomStringReflection, DomStringReflec
DomStringReflection::FrameScrolling,
DomStringReflectionDescriptor::new("HTMLFrameElement", "scrolling", "scrolling"),
),
(
DomStringReflection::FrameSetCols,
DomStringReflectionDescriptor::new_html_element(
"HTMLFrameSetElement",
"frameset",
"cols",
"cols",
),
),
(
DomStringReflection::FrameSetRows,
DomStringReflectionDescriptor::new_html_element(
"HTMLFrameSetElement",
"frameset",
"rows",
"rows",
),
),
(
DomStringReflection::HrColor,
DomStringReflectionDescriptor::new("HTMLHRElement", "color", "color"),
@@ -1398,6 +1398,88 @@ fn detached_frame_legacy_accessors_use_owner_prototypes() {
assert_eq!(result, "ok");
}
#[test]
fn detached_obsolete_element_reflectors_use_owner_prototypes() {
let mut vm = new_storage_test_vm("https://detached-obsolete-reflectors.test/");
let result = vm
.eval(
r#"
(() => {
const doc = document.implementation.createHTMLDocument("");
const frameset = doc.createElement("frameset");
const frame = doc.createElement("frame");
const font = doc.createElement("font");
const marquee = doc.createElement("marquee");
const div = doc.createElement("div");
const assert = (condition, message) => {
if (!condition) throw new Error(message);
};
const own = (object, name) => Object.prototype.hasOwnProperty.call(object, name);
const throwsTypeError = callback => {
try {
callback();
return false;
} catch (error) {
return error.name === "TypeError";
}
};
const cases = [
[HTMLFrameSetElement.prototype, frameset, "cols", "40%,60%", "cols"],
[HTMLFrameSetElement.prototype, frameset, "rows", "100,*", "rows"],
[HTMLFontElement.prototype, font, "face", "serif, sans-serif", "face"],
];
for (const [prototype, element, name, value, attribute] of cases) {
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
assert(!!descriptor, `${name} descriptor`);
assert(typeof descriptor.get === "function", `${name} getter`);
assert(typeof descriptor.set === "function", `${name} setter`);
assert(descriptor.enumerable && descriptor.configurable, `${name} descriptor flags`);
assert(!own(HTMLElement.prototype, name), `${name} owner`);
assert(!(name in div), `${name} should not be on div`);
assert(throwsTypeError(() => descriptor.get.call(div)), `${name} getter brand`);
assert(throwsTypeError(() => descriptor.set.call(div, value)), `${name} setter brand`);
assert(element[name] === "", `${name} default`);
element[name] = value;
assert(element[name] === value, `${name} value`);
assert(element.getAttribute(attribute) === value, `${name} attribute`);
assert(!own(element, name), `${name} should stay inherited`);
}
for (const [prototype, element, name, attribute] of [
[HTMLFrameElement.prototype, frame, "noResize", "noresize"],
[HTMLMarqueeElement.prototype, marquee, "trueSpeed", "truespeed"],
]) {
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
assert(!!descriptor, `${name} descriptor`);
assert(typeof descriptor.get === "function", `${name} getter`);
assert(typeof descriptor.set === "function", `${name} setter`);
assert(descriptor.enumerable && descriptor.configurable, `${name} descriptor flags`);
assert(!own(HTMLElement.prototype, name), `${name} owner`);
assert(!(name in div), `${name} should not be on div`);
assert(throwsTypeError(() => descriptor.get.call(div)), `${name} getter brand`);
assert(throwsTypeError(() => descriptor.set.call(div, true)), `${name} setter brand`);
assert(element[name] === false, `${name} default`);
element.setAttribute(attribute, "false");
assert(element[name] === true, `${name} attribute presence`);
element[name] = false;
assert(!element.hasAttribute(attribute), `${name} false removes attribute`);
element[name] = true;
assert(element.getAttribute(attribute) === "", `${name} true adds attribute`);
assert(!own(element, name), `${name} should stay inherited`);
}
return "ok";
})()
"#,
)
.expect("detached obsolete element reflectors should evaluate");
assert_eq!(result, "ok");
}
#[test]
fn detached_resource_legacy_accessors_use_owner_prototypes() {
let mut vm =