Skip to main content

euv_core/renderer/render/
impl.rs

1use crate::*;
2
3/// Implementation of the virtual DOM renderer.
4impl Renderer {
5    /// Renders the given virtual DOM tree into the real DOM.
6    ///
7    /// If a previous tree exists, patches the existing DOM to match the new tree.
8    /// Otherwise, creates new DOM nodes from scratch and appends them to the root.
9    ///
10    /// # Arguments
11    ///
12    /// - `VirtualNode`: The new virtual DOM tree to render.
13    pub fn render(&mut self, vnode: VirtualNode) {
14        let new_unwrapped: VirtualNode = self.unwrap_component(&vnode);
15        if let Some(old_vnode) = self.try_get_current_tree() {
16            let old_unwrapped: VirtualNode = self.unwrap_component(old_vnode);
17            self.patch_root(&old_unwrapped, &new_unwrapped);
18        } else {
19            let dom_node: Node = self.create_dom_node(&new_unwrapped);
20            while let Some(child) = self.get_root().first_child() {
21                self.get_root().remove_child(&child).unwrap();
22            }
23            self.get_root().append_child(&dom_node).unwrap();
24        }
25        self.set_current_tree(Some(vnode));
26    }
27
28    /// Patches the root DOM tree by replacing the single child of `self.root`.
29    ///
30    /// # Arguments
31    ///
32    /// - `&VirtualNode`: The old virtual node to patch from.
33    /// - `&VirtualNode`: The new virtual node to patch to.
34    fn patch_root(&mut self, old_node: &VirtualNode, new_node: &VirtualNode) {
35        let dom_child: Option<Node> = self.get_root().first_child();
36        let is_element: bool = if let Some(ref dom_child) = dom_child {
37            dom_child.dyn_ref::<Element>().is_some()
38        } else {
39            false
40        };
41        if is_element {
42            let element: Element = dom_child.unwrap().dyn_into::<Element>().unwrap();
43            self.patch_node(old_node, new_node, &element);
44        } else if let Some(dom_child) = dom_child {
45            let new_dom: Node = self.create_dom_node(new_node);
46            self.get_root().replace_child(&new_dom, &dom_child).unwrap();
47        } else {
48            let new_dom: Node = self.create_dom_node(new_node);
49            self.get_root().append_child(&new_dom).unwrap();
50        }
51    }
52
53    /// Patches an existing DOM node to match the new virtual node.
54    ///
55    /// # Arguments
56    ///
57    /// - `&VirtualNode`: The old virtual node.
58    /// - `&VirtualNode`: The new virtual node.
59    /// - `&Element`: The real DOM element to patch.
60    fn patch_node(
61        &mut self,
62        old_node: &VirtualNode,
63        new_node: &VirtualNode,
64        dom_element: &Element,
65    ) {
66        match (old_node, new_node) {
67            (VirtualNode::Text(old_text), VirtualNode::Text(new_text)) => {
68                if old_text != new_text {
69                    dom_element.set_text_content(Some(new_text.get_content()));
70                }
71            }
72            (
73                VirtualNode::Element {
74                    tag: old_tag,
75                    attributes: old_attrs,
76                    children: old_children,
77                    key: _old_key,
78                },
79                VirtualNode::Element {
80                    tag: new_tag,
81                    attributes: new_attrs,
82                    children: new_children,
83                    key: _new_key,
84                },
85            ) => {
86                if old_tag != new_tag {
87                    let new_dom: Node = self.create_dom_node(new_node);
88                    if let Some(parent) = dom_element.parent_node() {
89                        self.cleanup_dom_subtree(dom_element);
90                        parent.replace_child(&new_dom, dom_element).unwrap();
91                    }
92                    return;
93                }
94                self.patch_attributes(dom_element, old_attrs, new_attrs);
95                self.patch_children(dom_element, old_children, new_children);
96            }
97            (VirtualNode::Fragment(old_children), VirtualNode::Fragment(new_children)) => {
98                self.patch_children(dom_element, old_children, new_children);
99            }
100            (VirtualNode::Dynamic(_), VirtualNode::Dynamic(_)) => {}
101            _ => {
102                let new_dom: Node = self.create_dom_node(new_node);
103                if let Some(parent) = dom_element.parent_node() {
104                    self.cleanup_dom_subtree(dom_element);
105                    parent.replace_child(&new_dom, dom_element).unwrap();
106                }
107            }
108        }
109    }
110
111    /// Patches attributes of an element, adding, removing, or updating as needed.
112    ///
113    /// # Arguments
114    ///
115    /// - `&Element`: The DOM element whose attributes to patch.
116    /// - `&[AttributeEntry]`: The old attribute list.
117    /// - `&[AttributeEntry]`: The new attribute list.
118    fn patch_attributes(
119        &mut self,
120        element: &Element,
121        old_attrs: &[AttributeEntry],
122        new_attrs: &[AttributeEntry],
123    ) {
124        let old_map: HashMap<&str, &AttributeValue> = old_attrs
125            .iter()
126            .map(|attr: &AttributeEntry| (attr.get_name().as_str(), attr.get_value()))
127            .collect();
128        let new_map: HashMap<&str, ()> = new_attrs
129            .iter()
130            .map(|attr: &AttributeEntry| (attr.get_name().as_str(), ()))
131            .collect();
132        for old_attr in old_attrs {
133            if !new_map.contains_key(old_attr.get_name().as_str()) {
134                if let AttributeValue::Event(_) = old_attr.get_value()
135                    && let Some(euv_id_str) = element.get_attribute(DATA_EUV_ID)
136                    && let Ok(euv_id) = euv_id_str.parse::<usize>()
137                {
138                    cleanup_event_handler(euv_id, old_attr.get_name());
139                }
140                remove_dom_attribute_or_property(element, old_attr.get_name());
141            }
142        }
143        for new_attr in new_attrs {
144            let old_value: Option<&AttributeValue> =
145                old_map.get(new_attr.get_name().as_str()).copied();
146            let should_set: bool = match old_value {
147                Some(old_val) => old_val != new_attr.get_value(),
148                None => true,
149            };
150            if should_set {
151                match new_attr.get_value() {
152                    AttributeValue::Text(value) => {
153                        if value.is_empty() {
154                            remove_dom_attribute_or_property(element, new_attr.get_name());
155                        } else {
156                            set_dom_attribute_or_property(element, new_attr.get_name(), value);
157                        }
158                    }
159                    AttributeValue::Signal(signal) => {
160                        let value: String = signal.get();
161                        if value.is_empty() && !is_boolean_property(new_attr.get_name()) {
162                            remove_dom_attribute_or_property(element, new_attr.get_name());
163                        } else {
164                            set_dom_attribute_or_property(element, new_attr.get_name(), &value);
165                        }
166                    }
167                    AttributeValue::Event(handler) => {
168                        self.attach_event_listener(element, handler);
169                    }
170                    AttributeValue::Dynamic(_) => {}
171                    AttributeValue::Css(css_class) => {
172                        css_class.inject_style();
173                        set_dom_attribute_or_property(
174                            element,
175                            new_attr.get_name(),
176                            css_class.get_name(),
177                        );
178                    }
179                }
180            }
181        }
182    }
183
184    /// Gets a child node at the given index.
185    ///
186    /// # Arguments
187    ///
188    /// - `&Element`: The parent element.
189    /// - `u32`: The child index.
190    ///
191    /// # Returns
192    ///
193    /// - `Option<Node>`: The child node at the given index, if it exists.
194    fn get_child_node(parent: &Element, index: u32) -> Option<Node> {
195        parent.child_nodes().get(index)
196    }
197
198    /// Patches children of an element using a positional diff algorithm.
199    ///
200    /// # Arguments
201    ///
202    /// - `&Element`: The parent DOM element.
203    /// - `&[VirtualNode]`: The old children list.
204    /// - `&[VirtualNode]`: The new children list.
205    fn patch_children(
206        &mut self,
207        parent: &Element,
208        old_children: &[VirtualNode],
209        new_children: &[VirtualNode],
210    ) {
211        let old_len: usize = old_children.len();
212        let new_len: usize = new_children.len();
213        let common_len: usize = old_len.min(new_len);
214        for index in 0..common_len {
215            let old_child: &VirtualNode = &old_children[index];
216            let new_child: &VirtualNode = &new_children[index];
217            if let Some(dom_child) = Self::get_child_node(parent, index as u32) {
218                if let Some(element) = dom_child.dyn_ref::<Element>() {
219                    self.patch_node(old_child, new_child, element);
220                } else if let (VirtualNode::Text(old_text), VirtualNode::Text(new_text)) =
221                    (old_child, new_child)
222                {
223                    if old_text != new_text {
224                        dom_child.set_text_content(Some(new_text.get_content()));
225                    }
226                } else {
227                    let new_dom: Node = self.create_dom_node(new_child);
228                    if let Some(parent_node) = dom_child.parent_node() {
229                        if let Some(child_element) = dom_child.dyn_ref::<Element>() {
230                            self.cleanup_dom_subtree(child_element);
231                        }
232                        let _ = parent_node.replace_child(&new_dom, &dom_child);
233                    }
234                }
235            }
236        }
237        if new_len > old_len {
238            for new_child in new_children.iter().skip(common_len) {
239                let new_dom: Node = self.create_dom_node(new_child);
240                parent.append_child(&new_dom).unwrap();
241            }
242        } else if old_len > new_len {
243            for _ in common_len..old_len {
244                if let Some(last_child) = parent.last_child()
245                    && let Some(element) = last_child.dyn_ref::<Element>()
246                {
247                    self.cleanup_dom_subtree(element);
248                }
249                if let Some(last_child) = parent.last_child() {
250                    parent.remove_child(&last_child).unwrap();
251                }
252            }
253        }
254    }
255
256    /// Creates a real DOM node from a virtual node.
257    ///
258    /// # Arguments
259    ///
260    /// - `&VirtualNode`: The virtual node to materialize.
261    ///
262    /// # Returns
263    ///
264    /// - `Node`: The created DOM node.
265    ///
266    /// # Panics
267    ///
268    /// Panics if `window()` or `document()` is unavailable.
269    fn create_dom_node(&mut self, node: &VirtualNode) -> Node {
270        let document: Document = window().unwrap().document().unwrap();
271        self.create_dom_node_with_document(node, &document)
272    }
273
274    /// Creates a real DOM node using a pre-acquired document reference.
275    ///
276    /// # Arguments
277    ///
278    /// - `&VirtualNode`: The virtual node to materialize.
279    /// - `&Document`: The document reference for creating DOM elements.
280    ///
281    /// # Returns
282    ///
283    /// - `Node`: The created DOM node.
284    fn create_dom_node_with_document(&mut self, node: &VirtualNode, document: &Document) -> Node {
285        match node {
286            VirtualNode::Element {
287                tag,
288                attributes,
289                children,
290                ..
291            } => {
292                let element: Element = match tag {
293                    Tag::Element(name) => document.create_element(name).unwrap(),
294                    Tag::Component(_) => {
295                        let unwrapped: VirtualNode = self.unwrap_component(node);
296                        return self.create_dom_node_with_document(&unwrapped, document);
297                    }
298                };
299                for attr in attributes {
300                    match attr.get_value() {
301                        AttributeValue::Text(value) => {
302                            if !value.is_empty() || is_boolean_property(attr.get_name()) {
303                                set_dom_attribute_or_property(&element, attr.get_name(), value);
304                            }
305                        }
306                        AttributeValue::Signal(signal) => {
307                            let initial_value: String = signal.get();
308                            if !initial_value.is_empty() || is_boolean_property(attr.get_name()) {
309                                set_dom_attribute_or_property(
310                                    &element,
311                                    attr.get_name(),
312                                    &initial_value,
313                                );
314                            }
315                            let signal_addr: usize = signal.get_inner_addr();
316                            let existing_addrs: String = element
317                                .get_attribute("data-euv-signal-addrs")
318                                .unwrap_or_default();
319                            let updated_addrs: String = if existing_addrs.is_empty() {
320                                signal_addr.to_string()
321                            } else {
322                                format!("{},{}", existing_addrs, signal_addr)
323                            };
324                            let _ = element.set_attribute("data-euv-signal-addrs", &updated_addrs);
325                            let attr_name: String = attr.get_name().clone();
326                            let element_clone: Element = element.clone();
327                            let signal_for_sub: Signal<String> = *signal;
328                            let sub_signal: Signal<String> = signal_for_sub;
329                            signal_for_sub.replace_subscribe(move || {
330                                if !is_node_connected(&element_clone) {
331                                    sub_signal.clear_listeners();
332                                    return;
333                                }
334                                let new_value: String = sub_signal.get();
335                                if new_value.is_empty() && !is_boolean_property(&attr_name) {
336                                    remove_dom_attribute_or_property(&element_clone, &attr_name);
337                                } else {
338                                    set_dom_attribute_or_property(
339                                        &element_clone,
340                                        &attr_name,
341                                        &new_value,
342                                    );
343                                }
344                            });
345                        }
346                        AttributeValue::Event(handler) => {
347                            self.attach_event_listener(&element, handler);
348                        }
349                        AttributeValue::Dynamic(_) => {}
350                        AttributeValue::Css(css_class) => {
351                            css_class.inject_style();
352                            set_dom_attribute_or_property(
353                                &element,
354                                attr.get_name(),
355                                css_class.get_name(),
356                            );
357                        }
358                    }
359                }
360                for child in children {
361                    let child_node: Node = self.create_dom_node_with_document(child, document);
362                    element.append_child(&child_node).unwrap();
363                }
364                element.into()
365            }
366            VirtualNode::Text(text_node) => {
367                let text: Text = document.create_text_node(text_node.get_content());
368                if let Some(signal) = text_node.try_get_signal() {
369                    let text_clone: Text = text.clone();
370                    let signal_clone: Signal<String> = *signal;
371                    let sub_signal: Signal<String> = signal_clone;
372                    signal_clone.replace_subscribe({
373                        move || {
374                            if !is_node_connected(&text_clone) {
375                                sub_signal.clear_listeners();
376                                return;
377                            }
378                            let new_value: String = sub_signal.get();
379                            text_clone.set_text_content(Some(&new_value));
380                        }
381                    });
382                }
383                text.into()
384            }
385            VirtualNode::Fragment(children) => {
386                let fragment: Element = document.create_element("slot").unwrap();
387                let _ = fragment.set_attribute("style", "display:contents");
388                for child in children {
389                    let child_node: Node = self.create_dom_node_with_document(child, document);
390                    fragment.append_child(&child_node).unwrap();
391                }
392                fragment.into()
393            }
394            VirtualNode::Dynamic(dynamic_node) => {
395                let placeholder: Element = document.create_element("div").unwrap();
396                let style: &str = "display: contents;";
397                let _ = placeholder.set_attribute("style", style);
398                let dynamic_id: usize = Self::assign_dynamic_id(&placeholder);
399                let initial_dom: Node =
400                    self.setup_dynamic_node(dynamic_node, dynamic_id, &placeholder, true);
401                placeholder.append_child(&initial_dom).unwrap();
402                placeholder.into()
403            }
404            VirtualNode::Empty => document.create_text_node("").into(),
405        }
406    }
407
408    /// Initializes a DynamicNode: runs the initial render, creates a sub-renderer,
409    /// and registers the re-render callback in the signal update registry.
410    ///
411    /// # Arguments
412    ///
413    /// - `&DynamicNode`: The dynamic node to set up.
414    /// - `usize`: The unique dynamic ID assigned to the placeholder.
415    /// - `&Element`: The placeholder DOM element.
416    /// - `bool`: Whether to skip rendering if the output is unchanged.
417    ///
418    /// # Returns
419    ///
420    /// - `Node`: The initial rendered DOM node.
421    fn setup_dynamic_node(
422        &mut self,
423        dynamic_node: &DynamicNode,
424        dynamic_id: usize,
425        placeholder: &Element,
426        skip_equal: bool,
427    ) -> Node {
428        let mut hook_context: HookContext = dynamic_node.get_hook_context_value();
429        hook_context.reset_hook_index();
430        let initial_vnode: VirtualNode =
431            with_hook_context(hook_context.clone(), || dynamic_node.render());
432        let initial_unwrapped: VirtualNode = self.unwrap_component(&initial_vnode);
433        let initial_dom: Node = self.create_dom_node(&initial_unwrapped);
434        let render_fn: Rc<RefCell<RenderFnInner>> = dynamic_node.get_render_fn().clone();
435        let placeholder_clone: Element = placeholder.clone();
436        let mut renderer_for_sub: Renderer = Renderer::new(placeholder_clone.clone());
437        renderer_for_sub.set_current_tree(Some(initial_unwrapped));
438        let renderer_rc: Rc<RefCell<Renderer>> = Rc::new(RefCell::new(renderer_for_sub));
439        let callback: Box<dyn FnMut()> = Box::new(move || {
440            if placeholder_clone.parent_node().is_none() {
441                return;
442            }
443            hook_context.reset_hook_index();
444            let new_vnode: VirtualNode = with_hook_context(hook_context.clone(), || {
445                let mut inner: std::cell::RefMut<RenderFnInner> = render_fn.borrow_mut();
446                (inner.get_mut_render_fn())()
447            });
448            if skip_equal {
449                let renderer_ref: std::cell::Ref<Renderer> = renderer_rc.borrow();
450                if let Some(old_vnode) = renderer_ref.try_get_current_tree() {
451                    let new_unwrapped: VirtualNode = Renderer::unwrap_component_static(&new_vnode);
452                    if old_vnode == &new_unwrapped {
453                        return;
454                    }
455                }
456            }
457            let mut renderer_mut: std::cell::RefMut<Renderer> = renderer_rc.borrow_mut();
458            renderer_mut.render(new_vnode);
459        });
460        register_dynamic_listener(dynamic_id, callback);
461        initial_dom
462    }
463
464    /// Recursively unwraps component nodes into their rendered output.
465    ///
466    /// # Arguments
467    ///
468    /// - `&VirtualNode`: The virtual node to unwrap.
469    ///
470    /// # Returns
471    ///
472    /// - `VirtualNode`: The unwrapped virtual node with all components expanded.
473    fn unwrap_component(&self, node: &VirtualNode) -> VirtualNode {
474        match node {
475            VirtualNode::Element {
476                tag: Tag::Component(_),
477                children,
478                ..
479            } => {
480                if children.len() == 1 {
481                    self.unwrap_component(&children[0])
482                } else {
483                    VirtualNode::Fragment(children.clone())
484                }
485            }
486            VirtualNode::Element {
487                tag,
488                attributes,
489                children,
490                key,
491            } => {
492                if !children.iter().any(Self::subtree_has_component) {
493                    return node.clone();
494                }
495                let unwrapped_children: Vec<VirtualNode> = children
496                    .iter()
497                    .map(|child| self.unwrap_component(child))
498                    .collect();
499                VirtualNode::Element {
500                    tag: tag.clone(),
501                    attributes: attributes.clone(),
502                    children: unwrapped_children,
503                    key: key.clone(),
504                }
505            }
506            VirtualNode::Fragment(children) => {
507                if !children.iter().any(Self::subtree_has_component) {
508                    return node.clone();
509                }
510                let unwrapped_children: Vec<VirtualNode> = children
511                    .iter()
512                    .map(|child| self.unwrap_component(child))
513                    .collect();
514                VirtualNode::Fragment(unwrapped_children)
515            }
516            other => other.clone(),
517        }
518    }
519
520    /// Static version of `unwrap_component`.
521    ///
522    /// # Arguments
523    ///
524    /// - `&VirtualNode`: The virtual node to unwrap.
525    ///
526    /// # Returns
527    ///
528    /// - `VirtualNode`: The unwrapped virtual node with all components expanded.
529    fn unwrap_component_static(node: &VirtualNode) -> VirtualNode {
530        match node {
531            VirtualNode::Element {
532                tag: Tag::Component(_),
533                children,
534                ..
535            } => {
536                if children.len() == 1 {
537                    Self::unwrap_component_static(&children[0])
538                } else {
539                    VirtualNode::Fragment(children.clone())
540                }
541            }
542            VirtualNode::Element {
543                tag,
544                attributes,
545                children,
546                key,
547            } => {
548                if !children.iter().any(Self::subtree_has_component) {
549                    return node.clone();
550                }
551                let unwrapped_children: Vec<VirtualNode> =
552                    children.iter().map(Self::unwrap_component_static).collect();
553                VirtualNode::Element {
554                    tag: tag.clone(),
555                    attributes: attributes.clone(),
556                    children: unwrapped_children,
557                    key: key.clone(),
558                }
559            }
560            VirtualNode::Fragment(children) => {
561                if !children.iter().any(Self::subtree_has_component) {
562                    return node.clone();
563                }
564                let unwrapped_children: Vec<VirtualNode> =
565                    children.iter().map(Self::unwrap_component_static).collect();
566                VirtualNode::Fragment(unwrapped_children)
567            }
568            other => other.clone(),
569        }
570    }
571
572    /// Returns `true` if the given subtree contains any `Tag::Component` nodes.
573    ///
574    /// # Arguments
575    ///
576    /// - `&VirtualNode`: The virtual node to check.
577    ///
578    /// # Returns
579    ///
580    /// - `bool`: `true` if the subtree contains a component node.
581    fn subtree_has_component(node: &VirtualNode) -> bool {
582        match node {
583            VirtualNode::Element {
584                tag: Tag::Component(_),
585                ..
586            } => true,
587            VirtualNode::Element { children, .. } => {
588                children.iter().any(Self::subtree_has_component)
589            }
590            VirtualNode::Fragment(children) => children.iter().any(Self::subtree_has_component),
591            _ => false,
592        }
593    }
594
595    /// Assigns a new `data-euv-dynamic-id` to a newly created DynamicNode placeholder.
596    ///
597    /// # Arguments
598    ///
599    /// - `&Element`: The placeholder DOM element.
600    ///
601    /// # Returns
602    ///
603    /// - `usize`: The assigned dynamic ID.
604    fn assign_dynamic_id(placeholder: &Element) -> usize {
605        let dynamic_id: usize = NEXT_EUV_DYNAMIC_ID.fetch_add(1, Ordering::Relaxed);
606        let _ = placeholder.set_attribute("data-euv-dynamic-id", &dynamic_id.to_string());
607        dynamic_id
608    }
609
610    /// Recursively cleans up framework resources associated with a DOM subtree.
611    ///
612    /// Removes event handlers, dynamic node listeners, and signal listeners
613    /// for the given element and all of its descendants.
614    ///
615    /// # Arguments
616    ///
617    /// - `&Element`: The DOM element to clean up.
618    fn cleanup_dom_subtree(&self, element: &Element) {
619        if let Some(euv_id_str) = element.get_attribute(DATA_EUV_ID)
620            && let Ok(euv_id) = euv_id_str.parse::<usize>()
621        {
622            cleanup_element_handlers(euv_id);
623        }
624        if let Some(dynamic_id_str) = element.get_attribute("data-euv-dynamic-id")
625            && let Ok(dynamic_id) = dynamic_id_str.parse::<usize>()
626        {
627            cleanup_dynamic_node(dynamic_id);
628        }
629        if let Some(signal_addrs_str) = element.get_attribute("data-euv-signal-addrs") {
630            for addr_str in signal_addrs_str.split(',') {
631                if let Ok(addr) = addr_str.parse::<usize>() {
632                    clear_signal_listeners_by_addr(addr);
633                }
634            }
635        }
636        let child_nodes: NodeList = element.child_nodes();
637        let length: u32 = child_nodes.length();
638        for i in 0..length {
639            if let Some(child) = child_nodes.get(i) {
640                if let Some(child_element) = child.dyn_ref::<Element>() {
641                    self.cleanup_dom_subtree(child_element);
642                } else if let Some(text) = child.dyn_ref::<Text>() {
643                    cleanup_text_signal_listeners(text);
644                }
645            }
646        }
647    }
648
649    /// Registers an event handler for a DOM element using global event delegation.
650    ///
651    /// # Arguments
652    ///
653    /// - `&Element`: The DOM element to attach the handler to.
654    /// - `&NativeEventHandler`: The event handler to register.
655    fn attach_event_listener(&self, element: &Element, handler: &NativeEventHandler) {
656        let euv_id: usize = match element.get_attribute(DATA_EUV_ID) {
657            Some(id_str) => id_str.parse::<usize>().unwrap_or_else(|_| {
658                let new_id: usize = NEXT_EUV_ID.fetch_add(1, Ordering::Relaxed);
659                let _ = element.set_attribute(DATA_EUV_ID, &new_id.to_string());
660                new_id
661            }),
662            None => {
663                let new_id: usize = NEXT_EUV_ID.fetch_add(1, Ordering::Relaxed);
664                let _ = element.set_attribute(DATA_EUV_ID, &new_id.to_string());
665                new_id
666            }
667        };
668        let event_name: Cow<'static, str> = handler.get_event_name().clone();
669        if !NativeEventName::DELEGATABLE_EVENT_NAMES.contains(&&*event_name) {
670            ensure_delegated_listener(event_name.clone());
671        }
672        let key: (usize, Cow<'static, str>) = (euv_id, event_name);
673        let registry_ref: &mut HashMap<(usize, Cow<'static, str>), HandlerEntry> =
674            ensure_handler_registry_mut();
675        if let Some(existing_entry) = registry_ref.get(&key) {
676            let mut slot: std::cell::RefMut<HandlerSlot> = existing_entry.borrow_mut();
677            slot.set_handler(Some(handler.clone()));
678        } else {
679            let handler_slot: HandlerEntry =
680                Rc::new(RefCell::new(HandlerSlot::new(Some(handler.clone()))));
681            registry_ref.insert(key, handler_slot);
682        }
683    }
684}