mutations.rs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. //! Instructions returned by the VirtualDOM on how to modify the Real DOM.
  2. //!
  3. //! This module contains an internal API to generate these instructions.
  4. //!
  5. //! Beware that changing code in this module will break compatibility with
  6. //! interpreters for these types of DomEdits.
  7. use crate::innerlude::*;
  8. /// A renderer for Dioxus to modify during diffing
  9. ///
  10. /// The renderer should implement a Stack Machine. IE each call to the below methods are modifications to the renderer's
  11. /// internal stack for creating and modifying nodes.
  12. ///
  13. /// Dioxus guarantees that the stack is always in a valid state.
  14. pub trait Renderer<'a> {
  15. /// Load this element onto the stack
  16. fn push_root(&mut self, root: ElementId);
  17. /// Pop the topmost element from the stack
  18. fn pop_root(&mut self);
  19. /// Replace the given element with the next m elements on the stack
  20. fn replace_with(&mut self, root: ElementId, m: u32);
  21. /// Insert the next m elements on the stack after the given element
  22. fn insert_after(&mut self, root: ElementId, n: u32);
  23. /// Insert the next m elements on the stack before the given element
  24. fn insert_before(&mut self, root: ElementId, n: u32);
  25. /// Append the next n elements on the stack to the n+1 element on the stack
  26. fn append_children(&mut self, n: u32);
  27. /// Create a new element with the given text and ElementId
  28. fn create_text_node(&mut self, text: &'a str, root: ElementId);
  29. /// Create an element with the given tag name, optional namespace, and ElementId
  30. /// Note that namespaces do not cascade down the tree, so the renderer must handle this if it implements namespaces
  31. fn create_element(&mut self, tag: &'static str, ns: Option<&'static str>, id: ElementId);
  32. /// Create a hidden element to be used later for replacement.
  33. /// Used in suspense, lists, and other places where we need to hide a node before it is ready to be shown.
  34. /// This is up to the renderer to implement, but it should not be visible to the user.
  35. fn create_placeholder(&mut self, id: ElementId);
  36. /// Remove the targeted node from the DOM
  37. fn remove(&mut self, root: ElementId);
  38. /// Remove an attribute from an existing element
  39. fn remove_attribute(&mut self, attribute: &Attribute, root: ElementId);
  40. /// Remove all the children of the given element
  41. fn remove_children(&mut self, root: ElementId);
  42. /// Attach a new listener to the dom
  43. fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId);
  44. /// Remove an existing listener from the dom
  45. fn remove_event_listener(&mut self, event: &'static str, root: ElementId);
  46. /// Set the text content of a node
  47. fn set_text(&mut self, text: &'a str, root: ElementId);
  48. /// Set an attribute on an element
  49. fn set_attribute(
  50. &mut self,
  51. name: &'static str,
  52. value: AttributeValue<'a>,
  53. namespace: Option<&'a str>,
  54. root: ElementId,
  55. );
  56. /// General statistics for doing things that extend outside of the renderer
  57. fn mark_dirty_scope(&mut self, scope: ScopeId);
  58. /// Save the current n nodes to the ID to be loaded later
  59. fn save(&mut self, id: &'static str, num: u32);
  60. /// Loads a set of saved nodes from the ID into a scratch space
  61. fn load(&mut self, id: &'static str, index: u32);
  62. /// Assign the element on the stack's descendent the given ID
  63. fn assign_id(&mut self, descendent: &'static [u8], id: ElementId);
  64. /// Replace the given element of the topmost element with the next m elements on the stack
  65. /// Is essentially a combination of assign_id and replace_with
  66. fn replace_descendant(&mut self, descendent: &'static [u8], m: u32);
  67. }
  68. /*
  69. div {
  70. div {
  71. div {
  72. div {}
  73. }
  74. }
  75. }
  76. push_child(0)
  77. push_child(1)
  78. push_child(3)
  79. push_child(4)
  80. pop
  81. pop
  82. clone_node(0)
  83. set_node(el, [1,2,3,4])
  84. set_attribute("class", "foo")
  85. append_child(1)
  86. */
  87. //! Instructions returned by the VirtualDOM on how to modify the Real DOM.
  88. //!
  89. //! This module contains an internal API to generate these instructions.
  90. //!
  91. //! Beware that changing code in this module will break compatibility with
  92. //! interpreters for these types of DomEdits.
  93. use crate::innerlude::*;
  94. use std::{any::Any, fmt::Debug};
  95. /// ## Mutations
  96. ///
  97. /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
  98. /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
  99. /// applied the edits.
  100. ///
  101. /// Mutations are the only link between the RealDOM and the VirtualDOM.
  102. pub struct Mutations<'a> {
  103. /// The list of edits that need to be applied for the RealDOM to match the VirtualDOM.
  104. pub edits: Vec<DomEdit<'a>>,
  105. /// The list of Scopes that were diffed, created, and removed during the Diff process.
  106. pub dirty_scopes: FxHashSet<ScopeId>,
  107. /// The list of nodes to connect to the RealDOM.
  108. pub refs: Vec<NodeRefMutation<'a>>,
  109. }
  110. impl Debug for Mutations<'_> {
  111. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  112. f.debug_struct("Mutations")
  113. .field("edits", &self.edits)
  114. .field("noderefs", &self.refs)
  115. .finish()
  116. }
  117. }
  118. /// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
  119. /// network or through FFI boundaries.
  120. #[derive(Debug, PartialEq)]
  121. #[cfg_attr(
  122. feature = "serialize",
  123. derive(serde::Serialize, serde::Deserialize),
  124. serde(tag = "type")
  125. )]
  126. pub enum DomEdit<'bump> {
  127. /// Pop the topmost node from our stack and append them to the node
  128. /// at the top of the stack.
  129. AppendChildren {
  130. /// The parent to append nodes to.
  131. root: Option<u64>,
  132. /// The ids of the children to append.
  133. children: Vec<u64>,
  134. },
  135. /// Replace a given (single) node with a handful of nodes currently on the stack.
  136. ReplaceWith {
  137. /// The ID of the node to be replaced.
  138. root: Option<u64>,
  139. /// The ids of the nodes to replace the root with.
  140. nodes: Vec<u64>,
  141. },
  142. /// Insert a number of nodes after a given node.
  143. InsertAfter {
  144. /// The ID of the node to insert after.
  145. root: Option<u64>,
  146. /// The ids of the nodes to insert after the target node.
  147. nodes: Vec<u64>,
  148. },
  149. /// Insert a number of nodes before a given node.
  150. InsertBefore {
  151. /// The ID of the node to insert before.
  152. root: Option<u64>,
  153. /// The ids of the nodes to insert before the target node.
  154. nodes: Vec<u64>,
  155. },
  156. /// Remove a particular node from the DOM
  157. Remove {
  158. /// The ID of the node to remove.
  159. root: Option<u64>,
  160. },
  161. /// Create a new purely-text node
  162. CreateTextNode {
  163. /// The ID the new node should have.
  164. root: Option<u64>,
  165. /// The textcontent of the node
  166. text: &'bump str,
  167. },
  168. /// Create a new purely-element node
  169. CreateElement {
  170. /// The ID the new node should have.
  171. root: Option<u64>,
  172. /// The tagname of the node
  173. tag: &'bump str,
  174. /// The number of children nodes that will follow this message.
  175. children: u32,
  176. },
  177. /// Create a new purely-comment node with a given namespace
  178. CreateElementNs {
  179. /// The ID the new node should have.
  180. root: Option<u64>,
  181. /// The namespace of the node
  182. tag: &'bump str,
  183. /// The namespace of the node (like `SVG`)
  184. ns: &'static str,
  185. /// The number of children nodes that will follow this message.
  186. children: u32,
  187. },
  188. /// Create a new placeholder node.
  189. /// In most implementations, this will either be a hidden div or a comment node.
  190. CreatePlaceholder {
  191. /// The ID the new node should have.
  192. root: Option<u64>,
  193. },
  194. /// Create a new Event Listener.
  195. NewEventListener {
  196. /// The name of the event to listen for.
  197. event_name: &'static str,
  198. /// The ID of the node to attach the listener to.
  199. scope: ScopeId,
  200. /// The ID of the node to attach the listener to.
  201. root: Option<u64>,
  202. },
  203. /// Remove an existing Event Listener.
  204. RemoveEventListener {
  205. /// The ID of the node to remove.
  206. root: Option<u64>,
  207. /// The name of the event to remove.
  208. event: &'static str,
  209. },
  210. /// Set the textcontent of a node.
  211. SetText {
  212. /// The ID of the node to set the textcontent of.
  213. root: Option<u64>,
  214. /// The textcontent of the node
  215. text: &'bump str,
  216. },
  217. /// Set the value of a node's attribute.
  218. SetAttribute {
  219. /// The ID of the node to set the attribute of.
  220. root: Option<u64>,
  221. /// The name of the attribute to set.
  222. field: &'static str,
  223. /// The value of the attribute.
  224. value: AttributeValue<'bump>,
  225. // value: &'bump str,
  226. /// The (optional) namespace of the attribute.
  227. /// For instance, "style" is in the "style" namespace.
  228. ns: Option<&'bump str>,
  229. },
  230. /// Remove an attribute from a node.
  231. RemoveAttribute {
  232. /// The ID of the node to remove.
  233. root: Option<u64>,
  234. /// The name of the attribute to remove.
  235. name: &'static str,
  236. /// The namespace of the attribute.
  237. ns: Option<&'bump str>,
  238. },
  239. /// Clones a node.
  240. CloneNode {
  241. /// The ID of the node to clone.
  242. id: Option<u64>,
  243. /// The ID of the new node.
  244. new_id: u64,
  245. },
  246. /// Clones the children of a node. (allows cloning fragments)
  247. CloneNodeChildren {
  248. /// The ID of the node to clone.
  249. id: Option<u64>,
  250. /// The ID of the new node.
  251. new_ids: Vec<u64>,
  252. },
  253. /// Navigates to the last node to the first child of the current node.
  254. FirstChild {},
  255. /// Navigates to the last node to the last child of the current node.
  256. NextSibling {},
  257. /// Navigates to the last node to the parent of the current node.
  258. ParentNode {},
  259. /// Stores the last node with a new id.
  260. StoreWithId {
  261. /// The ID of the node to store.
  262. id: u64,
  263. },
  264. /// Manually set the last node.
  265. SetLastNode {
  266. /// The ID to set the last node to.
  267. id: u64,
  268. },
  269. }
  270. use rustc_hash::FxHashSet;
  271. use DomEdit::*;
  272. #[allow(unused)]
  273. impl<'a> Mutations<'a> {
  274. pub(crate) fn new() -> Self {
  275. Self {
  276. edits: Vec::new(),
  277. refs: Vec::new(),
  278. dirty_scopes: Default::default(),
  279. }
  280. }
  281. pub(crate) fn replace_with(&mut self, root: Option<u64>, nodes: Vec<u64>) {
  282. self.edits.push(ReplaceWith { nodes, root });
  283. }
  284. pub(crate) fn insert_after(&mut self, root: Option<u64>, nodes: Vec<u64>) {
  285. self.edits.push(InsertAfter { nodes, root });
  286. }
  287. pub(crate) fn insert_before(&mut self, root: Option<u64>, nodes: Vec<u64>) {
  288. self.edits.push(InsertBefore { nodes, root });
  289. }
  290. pub(crate) fn append_children(&mut self, root: Option<u64>, children: Vec<u64>) {
  291. self.edits.push(AppendChildren { root, children });
  292. }
  293. // Remove Nodes from the dom
  294. pub(crate) fn remove(&mut self, id: Option<u64>) {
  295. self.edits.push(Remove { root: id });
  296. }
  297. // Create
  298. pub(crate) fn create_text_node(&mut self, text: &'a str, id: Option<u64>) {
  299. self.edits.push(CreateTextNode { text, root: id });
  300. }
  301. pub(crate) fn create_element(
  302. &mut self,
  303. tag: &'static str,
  304. ns: Option<&'static str>,
  305. id: Option<u64>,
  306. children: u32,
  307. ) {
  308. match ns {
  309. Some(ns) => self.edits.push(CreateElementNs {
  310. root: id,
  311. ns,
  312. tag,
  313. children,
  314. }),
  315. None => self.edits.push(CreateElement {
  316. root: id,
  317. tag,
  318. children,
  319. }),
  320. }
  321. }
  322. // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
  323. pub(crate) fn create_placeholder(&mut self, id: Option<u64>) {
  324. self.edits.push(CreatePlaceholder { root: id });
  325. }
  326. // events
  327. pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
  328. let Listener {
  329. event,
  330. mounted_node,
  331. ..
  332. } = listener;
  333. let element_id = Some(mounted_node.get().unwrap().into());
  334. self.edits.push(NewEventListener {
  335. scope,
  336. event_name: event,
  337. root: element_id,
  338. });
  339. }
  340. pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: Option<u64>) {
  341. self.edits.push(RemoveEventListener { event, root });
  342. }
  343. // modify
  344. pub(crate) fn set_text(&mut self, text: &'a str, root: Option<u64>) {
  345. self.edits.push(SetText { text, root });
  346. }
  347. pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute<'a>, root: Option<u64>) {
  348. let Attribute {
  349. value, attribute, ..
  350. } = attribute;
  351. self.edits.push(SetAttribute {
  352. field: attribute.name,
  353. value: value.clone(),
  354. ns: attribute.namespace,
  355. root,
  356. });
  357. }
  358. pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: Option<u64>) {
  359. let Attribute { attribute, .. } = attribute;
  360. self.edits.push(RemoveAttribute {
  361. name: attribute.name,
  362. ns: attribute.namespace,
  363. root,
  364. });
  365. }
  366. pub(crate) fn mark_dirty_scope(&mut self, scope: ScopeId) {
  367. self.dirty_scopes.insert(scope);
  368. }
  369. pub(crate) fn clone_node(&mut self, id: Option<u64>, new_id: u64) {
  370. self.edits.push(CloneNode { id, new_id });
  371. }
  372. pub(crate) fn clone_node_children(&mut self, id: Option<u64>, new_ids: Vec<u64>) {
  373. self.edits.push(CloneNodeChildren { id, new_ids });
  374. }
  375. pub(crate) fn first_child(&mut self) {
  376. self.edits.push(FirstChild {});
  377. }
  378. pub(crate) fn next_sibling(&mut self) {
  379. self.edits.push(NextSibling {});
  380. }
  381. pub(crate) fn parent_node(&mut self) {
  382. self.edits.push(ParentNode {});
  383. }
  384. pub(crate) fn store_with_id(&mut self, id: u64) {
  385. self.edits.push(StoreWithId { id });
  386. }
  387. pub(crate) fn set_last_node(&mut self, id: u64) {
  388. self.edits.push(SetLastNode { id });
  389. }
  390. }
  391. // refs are only assigned once
  392. pub struct NodeRefMutation<'a> {
  393. pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
  394. pub element_id: ElementId,
  395. }
  396. impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
  397. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  398. f.debug_struct("NodeRefMutation")
  399. .field("element_id", &self.element_id)
  400. .finish()
  401. }
  402. }
  403. impl<'a> NodeRefMutation<'a> {
  404. pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
  405. self.element
  406. .as_ref()
  407. .and_then(|f| f.get())
  408. .and_then(|f| f.downcast_ref::<T>())
  409. }
  410. pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
  411. self.element
  412. .as_mut()
  413. .and_then(|f| f.get_mut())
  414. .and_then(|f| f.downcast_mut::<T>())
  415. }
  416. }