mirror of
https://github.com/lexmount/moli.git
synced 2026-10-07 00:01:00 +00:00
fix(renderer): complete section element reflection
This commit is contained in:
@@ -2,6 +2,7 @@ use crate::{
|
||||
custom_elements,
|
||||
dom::native::{DocumentTitleSetterTarget, Node},
|
||||
native_bridge::abort::dom_exception_value,
|
||||
webidl,
|
||||
};
|
||||
|
||||
use super::super::{
|
||||
@@ -11,6 +12,7 @@ use super::super::{
|
||||
global_bridge_object, set_private_value, throw_type_error, v8_string, v8str,
|
||||
},
|
||||
};
|
||||
use super::element::{canonical_dir_value, element_attribute, set_reflected_attribute};
|
||||
use super::node::{
|
||||
node_is_document, node_runtime_and_handle_from_object,
|
||||
node_runtime_and_handle_from_object_or_detached,
|
||||
@@ -300,6 +302,182 @@ pub(in crate::native_bridge::document) use structure::set_document_body_for_nati
|
||||
pub(crate) const XHTML_NS: &str = "http://www.w3.org/1999/xhtml";
|
||||
pub(crate) const SVG_NS: &str = "http://www.w3.org/2000/svg";
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
enum DocumentForwardedReflection {
|
||||
Dir,
|
||||
FgColor,
|
||||
LinkColor,
|
||||
VlinkColor,
|
||||
AlinkColor,
|
||||
BgColor,
|
||||
Count,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum DocumentForwardedTarget {
|
||||
DocumentElement,
|
||||
Body,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum DocumentForwardedValueKind {
|
||||
Dir,
|
||||
LegacyNullToEmptyDomString,
|
||||
}
|
||||
|
||||
struct DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget,
|
||||
attribute: &'static str,
|
||||
member: &'static str,
|
||||
value_kind: DocumentForwardedValueKind,
|
||||
}
|
||||
|
||||
const DOCUMENT_FORWARDED_REFLECTION_DESCRIPTORS: &[(
|
||||
DocumentForwardedReflection,
|
||||
DocumentForwardedReflectionDescriptor,
|
||||
)] = &[
|
||||
(
|
||||
DocumentForwardedReflection::Dir,
|
||||
DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget::DocumentElement,
|
||||
attribute: "dir",
|
||||
member: "dir",
|
||||
value_kind: DocumentForwardedValueKind::Dir,
|
||||
},
|
||||
),
|
||||
(
|
||||
DocumentForwardedReflection::FgColor,
|
||||
DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget::Body,
|
||||
attribute: "text",
|
||||
member: "fgColor",
|
||||
value_kind: DocumentForwardedValueKind::LegacyNullToEmptyDomString,
|
||||
},
|
||||
),
|
||||
(
|
||||
DocumentForwardedReflection::LinkColor,
|
||||
DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget::Body,
|
||||
attribute: "link",
|
||||
member: "linkColor",
|
||||
value_kind: DocumentForwardedValueKind::LegacyNullToEmptyDomString,
|
||||
},
|
||||
),
|
||||
(
|
||||
DocumentForwardedReflection::VlinkColor,
|
||||
DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget::Body,
|
||||
attribute: "vlink",
|
||||
member: "vlinkColor",
|
||||
value_kind: DocumentForwardedValueKind::LegacyNullToEmptyDomString,
|
||||
},
|
||||
),
|
||||
(
|
||||
DocumentForwardedReflection::AlinkColor,
|
||||
DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget::Body,
|
||||
attribute: "alink",
|
||||
member: "alinkColor",
|
||||
value_kind: DocumentForwardedValueKind::LegacyNullToEmptyDomString,
|
||||
},
|
||||
),
|
||||
(
|
||||
DocumentForwardedReflection::BgColor,
|
||||
DocumentForwardedReflectionDescriptor {
|
||||
target: DocumentForwardedTarget::Body,
|
||||
attribute: "bgcolor",
|
||||
member: "bgColor",
|
||||
value_kind: DocumentForwardedValueKind::LegacyNullToEmptyDomString,
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
const _: () = {
|
||||
assert!(
|
||||
DOCUMENT_FORWARDED_REFLECTION_DESCRIPTORS.len()
|
||||
== DocumentForwardedReflection::Count as usize
|
||||
);
|
||||
let mut index = 0;
|
||||
while index < DOCUMENT_FORWARDED_REFLECTION_DESCRIPTORS.len() {
|
||||
assert!(DOCUMENT_FORWARDED_REFLECTION_DESCRIPTORS[index].0 as usize == index);
|
||||
index += 1;
|
||||
}
|
||||
};
|
||||
|
||||
impl DocumentForwardedReflection {
|
||||
fn descriptor_from_callback_data(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
data: v8::Local<'_, v8::Value>,
|
||||
) -> Option<&'static DocumentForwardedReflectionDescriptor> {
|
||||
DOCUMENT_FORWARDED_REFLECTION_DESCRIPTORS
|
||||
.get(data.uint32_value(scope)? as usize)
|
||||
.map(|(_, descriptor)| descriptor)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> moli_webapi_declare::WebApiValue<'s> for DocumentForwardedReflection {
|
||||
fn to_v8_value(&self, scope: &mut v8::PinScope<'s, '_>) -> Option<v8::Local<'s, v8::Value>> {
|
||||
Some(v8::Integer::new_from_unsigned(scope, *self as u32).into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s> moli_webapi_declare::WebApiTemplateValue<'s> for DocumentForwardedReflection {
|
||||
fn to_v8_template_value(
|
||||
&self,
|
||||
scope: &mut v8::PinScope<'s, '_, ()>,
|
||||
) -> Option<v8::Local<'s, v8::Value>> {
|
||||
Some(v8::Integer::new_from_unsigned(scope, *self as u32).into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(name = "Document", enumerable)]
|
||||
struct DocumentForwardedReflectionPrototypeDeclaration {
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = document_forwarded_reflection_getter_function,
|
||||
setter = document_forwarded_reflection_setter_function,
|
||||
data = DocumentForwardedReflection::Dir
|
||||
)]
|
||||
dir: (),
|
||||
#[webapi(
|
||||
accessor_property = "fgColor",
|
||||
getter = document_forwarded_reflection_getter_function,
|
||||
setter = document_forwarded_reflection_setter_function,
|
||||
data = DocumentForwardedReflection::FgColor
|
||||
)]
|
||||
fg_color: (),
|
||||
#[webapi(
|
||||
accessor_property = "linkColor",
|
||||
getter = document_forwarded_reflection_getter_function,
|
||||
setter = document_forwarded_reflection_setter_function,
|
||||
data = DocumentForwardedReflection::LinkColor
|
||||
)]
|
||||
link_color: (),
|
||||
#[webapi(
|
||||
accessor_property = "vlinkColor",
|
||||
getter = document_forwarded_reflection_getter_function,
|
||||
setter = document_forwarded_reflection_setter_function,
|
||||
data = DocumentForwardedReflection::VlinkColor
|
||||
)]
|
||||
vlink_color: (),
|
||||
#[webapi(
|
||||
accessor_property = "alinkColor",
|
||||
getter = document_forwarded_reflection_getter_function,
|
||||
setter = document_forwarded_reflection_setter_function,
|
||||
data = DocumentForwardedReflection::AlinkColor
|
||||
)]
|
||||
alink_color: (),
|
||||
#[webapi(
|
||||
accessor_property = "bgColor",
|
||||
getter = document_forwarded_reflection_getter_function,
|
||||
setter = document_forwarded_reflection_setter_function,
|
||||
data = DocumentForwardedReflection::BgColor
|
||||
)]
|
||||
bg_color: (),
|
||||
}
|
||||
|
||||
#[derive(WebApiFunctionTemplate)]
|
||||
#[webapi(name = "Document", enumerable)]
|
||||
struct DocumentMetadataPrototypeDeclaration {
|
||||
@@ -518,6 +696,125 @@ fn document_receiver_runtime_and_handle<'s>(
|
||||
Some((runtime_ptr, handle))
|
||||
}
|
||||
|
||||
fn document_forwarded_target_handle(
|
||||
runtime: &JsContextHost,
|
||||
document_handle: DomHandle,
|
||||
target: DocumentForwardedTarget,
|
||||
for_setter: bool,
|
||||
) -> Option<DomHandle> {
|
||||
let dom = runtime.dom_host().dom();
|
||||
let document = dom.node(document_handle).and_then(Node::as_document)?;
|
||||
let handle = match target {
|
||||
DocumentForwardedTarget::DocumentElement => {
|
||||
document.document_element_handle(dom, document_handle)?
|
||||
}
|
||||
DocumentForwardedTarget::Body => match document.body_handle(dom, document_handle) {
|
||||
Some(body) => body,
|
||||
None if !for_setter => {
|
||||
let document_element = document.document_element_handle(dom, document_handle)?;
|
||||
dom.find_child(document_element, |handle| {
|
||||
dom.node(handle)
|
||||
.is_some_and(|node| node.is_html_element_named("frameset"))
|
||||
})?
|
||||
}
|
||||
None => return None,
|
||||
},
|
||||
};
|
||||
let node = runtime.dom_host().node(handle)?;
|
||||
let matches = match target {
|
||||
DocumentForwardedTarget::DocumentElement => node.is_html_element_named("html"),
|
||||
DocumentForwardedTarget::Body => {
|
||||
node.is_html_element_named("body")
|
||||
|| (!for_setter && node.is_html_element_named("frameset"))
|
||||
}
|
||||
};
|
||||
matches.then_some(handle)
|
||||
}
|
||||
|
||||
fn document_forwarded_reflection_getter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
let Some(descriptor) =
|
||||
DocumentForwardedReflection::descriptor_from_callback_data(scope, args.data())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some((runtime_ptr, document_handle)) =
|
||||
document_receiver_runtime_and_handle(scope, args.this())
|
||||
else {
|
||||
throw_type_error(
|
||||
scope,
|
||||
"Failed to get a reflected property on 'Document': Illegal invocation.",
|
||||
);
|
||||
return;
|
||||
};
|
||||
let runtime = unsafe { &*runtime_ptr };
|
||||
let Some(target_handle) =
|
||||
document_forwarded_target_handle(runtime, document_handle, descriptor.target, false)
|
||||
else {
|
||||
rv.set_empty_string();
|
||||
return;
|
||||
};
|
||||
let raw = element_attribute(runtime, target_handle, descriptor.attribute).unwrap_or_default();
|
||||
let value = match descriptor.value_kind {
|
||||
DocumentForwardedValueKind::Dir => canonical_dir_value(&raw),
|
||||
DocumentForwardedValueKind::LegacyNullToEmptyDomString => raw.as_str(),
|
||||
};
|
||||
set_document_string_return_value(scope, &mut rv, value);
|
||||
}
|
||||
|
||||
fn document_forwarded_reflection_setter_function<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
mut rv: v8::ReturnValue<'s, v8::Value>,
|
||||
) {
|
||||
let Some(descriptor) =
|
||||
DocumentForwardedReflection::descriptor_from_callback_data(scope, args.data())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
let Some((runtime_ptr, document_handle)) =
|
||||
document_receiver_runtime_and_handle(scope, args.this())
|
||||
else {
|
||||
throw_type_error(
|
||||
scope,
|
||||
"Failed to set a reflected property on 'Document': Illegal invocation.",
|
||||
);
|
||||
return;
|
||||
};
|
||||
let options = webidl::StringOptions {
|
||||
treat_null_as_empty_string: descriptor.value_kind
|
||||
== DocumentForwardedValueKind::LegacyNullToEmptyDomString,
|
||||
};
|
||||
let value = match webidl::convert_with_options::<webidl::DomString>(
|
||||
scope,
|
||||
args.get(0),
|
||||
webidl::Context::member("Document", descriptor.member),
|
||||
&options,
|
||||
) {
|
||||
Ok(value) => value.0,
|
||||
Err(error) => {
|
||||
webidl::throw_error(scope, &error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let runtime = unsafe { &*runtime_ptr };
|
||||
if let Some(target_handle) =
|
||||
document_forwarded_target_handle(runtime, document_handle, descriptor.target, true)
|
||||
{
|
||||
set_reflected_attribute(
|
||||
scope,
|
||||
runtime_ptr,
|
||||
target_handle,
|
||||
descriptor.attribute,
|
||||
&value,
|
||||
);
|
||||
}
|
||||
rv.set_undefined();
|
||||
}
|
||||
|
||||
fn document_obsolete_noop_callback<'s>(
|
||||
scope: &mut v8::PinScope<'s, '_>,
|
||||
args: v8::FunctionCallbackArguments<'s>,
|
||||
@@ -1356,6 +1653,9 @@ pub(crate) fn install_document_template_bindings<'s>(
|
||||
if interface_name == "Document" {
|
||||
DocumentMetadataPrototypeDeclaration::initialize_prototype_template(scope, prototype);
|
||||
DocumentStructurePrototypeDeclaration::initialize_prototype_template(scope, prototype);
|
||||
DocumentForwardedReflectionPrototypeDeclaration::initialize_prototype_template(
|
||||
scope, prototype,
|
||||
);
|
||||
DocumentFocusPrototypeDeclaration::initialize_prototype_template(scope, prototype);
|
||||
DocumentViewPrototypeDeclaration::initialize_prototype_template(scope, prototype);
|
||||
DocumentStatePrototypeDeclaration::initialize_prototype_template(scope, prototype);
|
||||
|
||||
@@ -411,8 +411,8 @@ pub(super) use geometry::{
|
||||
pub(super) use global_attributes::{
|
||||
anchor_target_getter_function, anchor_target_setter_function, area_no_href_setter_function,
|
||||
area_target_getter_function, area_target_setter_function, base_target_getter_function,
|
||||
base_target_setter_function, canonical_cross_origin_value, canonical_loading_value,
|
||||
canonical_preload_value, canonical_referrer_policy_value,
|
||||
base_target_setter_function, canonical_cross_origin_value, canonical_dir_value,
|
||||
canonical_loading_value, canonical_preload_value, canonical_referrer_policy_value,
|
||||
dom_string_reflection_getter_function, dom_string_reflection_setter_function,
|
||||
html_align_getter_function, html_align_setter_function, html_alt_getter_function,
|
||||
html_as_getter_function, html_bg_color_getter_function, html_border_getter_function,
|
||||
@@ -475,30 +475,28 @@ pub(in crate::native_bridge) const BODY_LEGACY_PROTOTYPE_ACCESSORS: &[&str] = &[
|
||||
];
|
||||
|
||||
use html_elements::{
|
||||
body_a_link_getter_function, body_a_link_setter_function, body_background_getter_function,
|
||||
body_background_setter_function, body_link_getter_function, body_link_setter_function,
|
||||
body_text_getter_function, body_text_setter_function, body_v_link_getter_function,
|
||||
body_v_link_setter_function, li_value_getter_function, li_value_setter_function,
|
||||
meta_content_getter_function, meta_content_setter_function, meta_http_equiv_getter_function,
|
||||
meta_http_equiv_setter_function, ol_reversed_getter_function, ol_reversed_setter_function,
|
||||
ol_start_getter_function, ol_start_setter_function, ol_type_getter_function,
|
||||
ol_type_setter_function, optgroup_disabled_getter_function, optgroup_disabled_setter_function,
|
||||
table_caption_getter_function, table_caption_setter_function,
|
||||
table_cell_col_span_getter_function, table_cell_col_span_setter_function,
|
||||
table_cell_index_getter_function, table_cell_row_span_getter_function,
|
||||
table_cell_row_span_setter_function, table_col_span_getter_function,
|
||||
table_col_span_setter_function, table_create_caption_callback, table_create_t_body_callback,
|
||||
table_create_t_foot_callback, table_create_t_head_callback, table_delete_caption_callback,
|
||||
table_delete_row_callback, table_delete_t_foot_callback, table_delete_t_head_callback,
|
||||
table_insert_row_callback, table_row_cells_getter_function, table_row_delete_cell_callback,
|
||||
table_row_index_getter_function, table_row_insert_cell_callback, table_rows_getter_function,
|
||||
table_section_delete_row_callback, table_section_insert_row_callback,
|
||||
table_section_row_index_getter_function, table_section_rows_getter_function,
|
||||
table_t_bodies_getter_function, table_t_foot_getter_function, table_t_foot_setter_function,
|
||||
table_t_head_getter_function, table_t_head_setter_function, track_default_getter_function,
|
||||
track_default_setter_function, track_kind_getter_function, track_kind_setter_function,
|
||||
track_ready_state_getter_function, track_src_getter_function, track_src_setter_function,
|
||||
track_srclang_getter_function, track_srclang_setter_function,
|
||||
body_background_getter_function, body_background_setter_function, li_value_getter_function,
|
||||
li_value_setter_function, meta_content_getter_function, meta_content_setter_function,
|
||||
meta_http_equiv_getter_function, meta_http_equiv_setter_function, ol_reversed_getter_function,
|
||||
ol_reversed_setter_function, ol_start_getter_function, ol_start_setter_function,
|
||||
ol_type_getter_function, ol_type_setter_function, optgroup_disabled_getter_function,
|
||||
optgroup_disabled_setter_function, table_caption_getter_function,
|
||||
table_caption_setter_function, table_cell_col_span_getter_function,
|
||||
table_cell_col_span_setter_function, table_cell_index_getter_function,
|
||||
table_cell_row_span_getter_function, table_cell_row_span_setter_function,
|
||||
table_col_span_getter_function, table_col_span_setter_function, table_create_caption_callback,
|
||||
table_create_t_body_callback, table_create_t_foot_callback, table_create_t_head_callback,
|
||||
table_delete_caption_callback, table_delete_row_callback, table_delete_t_foot_callback,
|
||||
table_delete_t_head_callback, table_insert_row_callback, table_row_cells_getter_function,
|
||||
table_row_delete_cell_callback, table_row_index_getter_function,
|
||||
table_row_insert_cell_callback, table_rows_getter_function, table_section_delete_row_callback,
|
||||
table_section_insert_row_callback, table_section_row_index_getter_function,
|
||||
table_section_rows_getter_function, table_t_bodies_getter_function,
|
||||
table_t_foot_getter_function, table_t_foot_setter_function, table_t_head_getter_function,
|
||||
table_t_head_setter_function, track_default_getter_function, track_default_setter_function,
|
||||
track_kind_getter_function, track_kind_setter_function, track_ready_state_getter_function,
|
||||
track_src_getter_function, track_src_setter_function, track_srclang_getter_function,
|
||||
track_srclang_setter_function,
|
||||
};
|
||||
use html_elements::{
|
||||
marquee_loop_getter_function, marquee_loop_setter_function,
|
||||
@@ -568,6 +566,7 @@ pub(super) use query::{
|
||||
node_get_elements_by_tag_name_ns_callback, node_matches_callback,
|
||||
node_query_selector_all_callback, node_query_selector_callback,
|
||||
};
|
||||
pub(super) use reflection::set_reflected_attribute;
|
||||
use reflection::{
|
||||
CrossOriginReflection, DomStringReflection, ElementReflectionInterface,
|
||||
NullToEmptyDomStringReflection, UnsignedLongReflection, UsvStringReflection,
|
||||
@@ -578,8 +577,8 @@ use reflection::{
|
||||
remove_reflected_attribute, set_attribute_property_on_object_or_detached,
|
||||
set_boolean_attribute_property_on_object_or_detached,
|
||||
set_dom_string_attribute_property_on_object,
|
||||
set_nullable_dom_string_attribute_property_on_object, set_reflected_attribute,
|
||||
set_reflected_boolean_attribute, set_reflected_style_attribute_with_inline_base_url,
|
||||
set_nullable_dom_string_attribute_property_on_object, set_reflected_boolean_attribute,
|
||||
set_reflected_style_attribute_with_inline_base_url,
|
||||
set_usv_string_attribute_property_on_object,
|
||||
};
|
||||
pub(crate) use shadow_dom::install_element_internals_template_bindings;
|
||||
@@ -610,7 +609,8 @@ use shadow_dom::{
|
||||
template_shadow_root_slot_assignment_getter_function,
|
||||
template_shadow_root_slot_assignment_setter_function,
|
||||
};
|
||||
use shared::{element_attribute, element_attribute_names, element_has_attribute, style_string};
|
||||
pub(super) use shared::element_attribute;
|
||||
use shared::{element_attribute_names, element_has_attribute, style_string};
|
||||
pub(super) use state_callbacks::{
|
||||
bridge_set_checked_state_callback, bridge_set_indeterminate_state_callback,
|
||||
bridge_set_input_value_callback, bridge_set_selected_state_callback,
|
||||
@@ -1759,26 +1759,30 @@ struct HtmlBodyOrFrameSetEventHandlersPrototypeDeclaration {
|
||||
struct HtmlBodyElementLegacyPrototypeDeclaration {
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = body_text_getter_function,
|
||||
setter = body_text_setter_function
|
||||
getter = null_to_empty_dom_string_reflection_getter_function,
|
||||
setter = null_to_empty_dom_string_reflection_setter_function,
|
||||
data = NullToEmptyDomStringReflection::BodyText
|
||||
)]
|
||||
text: (),
|
||||
#[webapi(
|
||||
accessor_property,
|
||||
getter = body_link_getter_function,
|
||||
setter = body_link_setter_function
|
||||
getter = null_to_empty_dom_string_reflection_getter_function,
|
||||
setter = null_to_empty_dom_string_reflection_setter_function,
|
||||
data = NullToEmptyDomStringReflection::BodyLink
|
||||
)]
|
||||
link: (),
|
||||
#[webapi(
|
||||
accessor_property = "vLink",
|
||||
getter = body_v_link_getter_function,
|
||||
setter = body_v_link_setter_function
|
||||
getter = null_to_empty_dom_string_reflection_getter_function,
|
||||
setter = null_to_empty_dom_string_reflection_setter_function,
|
||||
data = NullToEmptyDomStringReflection::BodyVLink
|
||||
)]
|
||||
v_link: (),
|
||||
#[webapi(
|
||||
accessor_property = "aLink",
|
||||
getter = body_a_link_getter_function,
|
||||
setter = body_a_link_setter_function
|
||||
getter = null_to_empty_dom_string_reflection_getter_function,
|
||||
setter = null_to_empty_dom_string_reflection_setter_function,
|
||||
data = NullToEmptyDomStringReflection::BodyALink
|
||||
)]
|
||||
a_link: (),
|
||||
#[webapi(
|
||||
|
||||
@@ -718,7 +718,7 @@ pub(in crate::native_bridge) fn node_dir_getter_function<'s>(
|
||||
canonical_attribute_getter_from_object(scope, args.this(), "dir", canonical_dir_value, rv);
|
||||
}
|
||||
|
||||
fn canonical_dir_value(raw: &str) -> &'static str {
|
||||
pub(in crate::native_bridge) fn canonical_dir_value(raw: &str) -> &'static str {
|
||||
if raw.eq_ignore_ascii_case("ltr") {
|
||||
"ltr"
|
||||
} else if raw.eq_ignore_ascii_case("rtl") {
|
||||
@@ -1093,7 +1093,7 @@ pub(in crate::native_bridge) fn null_to_empty_dom_string_reflection_getter_funct
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if let Some(local_name) = descriptor.local_name
|
||||
if let Some(local_name) = descriptor.html_local_name
|
||||
&& html_element_getter_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
@@ -1116,7 +1116,7 @@ pub(in crate::native_bridge) fn null_to_empty_dom_string_reflection_setter_funct
|
||||
if let Some(descriptor) =
|
||||
NullToEmptyDomStringReflection::descriptor_from_callback_data(scope, args.data())
|
||||
{
|
||||
if let Some(local_name) = descriptor.local_name
|
||||
if let Some(local_name) = descriptor.html_local_name
|
||||
&& html_element_setter_receiver(
|
||||
scope,
|
||||
args.this(),
|
||||
|
||||
@@ -110,30 +110,6 @@ macro_rules! body_attr_reflection {
|
||||
};
|
||||
}
|
||||
|
||||
body_attr_reflection!(
|
||||
body_text_getter_function,
|
||||
body_text_setter_function,
|
||||
"text",
|
||||
"text"
|
||||
);
|
||||
body_attr_reflection!(
|
||||
body_link_getter_function,
|
||||
body_link_setter_function,
|
||||
"link",
|
||||
"link"
|
||||
);
|
||||
body_attr_reflection!(
|
||||
body_v_link_getter_function,
|
||||
body_v_link_setter_function,
|
||||
"vlink",
|
||||
"vLink"
|
||||
);
|
||||
body_attr_reflection!(
|
||||
body_a_link_getter_function,
|
||||
body_a_link_setter_function,
|
||||
"alink",
|
||||
"aLink"
|
||||
);
|
||||
body_attr_reflection!(
|
||||
body_background_getter_function,
|
||||
body_background_setter_function,
|
||||
|
||||
@@ -908,7 +908,11 @@ impl_reflection_callback_data!(UsvStringReflection);
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
pub(super) enum NullToEmptyDomStringReflection {
|
||||
BodyALink,
|
||||
BodyBgColor,
|
||||
BodyLink,
|
||||
BodyText,
|
||||
BodyVLink,
|
||||
FontColor,
|
||||
FrameMarginHeight,
|
||||
FrameMarginWidth,
|
||||
@@ -928,10 +932,26 @@ const NULL_TO_EMPTY_DOM_STRING_REFLECTION_DESCRIPTORS: &[(
|
||||
NullToEmptyDomStringReflection,
|
||||
ReflectedAttributeDescriptor,
|
||||
)] = &[
|
||||
(
|
||||
NullToEmptyDomStringReflection::BodyALink,
|
||||
ReflectedAttributeDescriptor::new_html_element("HTMLBodyElement", "body", "alink", "aLink"),
|
||||
),
|
||||
(
|
||||
NullToEmptyDomStringReflection::BodyBgColor,
|
||||
ReflectedAttributeDescriptor::new("HTMLBodyElement", "bgcolor", "bgColor"),
|
||||
),
|
||||
(
|
||||
NullToEmptyDomStringReflection::BodyLink,
|
||||
ReflectedAttributeDescriptor::new_html_element("HTMLBodyElement", "body", "link", "link"),
|
||||
),
|
||||
(
|
||||
NullToEmptyDomStringReflection::BodyText,
|
||||
ReflectedAttributeDescriptor::new_html_element("HTMLBodyElement", "body", "text", "text"),
|
||||
),
|
||||
(
|
||||
NullToEmptyDomStringReflection::BodyVLink,
|
||||
ReflectedAttributeDescriptor::new_html_element("HTMLBodyElement", "body", "vlink", "vLink"),
|
||||
),
|
||||
(
|
||||
NullToEmptyDomStringReflection::FontColor,
|
||||
ReflectedAttributeDescriptor::new("HTMLFontElement", "color", "color"),
|
||||
@@ -1140,7 +1160,7 @@ impl CrossOriginReflection {
|
||||
|
||||
impl_reflection_callback_data!(CrossOriginReflection);
|
||||
|
||||
pub(super) fn set_reflected_attribute(
|
||||
pub(in crate::native_bridge) fn set_reflected_attribute(
|
||||
scope: &mut v8::PinScope<'_, '_>,
|
||||
runtime_ptr: *mut super::super::JsContextHost,
|
||||
handle: DomHandle,
|
||||
|
||||
@@ -15,7 +15,7 @@ fn with_element<T>(
|
||||
.map(map)
|
||||
}
|
||||
|
||||
pub(super) fn element_attribute(
|
||||
pub(in crate::native_bridge) fn element_attribute(
|
||||
runtime: &JsContextHost,
|
||||
handle: DomHandle,
|
||||
name: &str,
|
||||
|
||||
@@ -3704,6 +3704,114 @@ fn document_structure_uses_document_prototype_accessors() {
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn document_forwarded_reflections_use_html_targets() {
|
||||
let mut vm = new_storage_test_vm("https://document-forwarded-reflections.test/");
|
||||
|
||||
let result = vm
|
||||
.eval(
|
||||
r#"
|
||||
(() => {
|
||||
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 instanceof TypeError;
|
||||
}
|
||||
};
|
||||
const names = ["dir", "fgColor", "linkColor", "vlinkColor", "alinkColor", "bgColor"];
|
||||
const colorAttributes = {
|
||||
fgColor: "text",
|
||||
linkColor: "link",
|
||||
vlinkColor: "vlink",
|
||||
alinkColor: "alink",
|
||||
bgColor: "bgcolor"
|
||||
};
|
||||
const descriptors = new Map();
|
||||
for (const name of names) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(Document.prototype, name);
|
||||
assert(!!descriptor, `${name} descriptor missing`);
|
||||
assert(typeof descriptor.get === "function", `${name} getter`);
|
||||
assert(typeof descriptor.set === "function", `${name} setter`);
|
||||
assert(descriptor.enumerable === true, `${name} enumerable`);
|
||||
assert(descriptor.configurable === true, `${name} configurable`);
|
||||
descriptors.set(name, descriptor);
|
||||
}
|
||||
|
||||
if (!document.documentElement) {
|
||||
const html = document.createElement("html");
|
||||
html.append(document.createElement("head"), document.createElement("body"));
|
||||
document.append(html);
|
||||
}
|
||||
const detachedDocument = document.implementation.createHTMLDocument("");
|
||||
for (const [doc, label] of [[document, "live"], [detachedDocument, "detached"]]) {
|
||||
for (const name of names) {
|
||||
assert(!own(doc, name), `${label}.${name} should not be own before set`);
|
||||
assert(doc[name] === "", `${label}.${name} missing-value default`);
|
||||
}
|
||||
|
||||
doc.documentElement.setAttribute("dir", "RTL");
|
||||
assert(doc.dir === "rtl", `${label}.dir canonical getter`);
|
||||
doc.dir = { toString: () => "AUTO" };
|
||||
assert(doc.documentElement.getAttribute("dir") === "AUTO", `${label}.dir target attribute`);
|
||||
assert(doc.dir === "auto", `${label}.dir setter canonical getter`);
|
||||
doc.dir = null;
|
||||
assert(doc.documentElement.getAttribute("dir") === "null", `${label}.dir null attribute`);
|
||||
assert(doc.dir === "", `${label}.dir null canonical getter`);
|
||||
|
||||
for (const [name, attribute] of Object.entries(colorAttributes)) {
|
||||
doc[name] = { toString: () => `${label}-${name}` };
|
||||
assert(doc[name] === `${label}-${name}`, `${label}.${name} getter`);
|
||||
assert(doc.body.getAttribute(attribute) === `${label}-${name}`, `${label}.${name} target attribute`);
|
||||
doc[name] = null;
|
||||
assert(doc[name] === "", `${label}.${name} null getter`);
|
||||
assert(doc.body.getAttribute(attribute) === "", `${label}.${name} null attribute`);
|
||||
}
|
||||
|
||||
for (const name of names) {
|
||||
assert(!own(doc, name), `${label}.${name} should stay inherited after set`);
|
||||
assert(delete doc[name], `${label}.${name} delete`);
|
||||
assert(!own(doc, name), `${label}.${name} should stay inherited after delete`);
|
||||
}
|
||||
}
|
||||
|
||||
const xmlDocument = document.implementation.createDocument("urn:test", "root", null);
|
||||
let converted = false;
|
||||
xmlDocument.dir = { toString() { converted = true; return "rtl"; } };
|
||||
assert(converted, "XML document setter should still convert the value");
|
||||
assert(xmlDocument.dir === "", "XML document dir getter");
|
||||
assert(!xmlDocument.documentElement.hasAttribute("dir"), "XML document should not forward dir");
|
||||
xmlDocument.fgColor = "red";
|
||||
assert(xmlDocument.fgColor === "", "XML document color getter");
|
||||
|
||||
const framesetDocument = document.implementation.createHTMLDocument("");
|
||||
const frameset = framesetDocument.createElement("frameset");
|
||||
frameset.setAttribute("text", "seed");
|
||||
framesetDocument.body = frameset;
|
||||
assert(framesetDocument.fgColor === "seed", "frameset color getter target");
|
||||
framesetDocument.fgColor = "changed";
|
||||
assert(frameset.getAttribute("text") === "seed", "frameset color setter should be a no-op");
|
||||
|
||||
for (const [name, descriptor] of descriptors) {
|
||||
for (const receiver of [document.documentElement, {}]) {
|
||||
assert(throwsTypeError(() => descriptor.get.call(receiver)), `${name} getter receiver`);
|
||||
assert(throwsTypeError(() => descriptor.set.call(receiver, "wrong")), `${name} setter receiver`);
|
||||
}
|
||||
}
|
||||
return "ok";
|
||||
})()
|
||||
"#,
|
||||
)
|
||||
.expect("document forwarded reflections should evaluate");
|
||||
|
||||
assert_eq!(result, "ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detached_document_type_metadata_uses_prototype_accessors() {
|
||||
let mut vm = new_storage_test_vm("https://detached-doctype-prototype.test/");
|
||||
@@ -6654,6 +6762,14 @@ fn detached_body_legacy_accessors_use_owner_prototype() {
|
||||
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 instanceof TypeError;
|
||||
}
|
||||
};
|
||||
const accessor = (prototype, name) => {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
|
||||
assert(!!descriptor, `${prototype.constructor.name}.${name} descriptor missing`);
|
||||
@@ -6692,13 +6808,26 @@ fn detached_body_legacy_accessors_use_owner_prototype() {
|
||||
assert(body.vLink === `${label}-vlink` && body.getAttribute("vlink") === `${label}-vlink`, `${label}.vLink`);
|
||||
assert(body.aLink === `${label}-alink` && body.getAttribute("alink") === `${label}-alink`, `${label}.aLink`);
|
||||
assert(body.background === `${label}-background` && body.getAttribute("background") === `${label}-background`, `${label}.background`);
|
||||
for (const name of ["text", "link", "vLink", "aLink"]) {
|
||||
body[name] = null;
|
||||
const attribute = name.toLowerCase();
|
||||
assert(body[name] === "", `${label}.${name} null getter`);
|
||||
assert(body.getAttribute(attribute) === "", `${label}.${name} null attribute`);
|
||||
}
|
||||
for (const name of names) {
|
||||
assert(!own(body, name), `${label}.${name} should not be own after set`);
|
||||
assert(delete body[name], `${label}.${name} delete`);
|
||||
assert(!own(body, name), `${label}.${name} should stay inherited`);
|
||||
}
|
||||
assert(body.onload === handler, `${label}.onload after delete`);
|
||||
assert(body.text === `${label}-text`, `${label}.text after delete`);
|
||||
assert(body.text === "", `${label}.text after delete`);
|
||||
}
|
||||
for (const name of ["text", "link", "vLink", "aLink"]) {
|
||||
const descriptor = Object.getOwnPropertyDescriptor(HTMLBodyElement.prototype, name);
|
||||
for (const receiver of [document.createElement("div"), {}]) {
|
||||
assert(throwsTypeError(() => descriptor.get.call(receiver)), `${name} getter receiver`);
|
||||
assert(throwsTypeError(() => descriptor.set.call(receiver, "wrong")), `${name} setter receiver`);
|
||||
}
|
||||
}
|
||||
window.onload = null;
|
||||
return "ok";
|
||||
|
||||
Reference in New Issue
Block a user