mutations.rs 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. use std::{any::Any, fmt::Debug};
  9. /// ## Mutations
  10. ///
  11. /// This method returns "mutations" - IE the necessary changes to get the RealDOM to match the VirtualDOM. It also
  12. /// includes a list of NodeRefs that need to be applied and effects that need to be triggered after the RealDOM has
  13. /// applied the edits.
  14. ///
  15. /// Mutations are the only link between the RealDOM and the VirtualDOM.
  16. pub struct Mutations<'a> {
  17. pub edits: Vec<DomEdit<'a>>,
  18. pub dirty_scopes: FxHashSet<ScopeId>,
  19. pub refs: Vec<NodeRefMutation<'a>>,
  20. }
  21. impl Debug for Mutations<'_> {
  22. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  23. f.debug_struct("Mutations")
  24. .field("edits", &self.edits)
  25. .field("noderefs", &self.refs)
  26. .finish()
  27. }
  28. }
  29. /// A `DomEdit` represents a serialized form of the VirtualDom's trait-based API. This allows streaming edits across the
  30. /// network or through FFI boundaries.
  31. #[derive(Debug, PartialEq)]
  32. #[cfg_attr(
  33. feature = "serialize",
  34. derive(serde::Serialize, serde::Deserialize),
  35. serde(tag = "type")
  36. )]
  37. pub enum DomEdit<'bump> {
  38. PushRoot {
  39. root: u64,
  40. },
  41. AppendChildren {
  42. many: u32,
  43. },
  44. // // save a possibly-fragment node as a template
  45. // SaveAsTemplate {
  46. // many: u32,
  47. // },
  48. // "Root" refers to the item directly
  49. // it's a waste of an instruction to push the root directly
  50. ReplaceWith {
  51. root: u64,
  52. m: u32,
  53. },
  54. InsertAfter {
  55. root: u64,
  56. n: u32,
  57. },
  58. InsertBefore {
  59. root: u64,
  60. n: u32,
  61. },
  62. Remove {
  63. root: u64,
  64. },
  65. CreateTextNode {
  66. text: &'bump str,
  67. root: u64,
  68. },
  69. CreateElement {
  70. tag: &'bump str,
  71. root: u64,
  72. },
  73. CreateElementNs {
  74. tag: &'bump str,
  75. root: u64,
  76. ns: &'static str,
  77. },
  78. CreatePlaceholder {
  79. root: u64,
  80. },
  81. NewEventListener {
  82. event_name: &'static str,
  83. scope: ScopeId,
  84. root: u64,
  85. },
  86. RemoveEventListener {
  87. root: u64,
  88. event: &'static str,
  89. },
  90. SetText {
  91. root: u64,
  92. text: &'bump str,
  93. },
  94. SetAttribute {
  95. root: u64,
  96. field: &'static str,
  97. value: &'bump str,
  98. ns: Option<&'bump str>,
  99. },
  100. RemoveAttribute {
  101. root: u64,
  102. name: &'static str,
  103. ns: Option<&'bump str>,
  104. },
  105. }
  106. use fxhash::FxHashSet;
  107. use DomEdit::*;
  108. impl<'a> Mutations<'a> {
  109. pub(crate) fn new() -> Self {
  110. Self {
  111. edits: Vec::new(),
  112. refs: Vec::new(),
  113. dirty_scopes: Default::default(),
  114. }
  115. }
  116. // Navigation
  117. pub(crate) fn push_root(&mut self, root: ElementId) {
  118. let id = root.as_u64();
  119. self.edits.push(PushRoot { root: id });
  120. }
  121. pub(crate) fn replace_with(&mut self, root: ElementId, m: u32) {
  122. let root = root.as_u64();
  123. self.edits.push(ReplaceWith { m, root });
  124. }
  125. pub(crate) fn insert_after(&mut self, root: ElementId, n: u32) {
  126. let root = root.as_u64();
  127. self.edits.push(InsertAfter { n, root });
  128. }
  129. pub(crate) fn insert_before(&mut self, root: ElementId, n: u32) {
  130. let root = root.as_u64();
  131. self.edits.push(InsertBefore { n, root });
  132. }
  133. pub(crate) fn append_children(&mut self, n: u32) {
  134. self.edits.push(AppendChildren { many: n });
  135. }
  136. // Remove Nodes from the dom
  137. pub(crate) fn remove(&mut self, id: u64) {
  138. self.edits.push(Remove { root: id });
  139. }
  140. // Create
  141. pub(crate) fn create_text_node(&mut self, text: &'a str, id: ElementId) {
  142. let id = id.as_u64();
  143. self.edits.push(CreateTextNode { text, root: id });
  144. }
  145. pub(crate) fn create_element(
  146. &mut self,
  147. tag: &'static str,
  148. ns: Option<&'static str>,
  149. id: ElementId,
  150. ) {
  151. let id = id.as_u64();
  152. match ns {
  153. Some(ns) => self.edits.push(CreateElementNs { root: id, ns, tag }),
  154. None => self.edits.push(CreateElement { root: id, tag }),
  155. }
  156. }
  157. // placeholders are nodes that don't get rendered but still exist as an "anchor" in the real dom
  158. pub(crate) fn create_placeholder(&mut self, id: ElementId) {
  159. let id = id.as_u64();
  160. self.edits.push(CreatePlaceholder { root: id });
  161. }
  162. // events
  163. pub(crate) fn new_event_listener(&mut self, listener: &Listener, scope: ScopeId) {
  164. let Listener {
  165. event,
  166. mounted_node,
  167. ..
  168. } = listener;
  169. let element_id = mounted_node.get().unwrap().as_u64();
  170. self.edits.push(NewEventListener {
  171. scope,
  172. event_name: event,
  173. root: element_id,
  174. });
  175. }
  176. pub(crate) fn remove_event_listener(&mut self, event: &'static str, root: u64) {
  177. self.edits.push(RemoveEventListener { event, root });
  178. }
  179. // modify
  180. pub(crate) fn set_text(&mut self, text: &'a str, root: u64) {
  181. self.edits.push(SetText { text, root });
  182. }
  183. pub(crate) fn set_attribute(&mut self, attribute: &'a Attribute, root: u64) {
  184. let Attribute {
  185. name,
  186. value,
  187. namespace,
  188. ..
  189. } = attribute;
  190. self.edits.push(SetAttribute {
  191. field: name,
  192. value,
  193. ns: *namespace,
  194. root,
  195. });
  196. }
  197. pub(crate) fn remove_attribute(&mut self, attribute: &Attribute, root: u64) {
  198. let Attribute {
  199. name, namespace, ..
  200. } = attribute;
  201. self.edits.push(RemoveAttribute {
  202. name,
  203. ns: *namespace,
  204. root,
  205. });
  206. }
  207. pub(crate) fn mark_dirty_scope(&mut self, scope: ScopeId) {
  208. self.dirty_scopes.insert(scope);
  209. }
  210. }
  211. // refs are only assigned once
  212. pub struct NodeRefMutation<'a> {
  213. pub element: &'a mut Option<once_cell::sync::OnceCell<Box<dyn Any>>>,
  214. pub element_id: ElementId,
  215. }
  216. impl<'a> std::fmt::Debug for NodeRefMutation<'a> {
  217. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  218. f.debug_struct("NodeRefMutation")
  219. .field("element_id", &self.element_id)
  220. .finish()
  221. }
  222. }
  223. impl<'a> NodeRefMutation<'a> {
  224. pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
  225. self.element
  226. .as_ref()
  227. .and_then(|f| f.get())
  228. .and_then(|f| f.downcast_ref::<T>())
  229. }
  230. pub fn downcast_mut<T: 'static>(&mut self) -> Option<&mut T> {
  231. self.element
  232. .as_mut()
  233. .and_then(|f| f.get_mut())
  234. .and_then(|f| f.downcast_mut::<T>())
  235. }
  236. }