on bd2c84c help sign in
dodrio/src/node.rs
260 lines 7.3 kB history raw
1use crate::{cached_set::CacheId, RootRender, VdomWeak};
2use bumpalo::Bump;
3use std::fmt;
4use std::iter;
5use std::mem;
6use std::u32;
8/// A virtual DOM node.
9#[derive(Debug, Clone)]
10pub struct Node<'a> {
11 #[cfg(feature = "xxx-unstable-internal-use-only")]
12 #[doc(hidden)]
13 pub kind: NodeKind<'a>,
15 #[cfg(not(feature = "xxx-unstable-internal-use-only"))]
16 pub(crate) kind: NodeKind<'a>,
19pub_unstable_internal! {
20 /// A node is either a text node or an element.
21 #[derive(Debug, Clone)]
22 pub(crate) enum NodeKind<'a> {
23 /// A text node.
24 Text(TextNode<'a>),
26 /// An element potentially with attributes and children.
27 Element(&'a ElementNode<'a>),
29 /// A node in the vdom's `CachedSet`. This allows us to avoid
30 /// re-rendering and re-diffing subtrees.
31 Cached(CachedNode),
32 }
35pub_unstable_internal! {
36 /// Text nodes are just a string of text. They cannot have attributes or
37 /// children.
38 #[derive(Debug, Clone)]
39 pub(crate) struct TextNode<'a> {
40 pub text: &'a str,
41 }
44pub_unstable_internal! {
45 /// Elements have a tag name, zero or more attributes, and zero or more
46 /// children.
47 #[derive(Debug, Clone)]
48 pub(crate) struct ElementNode<'a> {
49 pub key: NodeKey,
50 pub tag_name: &'a str,
51 pub listeners: &'a [Listener<'a>],
52 pub attributes: &'a [Attribute<'a>],
53 pub children: &'a [Node<'a>],
54 pub namespace: Option<&'a str>,
55 }
58pub_unstable_internal! {
59 /// A cached node exists in an arena that is internal to the `Vdom`. It
60 /// allows us to avoid both re-rendering a sub-tree and re-diffing
61 /// it. `CachedNode`s cannot contain cycles, since they can only contain
62 /// other `CachedNode`s that already exist when they are being constructed.
63 #[derive(Debug, Clone, Copy)]
64 pub(crate) struct CachedNode {
65 pub id: CacheId,
66 // Always a copy of the inner cached node's key.
67 pub key: NodeKey,
68 }
71pub_unstable_internal! {
72 /// The key for keyed children.
73 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
74 pub(crate) struct NodeKey(pub(crate) u32);
77impl NodeKey {
78 pub const NONE: NodeKey = NodeKey(u32::MAX);
80 #[inline]
81 pub fn is_none(&self) -> bool {
82 *self == Self::NONE
83 }
85 #[inline]
86 pub fn is_some(&self) -> bool {
87 !self.is_none()
88 }
91/// An event listener callback function.
92///
93/// It takes three parameters:
94///
95/// 1. The virtual DOM's root rendering component.
96/// 2. A capability to scheduler virtual DOM re-rendering.
97/// 3. The event that occurred.
98pub(crate) type ListenerCallback<'a> =
99 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
101/// An event listener.
102pub struct Listener<'a> {
103 /// The type of event to listen for.
104 pub(crate) event: &'a str,
105 /// The callback to invoke when the event happens.
106 pub(crate) callback: ListenerCallback<'a>,
109/// An attribute on a DOM node, such as `id="my-thing"` or
110/// `href="https://example.com"`.
111#[derive(Clone, Debug)]
112pub struct Attribute<'a> {
113 pub(crate) name: &'a str,
114 pub(crate) value: &'a str,
117impl<'a> From<CachedNode> for Node<'a> {
118 #[inline]
119 fn from(c: CachedNode) -> Self {
120 Node {
121 kind: NodeKind::Cached(c),
122 }
123 }
126impl fmt::Debug for Listener<'_> {
127 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
128 let (a, b) = self.get_callback_parts();
129 let a = a as *mut u32;
130 let b = b as *mut u32;
131 f.debug_struct("Listener")
132 .field("event", &self.event)
133 .field("callback", &(a, b))
134 .finish()
135 }
138impl<'a> Attribute<'a> {
139 /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
140 #[inline]
141 pub fn name(&self) -> &'a str {
142 self.name
143 }
145 /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
146 #[inline]
147 pub fn value(&self) -> &'a str {
148 self.value
149 }
151 /// Certain attributes are considered "volatile" and can change via user
152 /// input that we can't see when diffing against the old virtual DOM. For
153 /// these attributes, we want to always re-set the attribute on the physical
154 /// DOM node, even if the old and new virtual DOM nodes have the same value.
155 #[inline]
156 pub(crate) fn is_volatile(&self) -> bool {
157 match self.name {
158 "value" | "checked" | "selected" => true,
159 _ => false,
160 }
161 }
164impl<'a> Node<'a> {
165 /// Construct a new Node of type element with given tag name and children
166 #[inline]
167 pub(crate) fn element<Listeners, Attributes, Children>(
168 bump: &'a Bump,
169 key: NodeKey,
170 tag_name: &'a str,
171 listeners: Listeners,
172 attributes: Attributes,
173 children: Children,
174 namespace: Option<&'a str>,
175 ) -> Node<'a>
176 where
177 Listeners: 'a + AsRef<[Listener<'a>]>,
178 Attributes: 'a + AsRef<[Attribute<'a>]>,
179 Children: 'a + AsRef<[Node<'a>]>,
180 {
181 let children: &'a Children = bump.alloc(children);
182 let children: &'a [Node<'a>] = children.as_ref();
184 let listeners: &'a Listeners = bump.alloc(listeners);
185 let listeners: &'a [Listener<'a>] = listeners.as_ref();
187 let attributes: &'a Attributes = bump.alloc(attributes);
188 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
190 let element = bump.alloc_with(|| ElementNode {
191 key,
192 tag_name,
193 listeners,
194 attributes,
195 children,
196 namespace,
197 });
199 Node {
200 kind: NodeKind::Element(element),
201 }
202 }
204 /// Construct a new text node with the given text.
205 #[inline]
206 pub(crate) fn text(text: &'a str) -> Node<'a> {
207 Node {
208 kind: NodeKind::Text(TextNode { text }),
209 }
210 }
212 #[inline]
213 pub(crate) fn key(&self) -> NodeKey {
214 match &self.kind {
215 NodeKind::Text(_) => NodeKey::NONE,
216 NodeKind::Element(e) => e.key,
217 NodeKind::Cached(c) => c.key,
218 }
219 }
222/// A node can become an iterator that yields the node itself once.
223///
224/// This implementation of `IntoIterator` mostly exists to improve the
225/// `typed-html` ergonomics, where the macro invokes `.into_iter()` on the child
226/// contents of a tag. By implementing `IntoIterator` here, we avoid having to
227/// do nasty shenanigans like `<p>vec![$contents]</p>` instead of plain old
228/// `<p>$contents</p>`.
229impl<'a> IntoIterator for Node<'a> {
230 type Item = Node<'a>;
231 type IntoIter = iter::Once<Self::Item>;
233 fn into_iter(self) -> Self::IntoIter {
234 iter::once(self)
235 }
238union CallbackFatPtr<'a> {
239 callback: ListenerCallback<'a>,
240 parts: (u32, u32),
243impl Listener<'_> {
244 #[inline]
245 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
246 assert_eq!(
247 mem::size_of::<ListenerCallback>(),
248 mem::size_of::<CallbackFatPtr>()
249 );
251 unsafe {
252 let fat = CallbackFatPtr {
253 callback: self.callback,
254 };
255 let (a, b) = fat.parts;
256 debug_assert!(a != 0);
257 (a, b)
258 }
259 }