on ad5f96e help sign in
dodrio/src/node.rs
289 lines 8.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(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 }
44mod flags {
45 bitflags::bitflags! {
46 #[derive(Default)]
47 pub struct ElementNodeFlags: u32 {
48 const HAS_KEYED_CHILDREN = 1 << 31;
49 const KEY_MASK = !(Self::HAS_KEYED_CHILDREN).bits;
50 }
51 }
53 impl ElementNodeFlags {
54 #[inline]
55 pub fn has_keyed_children(self) -> bool {
56 self.contains(ElementNodeFlags::HAS_KEYED_CHILDREN)
57 }
59 #[inline]
60 pub fn key(self) -> u32 {
61 (self & ElementNodeFlags::KEY_MASK).bits
62 }
64 #[inline]
65 pub fn set_key(&mut self, key: u32) {
66 debug_assert_eq!(key & (!ElementNodeFlags::KEY_MASK).bits, 0);
67 self.bits |= key;
68 }
69 }
72cfg_if::cfg_if! {
73 if #[cfg(feature = "xxx-unstable-internal-use-only")] {
74 #[doc(hidden)]
75 pub use self::flags::ElementNodeFlags;
76 } else {
77 pub(crate) use self::flags::ElementNodeFlags;
78 }
81pub_unstable_internal! {
82 /// Elements have a tag name, zero or more attributes, and zero or more
83 /// children.
84 #[derive(Debug, Clone)]
85 pub(crate) struct ElementNode<'a> {
86 pub flags: ElementNodeFlags,
87 pub tag_name: &'a str,
88 pub listeners: &'a [Listener<'a>],
89 pub attributes: &'a [Attribute<'a>],
90 pub children: &'a [Node<'a>],
91 pub namespace: Option<&'a str>,
92 }
95pub_unstable_internal! {
96 /// A cached node exists in an arena that is internal to the `Vdom`. It
97 /// allows us to avoid both re-rendering a sub-tree and re-diffing
98 /// it. `CachedNode`s cannot contain cycles, since they can only contain
99 /// other `CachedNode`s that already exist when they are being constructed.
100 #[derive(Debug, Clone, Copy)]
101 pub(crate) struct CachedNode {
102 pub id: CacheId,
103 // Always a copy of the inner cached node's flags when the inner node
104 // has flags.
105 pub flags: Option<ElementNodeFlags>,
106 }
109/// The key for keyed children.
110#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
111pub(crate) struct NodeKey(u32);
113/// An event listener callback function.
114///
115/// It takes three parameters:
116///
117/// 1. The virtual DOM's root rendering component.
118/// 2. A capability to scheduler virtual DOM re-rendering.
119/// 3. The event that occurred.
120pub(crate) type ListenerCallback<'a> =
121 &'a (dyn Fn(&mut dyn RootRender, VdomWeak, web_sys::Event) + 'static);
123/// An event listener.
124pub struct Listener<'a> {
125 /// The type of event to listen for.
126 pub(crate) event: &'a str,
127 /// The callback to invoke when the event happens.
128 pub(crate) callback: ListenerCallback<'a>,
131/// An attribute on a DOM node, such as `id="my-thing"` or
132/// `href="https://example.com"`.
133#[derive(Clone, Debug)]
134pub struct Attribute<'a> {
135 pub(crate) name: &'a str,
136 pub(crate) value: &'a str,
139impl<'a> From<CachedNode> for Node<'a> {
140 #[inline]
141 fn from(c: CachedNode) -> Self {
142 Node {
143 kind: NodeKind::Cached(c),
144 }
145 }
148impl fmt::Debug for Listener<'_> {
149 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150 let (a, b) = self.get_callback_parts();
151 let a = a as *mut u32;
152 let b = b as *mut u32;
153 f.debug_struct("Listener")
154 .field("event", &self.event)
155 .field("callback", &(a, b))
156 .finish()
157 }
160impl<'a> Attribute<'a> {
161 /// Get this attribute's name, such as `"id"` in `<div id="my-thing" />`.
162 #[inline]
163 pub fn name(&self) -> &'a str {
164 self.name
165 }
167 /// The attribute value, such as `"my-thing"` in `<div id="my-thing" />`.
168 #[inline]
169 pub fn value(&self) -> &'a str {
170 self.value
171 }
173 /// Certain attributes are considered "volatile" and can change via user
174 /// input that we can't see when diffing against the old virtual DOM. For
175 /// these attributes, we want to always re-set the attribute on the physical
176 /// DOM node, even if the old and new virtual DOM nodes have the same value.
177 #[inline]
178 pub(crate) fn is_volatile(&self) -> bool {
179 match self.name {
180 "value" | "checked" | "selected" => true,
181 _ => false,
182 }
183 }
186impl<'a> Node<'a> {
187 /// Construct a new Node of type element with given tag name and children
188 #[inline]
189 pub(crate) fn element<Listeners, Attributes, Children>(
190 bump: &'a Bump,
191 flags: ElementNodeFlags,
192 tag_name: &'a str,
193 listeners: Listeners,
194 attributes: Attributes,
195 children: Children,
196 namespace: Option<&'a str>,
197 ) -> Node<'a>
198 where
199 Listeners: 'a + AsRef<[Listener<'a>]>,
200 Attributes: 'a + AsRef<[Attribute<'a>]>,
201 Children: 'a + AsRef<[Node<'a>]>,
202 {
203 let children: &'a Children = bump.alloc(children);
204 let children: &'a [Node<'a>] = children.as_ref();
206 let listeners: &'a Listeners = bump.alloc(listeners);
207 let listeners: &'a [Listener<'a>] = listeners.as_ref();
209 let attributes: &'a Attributes = bump.alloc(attributes);
210 let attributes: &'a [Attribute<'a>] = attributes.as_ref();
212 Node {
213 kind: NodeKind::Element(ElementNode {
214 flags,
215 tag_name,
216 listeners,
217 attributes,
218 children,
219 namespace,
220 }),
221 }
222 }
224 /// Construct a new text node with the given text.
225 #[inline]
226 pub(crate) fn text(text: &'a str) -> Node<'a> {
227 Node {
228 kind: NodeKind::Text(TextNode { text }),
229 }
230 }
232 #[inline]
233 pub(crate) fn flags(&self) -> Option<ElementNodeFlags> {
234 match &self.kind {
235 NodeKind::Text(_) => None,
236 NodeKind::Element(e) => Some(e.flags),
237 NodeKind::Cached(c) => c.flags,
238 }
239 }
241 #[inline]
242 pub(crate) fn key(&self) -> NodeKey {
243 match &self.kind {
244 NodeKind::Text(_) => NodeKey(u32::MAX),
245 NodeKind::Element(e) => NodeKey(e.flags.key()),
246 NodeKind::Cached(c) => NodeKey(c.flags.map_or(u32::MAX, |f| f.key())),
247 }
248 }
251/// A node can become an iterator that yields the node itself once.
252///
253/// This implementation of `IntoIterator` mostly exists to improve the
254/// `typed-html` ergonomics, where the macro invokes `.into_iter()` on the child
255/// contents of a tag. By implementing `IntoIterator` here, we avoid having to
256/// do nasty shenanigans like `<p>vec![$contents]</p>` instead of plain old
257/// `<p>$contents</p>`.
258impl<'a> IntoIterator for Node<'a> {
259 type Item = Node<'a>;
260 type IntoIter = iter::Once<Self::Item>;
262 fn into_iter(self) -> Self::IntoIter {
263 iter::once(self)
264 }
267union CallbackFatPtr<'a> {
268 callback: ListenerCallback<'a>,
269 parts: (u32, u32),
272impl Listener<'_> {
273 #[inline]
274 pub(crate) fn get_callback_parts(&self) -> (u32, u32) {
275 assert_eq!(
276 mem::size_of::<ListenerCallback>(),
277 mem::size_of::<CallbackFatPtr>()
278 );
280 unsafe {
281 let fat = CallbackFatPtr {
282 callback: self.callback,
283 };
284 let (a, b) = fat.parts;
285 debug_assert!(a != 0);
286 (a, b)
287 }
288 }