nodes.rs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. //! Virtual Node Support
  2. //! --------------------
  3. //! VNodes represent lazily-constructed VDom trees that support diffing and event handlers.
  4. //!
  5. //! These VNodes should be *very* cheap and *very* fast to construct - building a full tree should be insanely quick.
  6. use crate::{
  7. events::SyntheticEvent,
  8. innerlude::{empty_cell, Context, DomTree, ElementId, Properties, Scope, ScopeId, FC},
  9. SuspendedContext,
  10. };
  11. use bumpalo::{boxed::Box as BumpBox, Bump};
  12. use std::{
  13. cell::{Cell, RefCell},
  14. fmt::{Arguments, Debug, Formatter},
  15. marker::PhantomData,
  16. mem::ManuallyDrop,
  17. rc::Rc,
  18. };
  19. /// Tools for the base unit of the virtual dom - the VNode
  20. /// VNodes are intended to be quickly-allocated, lightweight enum values.
  21. ///
  22. /// Components will be generating a lot of these very quickly, so we want to
  23. /// limit the amount of heap allocations / overly large enum sizes.
  24. pub enum VNode<'src> {
  25. Text(VText<'src>),
  26. Element(&'src VElement<'src>),
  27. Fragment(VFragment<'src>),
  28. Component(&'src VComponent<'src>),
  29. Suspended(&'src VSuspended<'src>),
  30. Anchor(VAnchor),
  31. }
  32. impl<'src> VNode<'src> {
  33. pub fn key(&self) -> Option<&'src str> {
  34. match &self {
  35. VNode::Element(el) => el.key,
  36. VNode::Component(c) => c.key,
  37. VNode::Fragment(f) => f.key,
  38. VNode::Text(_t) => None,
  39. VNode::Suspended(s) => None,
  40. VNode::Anchor(f) => None,
  41. }
  42. }
  43. pub fn direct_id(&self) -> ElementId {
  44. self.try_direct_id().unwrap()
  45. }
  46. pub fn try_direct_id(&self) -> Option<ElementId> {
  47. match &self {
  48. VNode::Text(el) => el.dom_id.get(),
  49. VNode::Element(el) => el.dom_id.get(),
  50. VNode::Anchor(el) => el.dom_id.get(),
  51. VNode::Fragment(_) => None,
  52. VNode::Component(_) => None,
  53. VNode::Suspended(_) => None,
  54. }
  55. }
  56. }
  57. pub struct VAnchor {
  58. pub dom_id: Cell<Option<ElementId>>,
  59. }
  60. pub struct VText<'src> {
  61. pub text: &'src str,
  62. pub dom_id: Cell<Option<ElementId>>,
  63. pub is_static: bool,
  64. }
  65. pub struct VFragment<'src> {
  66. pub children: &'src [VNode<'src>],
  67. pub key: Option<&'src str>,
  68. pub is_static: bool,
  69. }
  70. pub trait DioxusElement {
  71. const TAG_NAME: &'static str;
  72. const NAME_SPACE: Option<&'static str>;
  73. #[inline]
  74. fn tag_name(&self) -> &'static str {
  75. Self::TAG_NAME
  76. }
  77. #[inline]
  78. fn namespace(&self) -> Option<&'static str> {
  79. Self::NAME_SPACE
  80. }
  81. }
  82. pub struct VElement<'a> {
  83. pub key: Option<&'a str>,
  84. // tag is always static
  85. pub tag_name: &'static str,
  86. pub namespace: Option<&'static str>,
  87. pub dom_id: Cell<Option<ElementId>>,
  88. pub listeners: &'a [Listener<'a>],
  89. pub attributes: &'a [Attribute<'a>],
  90. pub children: &'a [VNode<'a>],
  91. }
  92. /// An attribute on a DOM node, such as `id="my-thing"` or
  93. /// `href="https://example.com"`.
  94. #[derive(Clone, Debug)]
  95. pub struct Attribute<'a> {
  96. pub name: &'static str,
  97. pub value: &'a str,
  98. pub is_static: bool,
  99. pub is_volatile: bool,
  100. // Doesn't exist in the html spec, mostly used to denote "style" tags - could be for any type of group
  101. pub namespace: Option<&'static str>,
  102. }
  103. /// An event listener.
  104. /// IE onclick, onkeydown, etc
  105. pub struct Listener<'bump> {
  106. /// The type of event to listen for.
  107. pub(crate) event: &'static str,
  108. pub mounted_node: Cell<Option<ElementId>>,
  109. pub(crate) callback: RefCell<Option<BumpBox<'bump, dyn FnMut(SyntheticEvent) + 'bump>>>,
  110. }
  111. impl Listener<'_> {
  112. // serialize the listener event stuff to a string
  113. pub fn serialize(&self) {
  114. //
  115. }
  116. pub fn deserialize() {
  117. //
  118. }
  119. }
  120. /// Virtual Components for custom user-defined components
  121. /// Only supports the functional syntax
  122. pub struct VComponent<'src> {
  123. pub key: Option<&'src str>,
  124. pub ass_scope: Cell<Option<ScopeId>>,
  125. pub(crate) caller: Rc<dyn Fn(&Scope) -> DomTree>,
  126. pub(crate) children: &'src [VNode<'src>],
  127. pub(crate) comparator: Option<&'src dyn Fn(&VComponent) -> bool>,
  128. pub(crate) drop_props: RefCell<Option<BumpBox<'src, dyn FnMut()>>>,
  129. pub is_static: bool,
  130. pub can_memoize: bool,
  131. pub name: &'static str,
  132. // a pointer into the bump arena (given by the 'src lifetime)
  133. pub(crate) raw_props: *const (),
  134. // a pointer to the raw fn typ
  135. pub(crate) user_fc: *const (),
  136. }
  137. pub struct VSuspended<'a> {
  138. pub dom_id: Cell<Option<ElementId>>,
  139. pub task_id: u64,
  140. pub callback: RefCell<Option<&'a dyn FnOnce(SuspendedContext<'a>) -> DomTree<'a>>>,
  141. }
  142. /// This struct provides an ergonomic API to quickly build VNodes.
  143. ///
  144. /// NodeFactory is used to build VNodes in the component's memory space.
  145. /// This struct adds metadata to the final VNode about listeners, attributes, and children
  146. #[derive(Copy, Clone)]
  147. pub struct NodeFactory<'a> {
  148. pub(crate) bump: &'a Bump,
  149. }
  150. impl<'a> NodeFactory<'a> {
  151. pub fn new(bump: &'a Bump) -> NodeFactory<'a> {
  152. NodeFactory { bump }
  153. }
  154. #[inline]
  155. pub fn bump(&self) -> &'a bumpalo::Bump {
  156. &self.bump
  157. }
  158. pub fn render_directly<F>(&self, lazy_nodes: LazyNodes<'a, F>) -> DomTree<'a>
  159. where
  160. F: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  161. {
  162. Some(lazy_nodes.into_vnode(NodeFactory { bump: self.bump }))
  163. }
  164. pub fn unstable_place_holder() -> VNode<'static> {
  165. VNode::Text(VText {
  166. text: "",
  167. dom_id: empty_cell(),
  168. is_static: true,
  169. })
  170. }
  171. /// Used in a place or two to make it easier to build vnodes from dummy text
  172. pub fn static_text(&self, text: &'static str) -> VNode<'a> {
  173. VNode::Text(VText {
  174. dom_id: empty_cell(),
  175. text,
  176. is_static: true,
  177. })
  178. }
  179. /// Parses a lazy text Arguments and returns a string and a flag indicating if the text is 'static
  180. ///
  181. /// Text that's static may be pointer compared, making it cheaper to diff
  182. pub fn raw_text(&self, args: Arguments) -> (&'a str, bool) {
  183. match args.as_str() {
  184. Some(static_str) => (static_str, true),
  185. None => {
  186. use bumpalo::core_alloc::fmt::Write;
  187. let mut s = bumpalo::collections::String::new_in(self.bump());
  188. s.write_fmt(args).unwrap();
  189. (s.into_bump_str(), false)
  190. }
  191. }
  192. }
  193. /// Create some text that's allocated along with the other vnodes
  194. ///
  195. pub fn text(&self, args: Arguments) -> VNode<'a> {
  196. let (text, is_static) = self.raw_text(args);
  197. VNode::Text(VText {
  198. text,
  199. is_static,
  200. dom_id: empty_cell(),
  201. })
  202. }
  203. pub fn element<L, A, V>(
  204. &self,
  205. el: impl DioxusElement,
  206. listeners: L,
  207. attributes: A,
  208. children: V,
  209. key: Option<Arguments>,
  210. ) -> VNode<'a>
  211. where
  212. L: 'a + AsRef<[Listener<'a>]>,
  213. A: 'a + AsRef<[Attribute<'a>]>,
  214. V: 'a + AsRef<[VNode<'a>]>,
  215. {
  216. self.raw_element(
  217. el.tag_name(),
  218. el.namespace(),
  219. listeners,
  220. attributes,
  221. children,
  222. key,
  223. )
  224. }
  225. pub fn raw_element<L, A, V>(
  226. &self,
  227. tag: &'static str,
  228. namespace: Option<&'static str>,
  229. listeners: L,
  230. attributes: A,
  231. children: V,
  232. key: Option<Arguments>,
  233. ) -> VNode<'a>
  234. where
  235. L: 'a + AsRef<[Listener<'a>]>,
  236. A: 'a + AsRef<[Attribute<'a>]>,
  237. V: 'a + AsRef<[VNode<'a>]>,
  238. {
  239. let listeners: &'a L = self.bump().alloc(listeners);
  240. let listeners = listeners.as_ref();
  241. let attributes: &'a A = self.bump().alloc(attributes);
  242. let attributes = attributes.as_ref();
  243. let children: &'a V = self.bump().alloc(children);
  244. let children = children.as_ref();
  245. let key = key.map(|f| self.raw_text(f).0);
  246. VNode::Element(self.bump().alloc(VElement {
  247. tag_name: tag,
  248. key,
  249. namespace,
  250. listeners,
  251. attributes,
  252. children,
  253. dom_id: empty_cell(),
  254. }))
  255. }
  256. pub fn attr(
  257. &self,
  258. name: &'static str,
  259. val: Arguments,
  260. namespace: Option<&'static str>,
  261. is_volatile: bool,
  262. ) -> Attribute<'a> {
  263. let (value, is_static) = self.raw_text(val);
  264. Attribute {
  265. name,
  266. value,
  267. is_static,
  268. namespace,
  269. is_volatile,
  270. }
  271. }
  272. pub fn attr_with_alloc_val(
  273. &self,
  274. name: &'static str,
  275. val: &'a str,
  276. namespace: Option<&'static str>,
  277. is_volatile: bool,
  278. ) -> Attribute<'a> {
  279. Attribute {
  280. name,
  281. value: val,
  282. is_static: false,
  283. namespace,
  284. is_volatile,
  285. }
  286. }
  287. pub fn component<P, V>(
  288. &self,
  289. component: FC<P>,
  290. props: P,
  291. key: Option<Arguments>,
  292. children: V,
  293. ) -> VNode<'a>
  294. where
  295. P: Properties + 'a,
  296. V: 'a + AsRef<[VNode<'a>]>,
  297. {
  298. let bump = self.bump();
  299. let name = crate::util::type_name_of(component);
  300. // We don't want the fat part of the fat pointer
  301. // This function does static dispatch so we don't need any VTable stuff
  302. let children: &'a V = bump.alloc(children);
  303. let children = children.as_ref();
  304. let props = bump.alloc(props);
  305. let raw_props = props as *mut P as *mut ();
  306. let user_fc = component as *const ();
  307. let comparator: Option<&dyn Fn(&VComponent) -> bool> = Some(bump.alloc_with(|| {
  308. move |other: &VComponent| {
  309. if user_fc == other.user_fc {
  310. // Safety
  311. // - We guarantee that FC<P> is the same by function pointer
  312. // - Because FC<P> is the same, then P must be the same (even with generics)
  313. // - Non-static P are autoderived to memoize as false
  314. // - This comparator is only called on a corresponding set of bumpframes
  315. let props_memoized = unsafe {
  316. let real_other: &P = &*(other.raw_props as *const _ as *const P);
  317. props.memoize(&real_other)
  318. };
  319. // It's only okay to memoize if there are no children and the props can be memoized
  320. // Implementing memoize is unsafe and done automatically with the props trait
  321. match (props_memoized, children.len() == 0) {
  322. (true, true) => true,
  323. _ => false,
  324. }
  325. } else {
  326. false
  327. }
  328. }
  329. }));
  330. // create a closure to drop the props
  331. let mut has_dropped = false;
  332. let drop_props: &mut dyn FnMut() = bump.alloc_with(|| {
  333. move || unsafe {
  334. if !has_dropped {
  335. let real_other = raw_props as *mut _ as *mut P;
  336. let b = BumpBox::from_raw(real_other);
  337. std::mem::drop(b);
  338. has_dropped = true;
  339. }
  340. }
  341. });
  342. let drop_props = unsafe { BumpBox::from_raw(drop_props) };
  343. let is_static = children.len() == 0 && P::IS_STATIC && key.is_none();
  344. let key = key.map(|f| self.raw_text(f).0);
  345. VNode::Component(bump.alloc_with(|| VComponent {
  346. user_fc,
  347. comparator,
  348. raw_props,
  349. children,
  350. caller: NodeFactory::create_component_caller(component, raw_props),
  351. is_static,
  352. drop_props: RefCell::new(Some(drop_props)),
  353. can_memoize: P::IS_STATIC,
  354. ass_scope: Cell::new(None),
  355. key,
  356. name,
  357. }))
  358. }
  359. pub fn create_component_caller<'g, P: 'g>(
  360. component: FC<P>,
  361. raw_props: *const (),
  362. ) -> Rc<dyn for<'r> Fn(&'r Scope) -> DomTree<'r>> {
  363. type Captured<'a> = Rc<dyn for<'r> Fn(&'r Scope) -> DomTree<'r> + 'a>;
  364. let caller: Captured = Rc::new(move |scp: &Scope| -> DomTree {
  365. // cast back into the right lifetime
  366. let safe_props: &'_ P = unsafe { &*(raw_props as *const P) };
  367. let cx: Context<P> = Context {
  368. props: safe_props,
  369. scope: scp,
  370. };
  371. let res = component(cx);
  372. let g2 = unsafe { std::mem::transmute(res) };
  373. g2
  374. });
  375. unsafe { std::mem::transmute::<_, Captured<'static>>(caller) }
  376. }
  377. pub fn fragment_from_iter(self, node_iter: impl IntoVNodeList<'a>) -> VNode<'a> {
  378. let children = node_iter.into_vnode_list(self);
  379. // TODO
  380. // We need a dedicated path in the rsx! macro that will trigger the "you need keys" warning
  381. //
  382. // if cfg!(debug_assertions) {
  383. // if children.len() > 1 {
  384. // if children.last().unwrap().key().is_none() {
  385. // log::error!(
  386. // r#"
  387. // Warning: Each child in an array or iterator should have a unique "key" prop.
  388. // Not providing a key will lead to poor performance with lists.
  389. // See docs.rs/dioxus for more information.
  390. // ---
  391. // To help you identify where this error is coming from, we've generated a backtrace.
  392. // "#,
  393. // );
  394. // }
  395. // }
  396. // }
  397. VNode::Fragment(VFragment {
  398. children,
  399. key: None,
  400. is_static: false,
  401. })
  402. }
  403. }
  404. /// Trait implementations for use in the rsx! and html! macros.
  405. ///
  406. /// ## Details
  407. ///
  408. /// This section provides convenience methods and trait implementations for converting common structs into a format accepted
  409. /// by the macros.
  410. ///
  411. /// All dynamic content in the macros must flow in through `fragment_from_iter`. Everything else must be statically layed out.
  412. /// We pipe basically everything through `fragment_from_iter`, so we expect a very specific type:
  413. /// ```
  414. /// impl IntoIterator<Item = impl IntoVNode<'a>>
  415. /// ```
  416. ///
  417. /// As such, all node creation must go through the factory, which is only availble in the component context.
  418. /// These strict requirements make it possible to manage lifetimes and state.
  419. pub trait IntoVNode<'a> {
  420. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a>;
  421. }
  422. pub trait IntoVNodeList<'a> {
  423. fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>];
  424. }
  425. impl<'a, T, V> IntoVNodeList<'a> for T
  426. where
  427. T: IntoIterator<Item = V>,
  428. V: IntoVNode<'a>,
  429. {
  430. fn into_vnode_list(self, cx: NodeFactory<'a>) -> &'a [VNode<'a>] {
  431. let mut nodes = bumpalo::collections::Vec::new_in(cx.bump());
  432. for node in self.into_iter() {
  433. nodes.push(node.into_vnode(cx));
  434. }
  435. if nodes.len() == 0 {
  436. nodes.push(VNode::Anchor(VAnchor {
  437. dom_id: empty_cell(),
  438. }));
  439. }
  440. nodes.into_bump_slice()
  441. }
  442. }
  443. pub struct ScopeChildren<'a>(pub &'a [VNode<'a>]);
  444. impl Copy for ScopeChildren<'_> {}
  445. impl<'a> Clone for ScopeChildren<'a> {
  446. fn clone(&self) -> Self {
  447. ScopeChildren(self.0)
  448. }
  449. }
  450. impl ScopeChildren<'_> {
  451. pub unsafe fn extend_lifetime(self) -> ScopeChildren<'static> {
  452. std::mem::transmute(self)
  453. }
  454. pub unsafe fn unextend_lfetime<'a>(self) -> ScopeChildren<'a> {
  455. std::mem::transmute(self)
  456. }
  457. }
  458. impl<'a> IntoVNodeList<'a> for ScopeChildren<'a> {
  459. fn into_vnode_list(self, _: NodeFactory<'a>) -> &'a [VNode<'a>] {
  460. self.0
  461. }
  462. }
  463. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  464. impl<'a> IntoIterator for VNode<'a> {
  465. type Item = VNode<'a>;
  466. type IntoIter = std::iter::Once<Self::Item>;
  467. fn into_iter(self) -> Self::IntoIter {
  468. std::iter::once(self)
  469. }
  470. }
  471. // For the case where a rendered VNode is passed into the rsx! macro through curly braces
  472. impl<'a> IntoVNode<'a> for VNode<'a> {
  473. fn into_vnode(self, _: NodeFactory<'a>) -> VNode<'a> {
  474. self
  475. }
  476. }
  477. /// A concrete type provider for closures that build VNode structures.
  478. ///
  479. /// This struct wraps lazy structs that build VNode trees Normally, we cannot perform a blanket implementation over
  480. /// closures, but if we wrap the closure in a concrete type, we can maintain separate implementations of IntoVNode.
  481. ///
  482. ///
  483. /// ```rust
  484. /// LazyNodes::new(|f| f.element("div", [], [], [] None))
  485. /// ```
  486. pub struct LazyNodes<'a, G>
  487. where
  488. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  489. {
  490. inner: G,
  491. _p: PhantomData<&'a ()>,
  492. }
  493. impl<'a, G> LazyNodes<'a, G>
  494. where
  495. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  496. {
  497. pub fn new(f: G) -> Self {
  498. Self {
  499. inner: f,
  500. _p: PhantomData {},
  501. }
  502. }
  503. }
  504. // Our blanket impl
  505. impl<'a, G> IntoVNode<'a> for LazyNodes<'a, G>
  506. where
  507. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  508. {
  509. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  510. (self.inner)(cx)
  511. }
  512. }
  513. // Our blanket impl
  514. impl<'a, G> IntoIterator for LazyNodes<'a, G>
  515. where
  516. G: FnOnce(NodeFactory<'a>) -> VNode<'a>,
  517. {
  518. type Item = Self;
  519. type IntoIter = std::iter::Once<Self::Item>;
  520. fn into_iter(self) -> Self::IntoIter {
  521. std::iter::once(self)
  522. }
  523. }
  524. // Conveniently, we also support "null" (nothing) passed in
  525. impl IntoVNode<'_> for () {
  526. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  527. cx.fragment_from_iter(None as Option<VNode>)
  528. }
  529. }
  530. // Conveniently, we also support "None"
  531. impl IntoVNode<'_> for Option<()> {
  532. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  533. cx.fragment_from_iter(None as Option<VNode>)
  534. }
  535. }
  536. impl<'a> IntoVNode<'a> for Option<VNode<'a>> {
  537. fn into_vnode(self, cx: NodeFactory<'a>) -> VNode<'a> {
  538. match self {
  539. Some(n) => n,
  540. None => cx.fragment_from_iter(None as Option<VNode>),
  541. }
  542. }
  543. }
  544. impl IntoVNode<'_> for &'static str {
  545. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  546. cx.static_text(self)
  547. }
  548. }
  549. impl IntoVNode<'_> for Arguments<'_> {
  550. fn into_vnode<'a>(self, cx: NodeFactory<'a>) -> VNode<'a> {
  551. cx.text(self)
  552. }
  553. }
  554. impl Debug for NodeFactory<'_> {
  555. fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  556. Ok(())
  557. }
  558. }
  559. impl Debug for VNode<'_> {
  560. fn fmt(&self, s: &mut Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
  561. match &self {
  562. VNode::Element(el) => {
  563. //
  564. s.debug_struct("VElement")
  565. .field("name", &el.tag_name)
  566. .field("key", &el.key)
  567. .finish()
  568. }
  569. VNode::Text(t) => write!(s, "VText {{ text: {} }}", t.text),
  570. VNode::Anchor(a) => write!(s, "VAnchor"),
  571. VNode::Fragment(frag) => write!(s, "VFragment {{ children: {:?} }}", frag.children),
  572. VNode::Suspended { .. } => write!(s, "VSuspended"),
  573. VNode::Component(comp) => write!(
  574. s,
  575. "VComponent {{ fc: {:?}, children: {:?} }}",
  576. comp.name, comp.children
  577. ),
  578. }
  579. }
  580. }