euv_core/noderef/impl.rs
1use super::*;
2
3impl<T: ?Sized> NodeRef<T> {
4 /// Returns an owned clone of the shared interior cell.
5 ///
6 /// # Returns
7 ///
8 /// - `Rc<UnsafeCell<Option<JsValue>>>`: The shared interior cell.
9 fn get_inner(&self) -> Rc<UnsafeCell<Option<JsValue>>> {
10 self.inner.clone()
11 }
12
13 /// Returns a shared reference to the interior cell.
14 ///
15 /// # Returns
16 ///
17 /// - `&Rc<UnsafeCell<Option<JsValue>>>`: The interior cell reference.
18 fn get_inner_ref(&self) -> &Rc<UnsafeCell<Option<JsValue>>> {
19 &self.inner
20 }
21
22 /// Creates a new empty `NodeRef`.
23 ///
24 /// This constructor is `pub` so that `Default::default()` and
25 /// `App::use_node_ref()` can both produce handles. Callers should not
26 /// normally need to invoke this directly — use [`App::use_node_ref`]
27 /// inside a component so the handle participates in the hook order.
28 pub fn new() -> Self {
29 Self::default()
30 }
31
32 /// Returns a clone of the raw `JsValue` if an element is currently
33 /// attached, otherwise `None`.
34 ///
35 /// Use this when you only need the underlying DOM element without
36 /// caring about its concrete type (e.g., passing it to a third-party
37 /// JS interop function). For type-safe access, use [`get_cloned`].
38 ///
39 /// [`get_cloned`]: NodeRef::get_cloned
40 ///
41 /// # Returns
42 ///
43 /// - `Option<JsValue>` - The current value (or a snapshot thereof).
44 pub fn get(&self) -> Option<JsValue> {
45 // SAFETY: we never hand out `&mut Option<JsValue>`; the only mutating
46 // access goes through `set` / `clear`, both of which `take` the
47 // existing value first, so there is no aliasing on the inner `JsValue`.
48 let cell: *mut Option<JsValue> = self.get_inner_ref().get();
49 unsafe { (*cell).as_ref().cloned() }
50 }
51
52 /// Returns a clone of the attached element cast to `T`, or `None` if
53 /// no element is attached or the cast fails.
54 ///
55 /// The cast uses [`JsCast::dyn_into`] and discards the `Err` arm — a
56 /// failed cast is reported as `None` rather than panicking, which
57 /// matches React/Yew behaviour and avoids crashing the renderer on
58 /// ref misuse.
59 ///
60 /// # Returns
61 ///
62 /// - `Option<T>` - A cloned copy of the inner value, if present.
63 pub fn get_cloned(&self) -> Option<T>
64 where
65 T: JsCast,
66 {
67 let value: JsValue = self.get()?;
68 value.dyn_into::<T>().ok()
69 }
70
71 /// Stores the given element as the current value of the handle.
72 ///
73 /// This is called by the renderer after a `ref:` attribute fires;
74 /// users should not normally need to call it directly. Setting the
75 /// value clears any previous element first — multiple mounts of the
76 /// same `NodeRef` therefore always reflect the most recent element.
77 ///
78 /// # Arguments
79 ///
80 /// - `JsValue` - A `JsValue` parameter.
81 pub fn set(&self, value: JsValue) {
82 // SAFETY: `set` replaces the inner value wholesale via `replace`
83 // (which uses `mem::swap` under the hood), so we never hold an
84 // overlapping reference. The previous `JsValue` is dropped before
85 // the new one is stored.
86 let cell: *mut Option<JsValue> = self.get_inner_ref().get();
87 unsafe {
88 let _: Option<JsValue> = (*cell).replace(value);
89 }
90 }
91
92 /// Returns a shared clone of the interior cell for registry wiring.
93 ///
94 /// The renderer calls this when a `ref:` attribute fires so the
95 /// `NodeRef`'s cell can be registered under the element's `euv_id`
96 /// and cleared by `cleanup_subtree` when the element is unmounted.
97 ///
98 /// # Returns
99 ///
100 /// - `NodeRefEntry` - A clone of the shared interior cell.
101 pub(crate) fn share_cell(&self) -> NodeRefEntry {
102 self.get_inner()
103 }
104
105 /// Clears the currently attached element, if any.
106 ///
107 /// Called by the renderer when a node is unmounted. After `clear`,
108 /// [`get`] and [`get_cloned`] both return `None` until the next
109 /// `set` call.
110 ///
111 /// [`get`]: NodeRef::get
112 pub fn clear(&self) {
113 let cell: *mut Option<JsValue> = self.get_inner_ref().get();
114 unsafe {
115 let _: Option<JsValue> = (*cell).take();
116 }
117 }
118
119 /// Returns `true` if an element is currently attached to this handle.
120 ///
121 /// # Returns
122 ///
123 /// - `bool` - `true` when the value has been initialised.
124 pub fn is_set(&self) -> bool {
125 let cell: *const Option<JsValue> = self.get_inner_ref().get();
126 // SAFETY: only `is_some()` is called — no `&mut`, no mutation.
127 unsafe { (*cell).is_some() }
128 }
129}
130
131// Blanket impl over the unsized `web_sys::Node` is what most users want,
132// but the macro passes a `JsValue` and the user chooses `T` per use site,
133// so we don't constrain `T` here — `get_cloned`'s `JsCast` bound is the
134// single point where the type check happens.
135//
136// `NodeRef<dyn Any>` (or any unsized type) is accepted by the type system
137// because `T: ?Sized`. The internal `JsValue` storage is independent of
138// `T` so there is no soundness concern.
139
140/// Manual `Clone` impl: `T` is `?Sized` so the `derive` macro (which
141/// requires `T: Clone`) cannot be used. `Rc` clone is cheap and shares
142/// the underlying cell with all clones.
143impl<T: ?Sized> Clone for NodeRef<T> {
144 /// Clones the [`NodeRef`] by reusing shared, cheap-to-clone state where possible.
145 fn clone(&self) -> Self {
146 Self {
147 inner: self.get_inner(),
148 _marker: PhantomData,
149 }
150 }
151}
152
153impl<T: ?Sized> Default for NodeRef<T> {
154 /// Returns an empty `NodeRef` (no element associated).
155 ///
156 /// The returned handle is independent of any hook context: it is not
157 /// registered as a hook and will never be populated by the renderer
158 /// unless it is the same instance that was returned by
159 /// [`App::use_node_ref`] and then later attached via a `ref:` attribute.
160 /// Prefer [`App::use_node_ref`] inside a component for normal usage.
161 fn default() -> Self {
162 Self {
163 inner: Rc::new(UnsafeCell::new(None)),
164 _marker: PhantomData,
165 }
166 }
167}
168
169impl<T: ?Sized> Debug for NodeRef<T> {
170 /// Formats the [`NodeRef`] via the supplied formatter.
171 ///
172 /// # Arguments
173 ///
174 /// - `&mut Formatter<'_>` - The formatter receiving the formatted output.
175 ///
176 /// # Returns
177 ///
178 /// - `fmt::Result` - Result of the formatting operation.
179 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
180 formatter
181 .debug_struct("NodeRef")
182 .field("is_set", &self.is_set())
183 .finish()
184 }
185}