Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions moli-dom/src/native/host/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,32 @@ impl DomHost {
})
}

/// Whether any current element can match this legacy named-property key.
///
/// This is deliberately independent of connectivity and document/tree scope:
/// callers may use a miss to avoid a traversal, but must still validate the
/// owner and scope on a hit. Reuse the incrementally maintained named indexes
/// rather than caching misses (custom-element upgrades can change eligibility
/// without changing an element's id or name).
pub fn has_element_with_named_item_key(&self, key: &str) -> bool {
if key.is_empty() {
return false;
}
self.ensure_id_index();
self.ensure_name_index();
let matches = |index: &NamedElementIndex| {
index.handles_by_value.get(key).is_some_and(|handles| {
handles.iter().any(|handle| {
self.node(*handle)
.and_then(Node::as_element)
.is_some_and(|element| element.matches_named_item_key(key))
})
})
};
self.id_index.borrow().as_ref().is_some_and(matches)
|| self.name_index.borrow().as_ref().is_some_and(matches)
}

pub fn element_handles_by_id_or_name_matching(
&self,
key: &str,
Expand Down
23 changes: 23 additions & 0 deletions moli-dom/src/native/host/query_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,29 @@ mod tests {
))
}

#[test]
fn global_named_item_candidate_check_tracks_live_attributes_in_detached_trees() {
let mut host = test_host();
assert!(!host.has_element_with_named_item_key("late"));
assert!(!host.has_element_with_named_item_key(""));
let input = host.create_element("input");
assert!(host.set_attribute(input, "name", "late"));
assert!(host.has_element_with_named_item_key("late"));
assert!(host.set_attribute(input, "name", "renamed"));
assert!(!host.has_element_with_named_item_key("late"));
assert!(host.has_element_with_named_item_key("renamed"));
assert!(host.set_attribute(input, "id", "renamed"));
assert!(host.remove_attribute(input, "name"));
assert!(host.has_element_with_named_item_key("renamed"));
let root = host.create_element("div");
assert!(host.append_child(root, input));
assert!(host.has_element_with_named_item_key("renamed"));
assert!(host.remove_attribute(input, "id"));
assert!(!host.has_element_with_named_item_key("renamed"));
assert!(host.set_attribute(input, "name", ""));
assert!(!host.has_element_with_named_item_key(""));
}

#[test]
fn cached_tag_name_collection_reuses_query_until_mutation() {
let mut host = test_host();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -487,9 +487,16 @@ pub(in crate::native_bridge::document) fn build_detached_element_object<'s>(
let mut object = if let Some(proto) =
element_interface_prototype(scope, owner_document, prototype_name, html_interface_like)
{
DetachedElementObjectDeclaration::new(proto, to_string_tag)
.bind(scope)
.ok()?
let declaration = DetachedElementObjectDeclaration::new(proto, to_string_tag);
if html_interface_like && local_name == "form" {
let template = v8::ObjectTemplate::new(scope);
crate::native_bridge::element::install_form_property_handlers(template);
let object = template.new_instance(scope)?;
declaration.bind_into(scope, object).ok()?;
object
} else {
declaration.bind(scope).ok()?
}
} else {
new_detached_object_with_prototype(scope, "__detachedElementPrototype", to_string_tag)?
};
Expand Down
29 changes: 28 additions & 1 deletion moli-renderer-v8/src/native_bridge/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,32 @@ mod event_handlers;
mod events;
mod focus;
mod forms;

#[cfg(test)]
std::thread_local! {
static FORM_LOOKUP_WORK: std::cell::Cell<(u64, u64)> = const { std::cell::Cell::new((0, 0)) };
}

#[cfg(test)]
pub(crate) fn take_form_lookup_work_for_test() -> (u64, u64) {
FORM_LOOKUP_WORK.with(|work| work.replace((0, 0)))
}

#[cfg(test)]
fn record_form_lookup_traversal_for_test() {
FORM_LOOKUP_WORK.with(|work| {
let (traversals, enumerations) = work.get();
work.set((traversals + 1, enumerations));
});
}

#[cfg(test)]
fn record_form_lookup_enumeration_for_test() {
FORM_LOOKUP_WORK.with(|work| {
let (traversals, enumerations) = work.get();
work.set((traversals, enumerations + 1));
});
}
mod geometry;
mod global_attributes;
mod html_elements;
Expand Down Expand Up @@ -645,7 +671,8 @@ use stylesheets::{
style_blocking_setter_function, style_disabled_getter_function, style_disabled_setter_function,
};
pub(super) use template_install::{
install_specialized_instance_properties, install_specialized_template,
install_form_property_handlers, install_specialized_instance_properties,
install_specialized_template,
};
pub(super) use tree_mutation::{
node_insert_adjacent_element_callback, node_insert_adjacent_html_callback,
Expand Down
2 changes: 1 addition & 1 deletion moli-renderer-v8/src/native_bridge/element/forms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use super::super::{
current_or_live_delegate_node_arg_handle, insert_before_in_reaction_scope,
insert_before_to_current_reaction_queue, node_or_foreign_arg_handle,
node_or_foreign_arg_handle_allow_detached, node_runtime_and_handle_from_args,
node_runtime_and_handle_from_args_or_detached, node_runtime_and_handle_from_object,
node_runtime_and_handle_from_args_or_detached,
node_runtime_and_handle_from_object_or_detached, remove_child_in_reaction_scope,
remove_child_to_current_reaction_queue, set_text_content_in_reaction_scope,
set_wrapped_node_or_null,
Expand Down
Loading
Loading