From 556d0e1216206339fa7d93ef4f61372ffa778fc8 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 18:35:07 -0400 Subject: [PATCH 1/8] Add documentation and safety instructions in Node --- packages/blitz-dom/src/node.rs | 66 +++++++++++++++++++++++++++++----- 1 file changed, 57 insertions(+), 9 deletions(-) diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index b684509cd..2daa6a2f7 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -41,51 +41,92 @@ pub enum DisplayOuter { None, } -// todo: might be faster to migrate this to ecs and split apart at a different boundary +/// A node in a [`Document`](crate::Document). pub struct Node { - // The actual tree we belong to. This is unsafe!! + /// The tree of nodes containing this node. + /// + /// # Safety + /// This is a raw pointer to the slab containing this node. + /// By using this pointer you must gurantee that this node outlives the tree. pub tree: *mut Slab, - /// Our Id + /// This node's ID. pub id: usize, - /// Our parent's ID + + /// This node's parent ID. pub parent: Option, - // What are our children? + + /// This node's child IDs. pub children: Vec, - /// Our parent in the layout hierachy: a separate list that includes anonymous collections of inline elements + + /// This node's parent ID in the layout hierachy: a separate list that includes anonymous collections of inline elements. pub layout_parent: Cell>, - /// A separate child list that includes anonymous collections of inline elements + + /// This node's child IDs in the layout hierachy: a separate list that includes anonymous collections of inline elements. pub layout_children: RefCell>>, - /// Node type (Element, TextNode, etc) specific data + /// Data specific to this node's type. pub raw_dom_data: NodeData, // This little bundle of joy is our style data from stylo and a lock guard that allows access to it // TODO: See if guard can be hoisted to a higher level + /// The (optional) stylo data for this node. pub stylo_element_data: AtomicRefCell>, + + /// The selector flags for this node. pub selector_flags: AtomicRefCell, + + /// Lock guard for this node's stylo data. pub guard: SharedRwLock, + + /// The state of the element, represented as a bitfield. pub element_state: ElementState, // Taffy layout data: pub style: Style, + + /// Flag indicating whether this node is hidden. pub hidden: bool, + + /// Flag indicating whether this node is hovered. pub is_hovered: bool, + + /// Flag indicating whether this node has a stylo snapshot. pub has_snapshot: bool, + + /// Flag indicating whether the current snapshot has been handled. pub snapshot_handled: AtomicBool, + + /// Outer display type of this node. pub display_outer: DisplayOuter, + + /// Cached layout for this node. pub cache: Cache, + + /// Cached layout before rounding for this node. pub unrounded_layout: Layout, + + /// Cached final layout for this node. pub final_layout: Layout, + + /// Registered event listeners for this node. pub listeners: Vec, + + /// Current scroll offset for this node. pub scroll_offset: kurbo::Point, - // Flags + /// Flag indicating whether this node is the root element of an inline layout. pub is_inline_root: bool, + + /// Flag indicating whether this node is the root element of a table. pub is_table_root: bool, } impl Node { + /// Create a new node in a tree. + /// + /// # Safety + /// `tree` must outlive this `Node`. pub fn new(tree: *mut Slab, id: usize, guard: SharedRwLock, data: NodeData) -> Self { Self { tree, @@ -135,6 +176,7 @@ impl Node { ) } + /// Returns true if this node, or any of its children, is a block. pub fn is_or_contains_block(&self) -> bool { let display = self.display_style().unwrap_or(Display::inline()); @@ -154,6 +196,7 @@ impl Node { } } + /// Returns true if this node is able to focused. pub fn is_focussable(&self) -> bool { self.raw_dom_data .downcast_element() @@ -167,24 +210,28 @@ impl Node { } } + /// Flag this node as hovered. pub fn hover(&mut self) { self.is_hovered = true; self.element_state.insert(ElementState::HOVER); self.set_restyle_hint(RestyleHint::RESTYLE_SELF); } + /// Un-flag this node as hovered. pub fn unhover(&mut self) { self.is_hovered = false; self.element_state.remove(ElementState::HOVER); self.set_restyle_hint(RestyleHint::RESTYLE_SELF); } + /// Flag this node as focused. pub fn focus(&mut self) { self.element_state .insert(ElementState::FOCUS | ElementState::FOCUSRING); self.set_restyle_hint(RestyleHint::RESTYLE_SELF); } + /// Un-flag this node as focused. pub fn blur(&mut self) { self.element_state .remove(ElementState::FOCUS | ElementState::FOCUSRING); @@ -192,6 +239,7 @@ impl Node { } } +/// The kind of a [`Node`]. #[derive(Debug, Clone, Copy, PartialEq)] pub enum NodeKind { Document, From ad9ae8de3d162dff6401e11fb744e3cb7eaeacf4 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 18:55:24 -0400 Subject: [PATCH 2/8] Cargo fmt --- packages/blitz-dom/src/stylo.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/blitz-dom/src/stylo.rs b/packages/blitz-dom/src/stylo.rs index 01c510053..a058abdd0 100644 --- a/packages/blitz-dom/src/stylo.rs +++ b/packages/blitz-dom/src/stylo.rs @@ -53,7 +53,6 @@ use style::values::computed::text::TextAlign as StyloTextAlign; impl crate::document::Document { /// Walk the whole tree, converting styles to layout pub fn flush_styles_to_layout(&mut self, node_id: usize) { - let display = { let node = self.nodes.get_mut(node_id).unwrap(); let stylo_element_data = node.stylo_element_data.borrow(); @@ -85,7 +84,6 @@ impl crate::document::Document { // If the node has children, then take those children and... let children = self.nodes[node_id].layout_children.borrow_mut().take(); if let Some(mut children) = children { - // Recursively call flush_styles_to_layout on each child for child in children.iter() { self.flush_styles_to_layout(*child); From 32583b3f8af004be293d2c9a0070a79cf5072475 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 19:05:05 -0400 Subject: [PATCH 3/8] Make Viewport::zoom public to replace methods and add docs --- packages/blitz-dom/src/image.rs | 7 ++++++- packages/blitz-dom/src/layout/mod.rs | 4 ++-- packages/blitz-dom/src/lib.rs | 6 ++++++ packages/blitz-dom/src/node.rs | 3 ++- packages/blitz-dom/src/viewport.rs | 29 ++++++++++++---------------- packages/dioxus-native/src/window.rs | 10 +++++----- 6 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/blitz-dom/src/image.rs b/packages/blitz-dom/src/image.rs index 577492f7e..09c84bad4 100644 --- a/packages/blitz-dom/src/image.rs +++ b/packages/blitz-dom/src/image.rs @@ -1,12 +1,17 @@ use taffy::{MaybeMath, MaybeResolve}; +/// Layout context for an image. #[derive(Debug, Clone, Copy)] pub struct ImageContext { + /// Inherent size of the image. pub inherent_size: taffy::Size, + + /// Specified size of the image. pub attr_size: taffy::Size>, } -pub fn image_measure_function( +/// Measure an image. +pub fn measure_image( known_dimensions: taffy::Size>, parent_size: taffy::Size>, image_context: &ImageContext, diff --git a/packages/blitz-dom/src/layout/mod.rs b/packages/blitz-dom/src/layout/mod.rs index 52a32f66e..d770eb2ed 100644 --- a/packages/blitz-dom/src/layout/mod.rs +++ b/packages/blitz-dom/src/layout/mod.rs @@ -7,7 +7,7 @@ use crate::node::{NodeData, NodeKind, NodeSpecificData}; use crate::{ document::Document, - image::{image_measure_function, ImageContext}, + image::{measure_image, ImageContext}, node::Node, }; use html5ever::local_name; @@ -245,7 +245,7 @@ impl LayoutPartialTree for Document { inputs, &node.style, |known_dimensions, _available_space| { - image_measure_function( + measure_image( known_dimensions, inputs.parent_size, &image_context, diff --git a/packages/blitz-dom/src/lib.rs b/packages/blitz-dom/src/lib.rs index 2934ab538..446e50a76 100644 --- a/packages/blitz-dom/src/lib.rs +++ b/packages/blitz-dom/src/lib.rs @@ -15,6 +15,7 @@ /// This is the primary entry point for this crate. pub mod document; +/// HTML document data structure. pub mod html_document; /// An implementation for Html5ever's sink trait, allowing us to parse HTML into a DOM. pub mod htmlsink; @@ -37,14 +38,19 @@ pub mod stylo_to_parley; /// Conversions from Stylo types to Taffy and Parley types pub mod stylo_to_taffy; +/// Image layout. pub mod image; +/// Utility functions. pub mod util; +/// Debugging. pub mod debug; +/// Events. pub mod events; +/// Window viewport. pub mod viewport; pub use document::{Document, DocumentLike}; diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index 2daa6a2f7..31ee74dec 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -464,7 +464,7 @@ impl ElementNodeData { } } - pub fn flush_is_focussable(&mut self) { + pub(crate) fn flush_is_focussable(&mut self) { let disabled: bool = self.attr_parsed(local_name!("disabled")).unwrap_or(false); let tabindex: Option = self.attr_parsed(local_name!("tabindex")); @@ -513,6 +513,7 @@ impl ElementNodeData { }); } + /// Take the current cached inline text layout. pub fn take_inline_layout(&mut self) -> Option> { std::mem::take(&mut self.inline_layout_data) } diff --git a/packages/blitz-dom/src/viewport.rs b/packages/blitz-dom/src/viewport.rs index 2774716f5..06ee9fbae 100644 --- a/packages/blitz-dom/src/viewport.rs +++ b/packages/blitz-dom/src/viewport.rs @@ -4,18 +4,23 @@ use style::{ properties::{style_structs::Font, ComputedValues}, }; +/// Window viewport. #[derive(Default, Debug, Clone)] pub struct Viewport { + /// Size of the window. pub window_size: (u32, u32), - hidpi_scale: f32, + /// Font size. + pub font_size: f32, - zoom: f32, + /// Zoom level. + pub zoom: f32, - pub font_size: f32, + hidpi_scale: f32, } impl Viewport { + /// Create a new viewport from a window's physical size and scale factor. pub fn new(physical_width: u32, physical_height: u32, scale_factor: f32) -> Self { Self { window_size: (physical_width, physical_height), @@ -25,31 +30,21 @@ impl Viewport { } } - // Total scaling, the product of the zoom and hdpi scale + /// Total scaling, the product of the zoom and hdpi scale. pub fn scale(&self) -> f32 { self.hidpi_scale * self.zoom } - // Total scaling, the product of the zoom and hdpi scale + + /// Total scaling, the product of the zoom and hdpi scale (as an `f64`). pub fn scale_f64(&self) -> f64 { self.scale() as f64 } + /// Set the hidi scale. pub fn set_hidpi_scale(&mut self, scale: f32) { self.hidpi_scale = scale; } - pub fn zoom(&self) -> f32 { - self.zoom - } - - pub fn set_zoom(&mut self, zoom: f32) { - self.zoom = zoom; - } - - pub fn zoom_mut(&mut self) -> &mut f32 { - &mut self.zoom - } - pub(crate) fn make_device(&self) -> Device { let width = self.window_size.0 as f32 / self.scale(); let height = self.window_size.1 as f32 / self.scale(); diff --git a/packages/dioxus-native/src/window.rs b/packages/dioxus-native/src/window.rs index 37725efbf..582dacc6f 100644 --- a/packages/dioxus-native/src/window.rs +++ b/packages/dioxus-native/src/window.rs @@ -213,8 +213,8 @@ impl View { pub fn mouse_move(&mut self, x: f32, y: f32) -> bool { let viewport_scroll = self.dom.as_ref().viewport_scroll(); - let dom_x = x + viewport_scroll.x as f32 / self.viewport.zoom(); - let dom_y = y + viewport_scroll.y as f32 / self.viewport.zoom(); + let dom_x = x + viewport_scroll.x as f32 / self.viewport.zoom; + let dom_y = y + viewport_scroll.y as f32 / self.viewport.zoom; // println!("Mouse move: ({}, {})", x, y); // println!("Unscaled: ({}, {})",); @@ -338,15 +338,15 @@ impl View { if ctrl | meta { match key_code { KeyCode::Equal => { - *self.viewport.zoom_mut() += 0.1; + self.viewport.zoom += 0.1; self.kick_dom_viewport(); } KeyCode::Minus => { - *self.viewport.zoom_mut() -= 0.1; + self.viewport.zoom -= 0.1; self.kick_dom_viewport(); } KeyCode::Digit0 => { - *self.viewport.zoom_mut() = 1.0; + self.viewport.zoom = 1.0; self.kick_dom_viewport(); } _ => {} From f464f26b6dca4007eb83df71d6171906c3dcfd57 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 19:55:12 -0400 Subject: [PATCH 4/8] Remove unsafe raw pointers to the node tree --- packages/blitz-dom/src/document.rs | 62 ++++-- packages/blitz-dom/src/layout/construct.rs | 3 +- packages/blitz-dom/src/node.rs | 88 +++----- packages/blitz-dom/src/stylo.rs | 198 ++++++++++++------ packages/blitz-dom/src/util.rs | 3 +- .../src/renderer/render.rs | 71 ++++--- .../src/documents/dioxus_document.rs | 17 +- 7 files changed, 269 insertions(+), 173 deletions(-) diff --git a/packages/blitz-dom/src/document.rs b/packages/blitz-dom/src/document.rs index f299aa11d..af9477aaa 100644 --- a/packages/blitz-dom/src/document.rs +++ b/packages/blitz-dom/src/document.rs @@ -1,5 +1,6 @@ use crate::events::{EventData, HitResult, RendererEvent}; use crate::node::{ImageData, NodeSpecificData, TextBrush}; +use crate::stylo::BlitzNode; use crate::{ElementNodeData, Node, NodeData, TextNodeData, Viewport}; use app_units::Au; use html5ever::local_name; @@ -510,24 +511,32 @@ impl Document { } pub fn try_root_element(&self) -> Option<&Node> { - TDocument::as_node(&self.root_node()).first_element_child() + TDocument::as_node(&BlitzNode { + node: self.root_node(), + tree: self.tree(), + }) + .first_element_child() + .map(|blitz_node| blitz_node.node) } pub fn root_element(&self) -> &Node { - TDocument::as_node(&self.root_node()) - .first_element_child() - .unwrap() - .as_element() - .unwrap() + TDocument::as_node(&BlitzNode { + node: self.root_node(), + tree: self.tree(), + }) + .first_element_child() + .unwrap() + .as_element() + .unwrap() + .node } pub fn create_node(&mut self, node_data: NodeData) -> usize { - let slab_ptr = self.nodes.as_mut() as *mut Slab; let entry = self.nodes.vacant_entry(); let id = entry.key(); let guard = self.guard.clone(); - entry.insert(Node::new(slab_ptr, id, guard, node_data)); + entry.insert(Node::new(id, guard, node_data)); // self.quadtree.insert( // AreaBuilder::default() @@ -761,8 +770,13 @@ impl Document { } pub fn snapshot_node(&mut self, node_id: usize) { + let node = &self.nodes[node_id]; + let opaque_node_id = TNode::opaque(&BlitzNode { + node, + tree: &self.nodes, + }); + let node = &mut self.nodes[node_id]; - let opaque_node_id = TNode::opaque(&&*node); node.has_snapshot = true; node.snapshot_handled .store(false, std::sync::atomic::Ordering::SeqCst); @@ -827,9 +841,12 @@ impl Document { /// Restyle the tree and then relayout it pub fn resolve(&mut self) { - if TDocument::as_node(&&self.nodes[0]) - .first_element_child() - .is_none() + if TDocument::as_node(&BlitzNode { + node: &self.nodes[0], + tree: self.tree(), + }) + .first_element_child() + .is_none() { println!("No DOM - not resolving"); return; @@ -850,9 +867,12 @@ impl Document { // Takes (x, y) co-ordinates (relative to the ) pub fn hit(&self, x: f32, y: f32) -> Option { - if TDocument::as_node(&&self.nodes[0]) - .first_element_child() - .is_none() + if TDocument::as_node(&BlitzNode { + node: &self.nodes[0], + tree: self.tree(), + }) + .first_element_child() + .is_none() { println!("No DOM - not resolving"); return None; @@ -872,21 +892,27 @@ impl Document { &self.nodes[node_id] } // Next is next sibling or parent - else if let Some(parent) = node.parent_node() { + else if let Some(parent) = (BlitzNode { + node, + tree: self.tree(), + }) + .parent_node() + { let self_idx = parent + .node .children .iter() .position(|id| *id == node.id) .unwrap(); // Next is next sibling - if let Some(sibling_id) = parent.children.get(self_idx + 1) { + if let Some(sibling_id) = parent.node.children.get(self_idx + 1) { look_in_children = true; &self.nodes[*sibling_id] } // Next is parent else { look_in_children = false; - node = parent; + node = parent.node; continue; } } diff --git a/packages/blitz-dom/src/layout/construct.rs b/packages/blitz-dom/src/layout/construct.rs index 48edbad4c..eeb36d017 100644 --- a/packages/blitz-dom/src/layout/construct.rs +++ b/packages/blitz-dom/src/layout/construct.rs @@ -22,6 +22,7 @@ use crate::{ ListItemLayout, ListItemLayoutPosition, Marker, NodeKind, NodeSpecificData, TextBrush, TextInputData, TextLayout, }, + stylo::BlitzNode, stylo_to_parley, Document, ElementNodeData, Node, NodeData, }; @@ -454,7 +455,7 @@ fn collect_complex_layout_children( let parent_style = doc.nodes[container_node_id].primary_styles().unwrap(); let read_guard = doc.guard.read(); let guards = StylesheetGuards::same(&read_guard); - let style = doc.stylist.style_for_anonymous::<&Node>( + let style = doc.stylist.style_for_anonymous::( &guards, &PseudoElement::ServoAnonymousBox, &parent_style, diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index 31ee74dec..c6034dfa7 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -48,7 +48,7 @@ pub struct Node { /// # Safety /// This is a raw pointer to the slab containing this node. /// By using this pointer you must gurantee that this node outlives the tree. - pub tree: *mut Slab, + //pub tree: *mut Slab, /// This node's ID. pub id: usize, @@ -127,10 +127,8 @@ impl Node { /// /// # Safety /// `tree` must outlive this `Node`. - pub fn new(tree: *mut Slab, id: usize, guard: SharedRwLock, data: NodeData) -> Self { + pub fn new(id: usize, guard: SharedRwLock, data: NodeData) -> Self { Self { - tree, - id, parent: None, children: vec![], @@ -185,10 +183,11 @@ impl Node { DisplayOutside::Block => true, _ => { if display.inside() == DisplayInside::Flow { - self.children - .iter() - .copied() - .any(|child_id| self.tree()[child_id].is_or_contains_block()) + /*self.children + .iter() + .copied() + .any(|child_id| self.tree()[child_id].is_or_contains_block())*/ + todo!() } else { false } @@ -687,15 +686,6 @@ impl TextNodeData { // } impl Node { - pub fn tree(&self) -> &Slab { - unsafe { &*self.tree } - } - - #[track_caller] - pub fn with(&self, id: usize) -> &Node { - self.tree().get(id).unwrap() - } - pub fn print_tree(&self, level: usize) { println!( "{} {} {:?} {} {:?}", @@ -707,38 +697,19 @@ impl Node { ); // println!("{} {:?}", " ".repeat(level), self.children); for child_id in self.children.iter() { - let child = self.with(*child_id); - child.print_tree(level + 1) + //let child = self.with(*child_id); + todo!(); + //child.print_tree(level + 1) } } // Get the index of the current node in the parents child list pub fn child_index(&self) -> Option { - self.tree()[self.parent?] - .children - .iter() - .position(|id| *id == self.id) - } - - // Get the nth node in the parents child list - pub fn forward(&self, n: usize) -> Option<&Node> { - let child_idx = self.child_index().unwrap_or(0); - self.tree()[self.parent?] - .children - .get(child_idx + n) - .map(|id| self.with(*id)) - } - - pub fn backward(&self, n: usize) -> Option<&Node> { - let child_idx = self.child_index().unwrap_or(0); - if child_idx < n { - return None; - } - - self.tree()[self.parent?] - .children - .get(child_idx - n) - .map(|id| self.with(*id)) + /*self.tree()[self.parent?] + .children + .iter() + .position(|id| *id == self.id)*/ + todo!() } pub fn is_element(&self) -> bool { @@ -858,7 +829,8 @@ impl Node { } NodeData::Element(..) | NodeData::AnonymousBlock(..) => { for child_id in self.children.iter() { - self.with(*child_id).write_text_content(out); + // self.with(*child_id).write_text_content(out); + todo!() } } _ => {} @@ -893,6 +865,7 @@ impl Node { let y = y - self.final_layout.location.y + self.scroll_offset.y as f32; let size = self.final_layout.size; + /* if x < 0.0 || x > size.width + self.scroll_offset.x as f32 || y < 0.0 @@ -900,18 +873,20 @@ impl Node { { return None; } + */ + todo!() // Call `.hit()` on each child in turn. If any return `Some` then return that value. Else return `Some(self.id). - self.layout_children - .borrow() - .iter() - .flatten() - .find_map(|&i| self.with(i).hit(x, y)) - .or(Some(HitResult { - node_id: self.id, - x, - y, - })) + /*self.layout_children + .borrow() + .iter() + .flatten() + .find_map(|&i| self.with(i).hit(x, y)) + .or(Some(HitResult { + node_id: self.id, + x, + y, + }))*/ } /// Computes the Document-relative coordinates of the Node @@ -919,11 +894,14 @@ impl Node { let x = x + self.final_layout.location.x - self.scroll_offset.x as f32; let y = y + self.final_layout.location.y - self.scroll_offset.y as f32; + /* // Recurse up the layout hierarchy self.layout_parent .get() .map(|i| self.with(i).absolute_position(x, y)) .unwrap_or(taffy::Point { x, y }) + */ + todo!() } /// Creates a synteh diff --git a/packages/blitz-dom/src/stylo.rs b/packages/blitz-dom/src/stylo.rs index a058abdd0..d53eaaeaf 100644 --- a/packages/blitz-dom/src/stylo.rs +++ b/packages/blitz-dom/src/stylo.rs @@ -16,6 +16,7 @@ use selectors::{ sink::Push, Element, OpaqueElement, }; +use slab::Slab; use style::applicable_declarations::ApplicableDeclarationBlock; use style::color::AbsoluteColor; use style::invalidation::element::restyle_hints::RestyleHint; @@ -112,15 +113,18 @@ impl crate::document::Document { ua_or_user: &guard.read(), }; - let root = TDocument::as_node(&&self.nodes[0]) - .first_element_child() - .unwrap() - .as_element() - .unwrap(); + let root = TDocument::as_node(&BlitzNode { + node: &self.nodes[0], + tree: &self.nodes, + }) + .first_element_child() + .unwrap() + .as_element() + .unwrap(); // Force restyle all nodes // TODO: finer grained style invalidation - let mut stylo_element_data = root.stylo_element_data.borrow_mut(); + let mut stylo_element_data = root.node.stylo_element_data.borrow_mut(); if let Some(data) = &mut *stylo_element_data { data.hint |= RestyleHint::restyle_subtree(); data.hint |= RestyleHint::recascade_subtree(); @@ -146,7 +150,13 @@ impl crate::document::Document { // components/layout_2020/lib.rs:983 let root = self.root_element(); // dbg!(root); - let token = RecalcStyle::pre_traverse(root, &context); + let token = RecalcStyle::pre_traverse( + BlitzNode { + node: root, + tree: self.tree(), + }, + &context, + ); if token.should_traverse() { // Style the elements, resolving their data @@ -162,13 +172,50 @@ impl crate::document::Document { /// /// Since BlitzNodes are not persistent (IE we don't keep the pointers around between frames), we choose to just implement /// the tree structure in the nodes themselves, and temporarily give out pointers during the layout phase. -type BlitzNode<'a> = &'a Node; +#[derive(Clone, Copy, Debug)] +pub struct BlitzNode<'a> { + pub node: &'a Node, + pub tree: &'a Slab, +} + +impl BlitzNode<'_> { + pub fn forward(&self, n: usize) -> Option { + let child_idx = self.node.child_index().unwrap_or(0); + self.tree[self.node.parent?] + .children + .get(child_idx + n) + .map(|id| Self { + node: &self.tree[*id], + tree: self.tree, + }) + } + + pub fn backward(&self, n: usize) -> Option { + let child_idx = self.node.child_index().unwrap_or(0); + self.tree[self.node.parent?] + .children + .get(child_idx - n) + .map(|id| Self { + node: &self.tree[*id], + tree: self.tree, + }) + } +} + +impl PartialEq for BlitzNode<'_> { + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} + +// TODO (Matt) +impl Eq for BlitzNode<'_> {} impl<'a> TDocument for BlitzNode<'a> { - type ConcreteNode = BlitzNode<'a>; + type ConcreteNode = Self; fn as_node(&self) -> Self::ConcreteNode { - self + *self } fn is_html_document(&self) -> bool { @@ -180,25 +227,25 @@ impl<'a> TDocument for BlitzNode<'a> { } fn shared_lock(&self) -> &SharedRwLock { - &self.guard + &self.node.guard } } impl NodeInfo for BlitzNode<'_> { fn is_element(&self) -> bool { - Node::is_element(self) + self.node.is_element() } fn is_text_node(&self) -> bool { - Node::is_text_node(self) + self.node.is_text_node() } } impl<'a> TShadowRoot for BlitzNode<'a> { - type ConcreteNode = BlitzNode<'a>; + type ConcreteNode = Self; fn as_node(&self) -> Self::ConcreteNode { - self + *self } fn host(&self) -> ::ConcreteElement { @@ -220,27 +267,33 @@ impl<'a> TNode for BlitzNode<'a> { type ConcreteShadowRoot = BlitzNode<'a>; fn parent_node(&self) -> Option { - self.parent.map(|id| self.with(id)) + //self.parent.map(|id| self.with(id)) + todo!() } fn first_child(&self) -> Option { - self.children.first().map(|id| self.with(*id)) + //self.children.first().map(|id| self.with(*id)) + todo!() } fn last_child(&self) -> Option { - self.children.last().map(|id| self.with(*id)) + //self.children.last().map(|id| self.with(*id)) + todo!() } fn prev_sibling(&self) -> Option { - self.backward(1) + //self.backward(1) + todo!() } fn next_sibling(&self) -> Option { - self.forward(1) + //self.forward(1) + todo!() } fn owner_doc(&self) -> Self::ConcreteDocument { - self.with(1) + //self.with(1) + todo!() } fn is_in_document(&self) -> bool { @@ -256,23 +309,23 @@ impl<'a> TNode for BlitzNode<'a> { } fn opaque(&self) -> OpaqueNode { - OpaqueNode(self.id) + OpaqueNode(self.node.id) } fn debug_id(self) -> usize { - self.id + self.node.id } fn as_element(&self) -> Option { - match self.raw_dom_data { - NodeData::Element { .. } => Some(self), + match self.node.raw_dom_data { + NodeData::Element { .. } => Some(*self), _ => None, } } fn as_document(&self) -> Option { - match self.raw_dom_data { - NodeData::Document { .. } => Some(self), + match self.node.raw_dom_data { + NodeData::Document { .. } => Some(*self), _ => None, } } @@ -304,7 +357,7 @@ impl selectors::Element for BlitzNode<'_> { } fn is_pseudo_element(&self) -> bool { - matches!(self.raw_dom_data, NodeData::AnonymousBlock(_)) + matches!(self.node.raw_dom_data, NodeData::AnonymousBlock(_)) } // These methods are implemented naively since we only threaded real nodes and not fake nodes @@ -343,11 +396,11 @@ impl selectors::Element for BlitzNode<'_> { } fn has_local_name(&self, local_name: &LocalName) -> bool { - self.raw_dom_data.is_element_with_tag_name(local_name) + self.node.raw_dom_data.is_element_with_tag_name(local_name) } fn has_namespace(&self, ns: &Namespace) -> bool { - self.element_data().expect("Not an element").name.ns == *ns + self.node.element_data().expect("Not an element").name.ns == *ns } fn is_same_type(&self, _other: &Self) -> bool { @@ -362,7 +415,7 @@ impl selectors::Element for BlitzNode<'_> { local_name: &GenericAtomIdent, operation: &AttrSelectorOperation<&AtomString>, ) -> bool { - let Some(attr_value) = self.raw_dom_data.attr(local_name.0.clone()) else { + let Some(attr_value) = self.node.raw_dom_data.attr(local_name.0.clone()) else { return false; }; @@ -404,6 +457,7 @@ impl selectors::Element for BlitzNode<'_> { match *pseudo_class { NonTSPseudoClass::Active => false, NonTSPseudoClass::AnyLink => self + .node .raw_dom_data .downcast_element() .map(|elem| { @@ -412,6 +466,7 @@ impl selectors::Element for BlitzNode<'_> { }) .unwrap_or(false), NonTSPseudoClass::Checked => self + .node .raw_dom_data .downcast_element() .and_then(|elem| elem.checkbox_input_checked()) @@ -421,15 +476,16 @@ impl selectors::Element for BlitzNode<'_> { NonTSPseudoClass::Defined => false, NonTSPseudoClass::Disabled => false, NonTSPseudoClass::Enabled => false, - NonTSPseudoClass::Focus => self.element_state.contains(ElementState::FOCUS), + NonTSPseudoClass::Focus => self.node.element_state.contains(ElementState::FOCUS), NonTSPseudoClass::FocusWithin => false, NonTSPseudoClass::FocusVisible => false, NonTSPseudoClass::Fullscreen => false, - NonTSPseudoClass::Hover => self.element_state.contains(ElementState::HOVER), + NonTSPseudoClass::Hover => self.node.element_state.contains(ElementState::HOVER), NonTSPseudoClass::Indeterminate => false, NonTSPseudoClass::Lang(_) => false, NonTSPseudoClass::CustomState(_) => false, NonTSPseudoClass::Link => self + .node .raw_dom_data .downcast_element() .map(|elem| { @@ -462,7 +518,7 @@ impl selectors::Element for BlitzNode<'_> { pe: &PseudoElement, _context: &mut MatchingContext, ) -> bool { - match self.raw_dom_data { + match self.node.raw_dom_data { NodeData::AnonymousBlock(_) => *pe == PseudoElement::ServoAnonymousBox, _ => false, } @@ -472,20 +528,21 @@ impl selectors::Element for BlitzNode<'_> { // Handle flags that apply to the element. let self_flags = flags.for_self(); if !self_flags.is_empty() { - *self.selector_flags.borrow_mut() |= self_flags; + *self.node.selector_flags.borrow_mut() |= self_flags; } // Handle flags that apply to the parent. let parent_flags = flags.for_parent(); if !parent_flags.is_empty() { if let Some(parent) = self.parent_node() { - *parent.selector_flags.borrow_mut() |= self_flags; + *parent.node.selector_flags.borrow_mut() |= self_flags; } } } fn is_link(&self) -> bool { - self.raw_dom_data + self.node + .raw_dom_data .is_element_with_tag_name(&local_name!("a")) } @@ -498,7 +555,8 @@ impl selectors::Element for BlitzNode<'_> { id: &::Identifier, case_sensitivity: selectors::attr::CaseSensitivity, ) -> bool { - self.element_data() + self.node + .element_data() .and_then(|data| data.id.as_ref()) .map(|id_attr| case_sensitivity.eq_atom(id_attr, id)) .unwrap_or(false) @@ -509,7 +567,7 @@ impl selectors::Element for BlitzNode<'_> { search_name: &::Identifier, case_sensitivity: selectors::attr::CaseSensitivity, ) -> bool { - let class_attr = self.raw_dom_data.attr(local_name!("class")); + let class_attr = self.node.raw_dom_data.attr(local_name!("class")); if let Some(class_attr) = class_attr { // split the class attribute for pheme in class_attr.split_ascii_whitespace() { @@ -562,19 +620,19 @@ impl<'a> TElement for BlitzNode<'a> { type TraversalChildrenIterator = Traverser<'a>; fn as_node(&self) -> Self::ConcreteNode { - self + *self } fn unopaque(opaque: OpaqueElement) -> Self { // FIXME: this is wrong in the case where pushing new elements casuses reallocations. // We should see if selectors will accept a PR that allows creation from a usize - unsafe { &*opaque.as_const_ptr() } + unsafe { *opaque.as_const_ptr() } } fn traversal_children(&self) -> style::dom::LayoutIterator { LayoutIterator(Traverser { // dom: self.tree(), - parent: self, + parent: *self, child_index: 0, }) } @@ -594,7 +652,8 @@ impl<'a> TElement for BlitzNode<'a> { } fn style_attribute(&self) -> Option>> { - self.element_data() + self.node + .element_data() .expect("Not an element") .style_attribute .as_ref() @@ -616,7 +675,7 @@ impl<'a> TElement for BlitzNode<'a> { } fn state(&self) -> ElementState { - self.element_state + self.node.element_state } fn has_part_attr(&self) -> bool { @@ -628,14 +687,14 @@ impl<'a> TElement for BlitzNode<'a> { } fn id(&self) -> Option<&style::Atom> { - self.element_data().and_then(|data| data.id.as_ref()) + self.node.element_data().and_then(|data| data.id.as_ref()) } fn each_class(&self, mut callback: F) where F: FnMut(&style::values::AtomIdent), { - let class_attr = self.raw_dom_data.attr(local_name!("class")); + let class_attr = self.node.raw_dom_data.attr(local_name!("class")); if let Some(class_attr) = class_attr { // split the class attribute for pheme in class_attr.split_ascii_whitespace() { @@ -649,7 +708,7 @@ impl<'a> TElement for BlitzNode<'a> { where F: FnMut(&style::LocalName), { - if let Some(attrs) = self.raw_dom_data.attrs() { + if let Some(attrs) = self.node.raw_dom_data.attrs() { for attr in attrs.iter() { callback(&GenericAtomIdent(attr.name.local.clone())); } @@ -661,15 +720,15 @@ impl<'a> TElement for BlitzNode<'a> { } fn has_snapshot(&self) -> bool { - self.has_snapshot + self.node.has_snapshot } fn handled_snapshot(&self) -> bool { - self.snapshot_handled.load(Ordering::SeqCst) + self.node.snapshot_handled.load(Ordering::SeqCst) } unsafe fn set_handled_snapshot(&self) { - self.snapshot_handled.store(true, Ordering::SeqCst); + self.node.snapshot_handled.store(true, Ordering::SeqCst); } unsafe fn set_dirty_descendants(&self) {} @@ -685,7 +744,7 @@ impl<'a> TElement for BlitzNode<'a> { } unsafe fn ensure_data(&self) -> AtomicRefMut { - let mut stylo_data = self.stylo_element_data.borrow_mut(); + let mut stylo_data = self.node.stylo_element_data.borrow_mut(); if stylo_data.is_none() { *stylo_data = Some(Default::default()); } @@ -693,15 +752,15 @@ impl<'a> TElement for BlitzNode<'a> { } unsafe fn clear_data(&self) { - *self.stylo_element_data.borrow_mut() = None; + *self.node.stylo_element_data.borrow_mut() = None; } fn has_data(&self) -> bool { - self.stylo_element_data.borrow().is_some() + self.node.stylo_element_data.borrow().is_some() } fn borrow_data(&self) -> Option> { - let stylo_data = self.stylo_element_data.borrow(); + let stylo_data = self.node.stylo_element_data.borrow(); if stylo_data.is_some() { Some(AtomicRef::map(stylo_data, |sd| sd.as_ref().unwrap())) } else { @@ -710,7 +769,7 @@ impl<'a> TElement for BlitzNode<'a> { } fn mutate_data(&self) -> Option> { - let stylo_data = self.stylo_element_data.borrow_mut(); + let stylo_data = self.node.stylo_element_data.borrow_mut(); if stylo_data.is_some() { Some(AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap())) } else { @@ -769,6 +828,7 @@ impl<'a> TElement for BlitzNode<'a> { fn is_html_document_body_element(&self) -> bool { // Check node is a element let is_body_element = self + .node .raw_dom_data .is_element_with_tag_name(&local_name!("body")); @@ -777,12 +837,15 @@ impl<'a> TElement for BlitzNode<'a> { return false; } + /* // If it is then check if it is a child of the root () element let root_node = &self.tree()[0]; let root_element = TDocument::as_node(&root_node) .first_element_child() .unwrap(); root_element.children.contains(&self.id) + */ + todo!() } fn synthesize_presentational_hints_for_legacy_attributes( @@ -792,14 +855,15 @@ impl<'a> TElement for BlitzNode<'a> { ) where V: Push, { - let Some(elem) = self.raw_dom_data.downcast_element() else { + let Some(elem) = self.node.raw_dom_data.downcast_element() else { return; }; let mut push_style = |decl: PropertyDeclaration| { hints.push(ApplicableDeclarationBlock::from_declarations( Arc::new( - self.guard + self.node + .guard .wrap(PropertyDeclarationBlock::with_one(decl, Importance::Normal)), ), CascadeLevel::PresHints, @@ -898,11 +962,11 @@ impl<'a> TElement for BlitzNode<'a> { } fn local_name(&self) -> &LocalName { - &self.element_data().expect("Not an element").name.local + &self.node.element_data().expect("Not an element").name.local } fn namespace(&self) -> &Namespace { - &self.element_data().expect("Not an element").name.ns + &self.node.element_data().expect("Not an element").name.ns } fn query_container_size( @@ -921,11 +985,12 @@ impl<'a> TElement for BlitzNode<'a> { } fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool { - self.selector_flags.borrow().contains(flags) + self.node.selector_flags.borrow().contains(flags) } fn relative_selector_search_direction(&self) -> ElementSelectorFlags { - self.selector_flags + self.node + .selector_flags .borrow() .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING) } @@ -961,18 +1026,21 @@ impl<'a> Iterator for Traverser<'a> { type Item = BlitzNode<'a>; fn next(&mut self) -> Option { - let node_id = self.parent.children.get(self.child_index)?; - let node = self.parent.with(*node_id); + let node_id = self.parent.node.children.get(self.child_index)?; + let node = self.parent.tree.get(*node_id)?; self.child_index += 1; - Some(node) + Some(BlitzNode { + node, + tree: self.parent.tree, + }) } } impl std::hash::Hash for BlitzNode<'_> { fn hash(&self, state: &mut H) { - state.write_usize(self.id) + state.write_usize(self.node.id) } } diff --git a/packages/blitz-dom/src/util.rs b/packages/blitz-dom/src/util.rs index 3e082257b..de1b5df44 100644 --- a/packages/blitz-dom/src/util.rs +++ b/packages/blitz-dom/src/util.rs @@ -238,7 +238,8 @@ pub fn walk_tree(indent: usize, node: &Node) { if !node.children.is_empty() { for child_id in node.children.iter() { - walk_tree(indent + 2, node.with(*child_id)); + //walk_tree(indent + 2, node.with(*child_id)); + todo!() } if let NodeData::Element(data) = &node.raw_dom_data { diff --git a/packages/blitz-renderer-vello/src/renderer/render.rs b/packages/blitz-renderer-vello/src/renderer/render.rs index 4b7c8fdda..5ae0c6153 100644 --- a/packages/blitz-renderer-vello/src/renderer/render.rs +++ b/packages/blitz-renderer-vello/src/renderer/render.rs @@ -10,6 +10,7 @@ use blitz_dom::node::{ ListItemLayout, ListItemLayoutPosition, Marker, NodeData, TextBrush, TextInputData, TextNodeData, }; +use blitz_dom::stylo::BlitzNode; use blitz_dom::{local_name, Document, Node}; use parley::Line; @@ -338,31 +339,37 @@ impl VelloSceneGenerator<'_> { // - position, table, text, ui, // - custom_properties, writing_mode, rules, visited_style, flags, box_, column, counters, effects, // - inherited_box, inherited_table, inherited_text, inherited_ui, - let element = &self.dom.as_ref().tree()[node_id]; + let element = BlitzNode { + node: &self.dom.as_ref().tree()[node_id], + tree: self.dom.as_ref().tree(), + }; // Early return if the element is hidden - if matches!(element.style.display, taffy::Display::None) { + if matches!(element.node.style.display, taffy::Display::None) { return; } // Only draw elements with a style - if element.primary_styles().is_none() { + if element.node.primary_styles().is_none() { return; } // Hide elements with "hidden" attribute - if let Some("true" | "") = element.attr(local_name!("hidden")) { + if let Some("true" | "") = element.node.attr(local_name!("hidden")) { return; } // Hide inputs with type=hidden // Implemented here rather than using the style engine for performance reasons - if element.local_name() == "input" && element.attr(local_name!("type")) == Some("hidden") { + if element.local_name() == "input" + && element.node.attr(local_name!("type")) == Some("hidden") + { return; } // Hide elements with a visibility style other than visible if element + .node .primary_styles() .unwrap() .get_inherited_box() @@ -373,12 +380,12 @@ impl VelloSceneGenerator<'_> { } // We can't fully support opacity yet, but we can hide elements with opacity 0 - if element.primary_styles().unwrap().get_effects().opacity == 0.0 { + if element.node.primary_styles().unwrap().get_effects().opacity == 0.0 { return; } // TODO: account for overflow_x vs overflow_y - let styles = &element.primary_styles().unwrap(); + let styles = &element.node.primary_styles().unwrap(); let overflow_x = styles.get_box().overflow_x; let overflow_y = styles.get_box().overflow_y; let should_clip = @@ -392,7 +399,7 @@ impl VelloSceneGenerator<'_> { border, padding, .. - } = element.final_layout; + } = element.node.final_layout; let scaled_pb = (padding + border).map(f64::from); let pos = vello::kurbo::Point { x: pos.x + scaled_pb.left, @@ -432,16 +439,16 @@ impl VelloSceneGenerator<'_> { // Now that background has been drawn, offset pos and cx in order to draw our contents scrolled let pos = Point { - x: pos.x - element.scroll_offset.x, - y: pos.y - element.scroll_offset.y, + x: pos.x - element.node.scroll_offset.x, + y: pos.y - element.node.scroll_offset.y, }; cx.pos = Point { - x: cx.pos.x - element.scroll_offset.x, - y: cx.pos.y - element.scroll_offset.y, + x: cx.pos.x - element.node.scroll_offset.x, + y: cx.pos.y - element.node.scroll_offset.y, }; cx.transform = cx.transform.then_translate(Vec2 { - x: -element.scroll_offset.x, - y: -element.scroll_offset.y, + x: -element.node.scroll_offset.x, + y: -element.node.scroll_offset.y, }); cx.draw_image(scene); cx.draw_svg(scene); @@ -477,6 +484,7 @@ impl VelloSceneGenerator<'_> { let x_offset = -(layout.full_width() / layout.scale() + x_padding); //Align the marker with the baseline of the first line of text in the list item let y_offset = if let Some(text_layout) = &element + .node .raw_dom_data .downcast_element() .and_then(|el| el.inline_layout_data.as_ref()) @@ -498,9 +506,9 @@ impl VelloSceneGenerator<'_> { cx.stroke_text(scene, layout.lines(), pos); } - if element.is_inline_root { + if element.node.is_inline_root { let text_layout = &element - .raw_dom_data + .node .raw_dom_data .downcast_element() .unwrap() .inline_layout_data @@ -523,6 +531,7 @@ impl VelloSceneGenerator<'_> { } else { for child_id in cx .element + .node .layout_children .borrow() .as_ref() @@ -558,8 +567,9 @@ impl VelloSceneGenerator<'_> { } } - fn element_cx<'w>(&'w self, element: &'w Node, location: Point) -> ElementCx<'w> { + fn element_cx<'w>(&'w self, element: BlitzNode<'w>, location: Point) -> ElementCx<'w> { let style = element + .node .stylo_element_data .borrow() .as_ref() @@ -568,7 +578,7 @@ impl VelloSceneGenerator<'_> { ComputedValues::initial_values_with_font_override(Font::initial_values()).to_arc(), ); - let (layout, pos) = self.node_position(element.id, location); + let (layout, pos) = self.node_position(element.node.id, location); let scale = self.scale; // the bezpaths for every element are (potentially) cached (not yet, tbd) @@ -588,13 +598,19 @@ impl VelloSceneGenerator<'_> { element, transform, image: element + .node .element_data() .unwrap() .image_data() .map(|data| &*data.image), - svg: element.element_data().unwrap().svg_data(), - text_input: element.element_data().unwrap().text_input_data(), - list_item: element.element_data().unwrap().list_item_data.as_deref(), + svg: element.node.element_data().unwrap().svg_data(), + text_input: element.node.element_data().unwrap().text_input_data(), + list_item: element + .node + .element_data() + .unwrap() + .list_item_data + .as_deref(), devtools: &self.devtools, } } @@ -606,7 +622,7 @@ struct ElementCx<'a> { style: style::servo_arc::Arc, pos: Point, scale: f64, - element: &'a Node, + element: BlitzNode<'a>, transform: Affine, image: Option<&'a DynamicImage>, svg: Option<&'a usvg::Tree>, @@ -722,6 +738,7 @@ impl ElementCx<'_> { if let Some(image) = self.image { let mut resized_image = self .element + .node .element_data() .unwrap() .image_data() @@ -761,7 +778,7 @@ impl ElementCx<'_> { let shape = &self.frame.outer_rect; let stroke = Stroke::new(self.scale); - let stroke_color = match self.element.style.display { + let stroke_color = match self.element.node.style.display { taffy::Display::Block => Color::rgb(1.0, 0.0, 0.0), taffy::Display::Flex => Color::rgb(0.0, 1.0, 0.0), taffy::Display::Grid => Color::rgb(0.0, 0.0, 1.0), @@ -1547,16 +1564,20 @@ impl ElementCx<'_> { fn draw_input(&self, scene: &mut Scene) { if self.element.local_name() == "input" - && matches!(self.element.attr(local_name!("type")), Some("checkbox")) + && matches!( + self.element.node.attr(local_name!("type")), + Some("checkbox") + ) { let Some(checked) = self .element + .node .element_data() .and_then(|data| data.checkbox_input_checked()) else { return; }; - let disabled = self.element.attr(local_name!("disabled")).is_some(); + let disabled = self.element.node.attr(local_name!("disabled")).is_some(); // TODO this should be coming from css accent-color, but I couldn't find how to retrieve it let accent_color = if disabled { diff --git a/packages/dioxus-native/src/documents/dioxus_document.rs b/packages/dioxus-native/src/documents/dioxus_document.rs index d2373eb6d..49a1f9587 100644 --- a/packages/dioxus-native/src/documents/dioxus_document.rs +++ b/packages/dioxus-native/src/documents/dioxus_document.rs @@ -10,8 +10,9 @@ use blitz_dom::{ events::{EventData, RendererEvent}, local_name, namespace_url, node::{Attribute, NodeSpecificData}, - ns, Atom, Document, DocumentLike, ElementNodeData, Node, NodeData, QualName, Viewport, - DEFAULT_CSS, + ns, + stylo::BlitzNode, + Atom, Document, DocumentLike, ElementNodeData, Node, NodeData, QualName, Viewport, DEFAULT_CSS, }; use dioxus::{ @@ -687,12 +688,12 @@ impl WriteMutations for MutationWriter<'_> { let new_nodes = self.state.stack.split_off(self.state.stack.len() - m); let anchor_node_id = self.state.element_to_node_id(id); - let next_sibling_id = self - .doc - .get_node(anchor_node_id) - .unwrap() - .forward(1) - .map(|node| node.id); + let next_sibling_id = BlitzNode { + node: self.doc.get_node(anchor_node_id).unwrap(), + tree: self.doc.as_ref().tree(), + } + .forward(1) + .map(|blitz_node| blitz_node.node.id); match next_sibling_id { Some(anchor_node_id) => { From 86d3f2c219d3ad4abe2fe02e7ab1920f802fc773 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 20:12:36 -0400 Subject: [PATCH 5/8] Rename BlitzNode to Handle and migrate methods from Node --- packages/blitz-dom/src/document.rs | 20 +-- packages/blitz-dom/src/layout/construct.rs | 4 +- packages/blitz-dom/src/node.rs | 65 +--------- packages/blitz-dom/src/stylo.rs | 122 ++++++++++++++---- .../src/renderer/render.rs | 8 +- .../src/documents/dioxus_document.rs | 14 +- 6 files changed, 122 insertions(+), 111 deletions(-) diff --git a/packages/blitz-dom/src/document.rs b/packages/blitz-dom/src/document.rs index af9477aaa..bd9142da7 100644 --- a/packages/blitz-dom/src/document.rs +++ b/packages/blitz-dom/src/document.rs @@ -1,6 +1,6 @@ use crate::events::{EventData, HitResult, RendererEvent}; use crate::node::{ImageData, NodeSpecificData, TextBrush}; -use crate::stylo::BlitzNode; +use crate::stylo::Handle; use crate::{ElementNodeData, Node, NodeData, TextNodeData, Viewport}; use app_units::Au; use html5ever::local_name; @@ -511,7 +511,7 @@ impl Document { } pub fn try_root_element(&self) -> Option<&Node> { - TDocument::as_node(&BlitzNode { + TDocument::as_node(&Handle { node: self.root_node(), tree: self.tree(), }) @@ -520,7 +520,7 @@ impl Document { } pub fn root_element(&self) -> &Node { - TDocument::as_node(&BlitzNode { + TDocument::as_node(&Handle { node: self.root_node(), tree: self.tree(), }) @@ -771,7 +771,7 @@ impl Document { pub fn snapshot_node(&mut self, node_id: usize) { let node = &self.nodes[node_id]; - let opaque_node_id = TNode::opaque(&BlitzNode { + let opaque_node_id = TNode::opaque(&Handle { node, tree: &self.nodes, }); @@ -841,7 +841,7 @@ impl Document { /// Restyle the tree and then relayout it pub fn resolve(&mut self) { - if TDocument::as_node(&BlitzNode { + if TDocument::as_node(&Handle { node: &self.nodes[0], tree: self.tree(), }) @@ -867,7 +867,7 @@ impl Document { // Takes (x, y) co-ordinates (relative to the ) pub fn hit(&self, x: f32, y: f32) -> Option { - if TDocument::as_node(&BlitzNode { + if TDocument::as_node(&Handle { node: &self.nodes[0], tree: self.tree(), }) @@ -878,7 +878,11 @@ impl Document { return None; } - self.root_element().hit(x, y) + Handle { + node: self.root_element(), + tree: self.tree(), + } + .hit(x, y) } pub fn next_node(&self, start: &Node, mut filter: impl FnMut(&Node) -> bool) -> Option { @@ -892,7 +896,7 @@ impl Document { &self.nodes[node_id] } // Next is next sibling or parent - else if let Some(parent) = (BlitzNode { + else if let Some(parent) = (Handle { node, tree: self.tree(), }) diff --git a/packages/blitz-dom/src/layout/construct.rs b/packages/blitz-dom/src/layout/construct.rs index eeb36d017..228d78a47 100644 --- a/packages/blitz-dom/src/layout/construct.rs +++ b/packages/blitz-dom/src/layout/construct.rs @@ -22,7 +22,7 @@ use crate::{ ListItemLayout, ListItemLayoutPosition, Marker, NodeKind, NodeSpecificData, TextBrush, TextInputData, TextLayout, }, - stylo::BlitzNode, + stylo::Handle, stylo_to_parley, Document, ElementNodeData, Node, NodeData, }; @@ -455,7 +455,7 @@ fn collect_complex_layout_children( let parent_style = doc.nodes[container_node_id].primary_styles().unwrap(); let read_guard = doc.guard.read(); let guards = StylesheetGuards::same(&read_guard); - let style = doc.stylist.style_for_anonymous::( + let style = doc.stylist.style_for_anonymous::( &guards, &PseudoElement::ServoAnonymousBox, &parent_style, diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index c6034dfa7..76a9db309 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -4,7 +4,6 @@ use image::DynamicImage; use parley::{FontContext, LayoutContext, PlainEditorOp}; use peniko::kurbo; use selectors::matching::{ElementSelectorFlags, QuirksMode}; -use slab::Slab; use std::cell::{Cell, RefCell}; use std::fmt::Write; use std::str::FromStr; @@ -29,9 +28,8 @@ use taffy::{ Cache, }; use url::Url; -use winit::event::Modifiers; -use crate::events::{EventData, EventListener, HitResult}; +use crate::events::EventListener; use crate::layout::table::TableContext; #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -851,67 +849,6 @@ impl Node { .map(|s| s.clone_order()) .unwrap_or(0) } - - /// Takes an (x, y) position (relative to the *parent's* top-left corner) and returns: - /// - None if the position is outside of this node's bounds - /// - Some(HitResult) if the position is within the node but doesn't match any children - /// - The result of recursively calling child.hit() on the the child element that is - /// positioned at that position if there is one. - /// - /// TODO: z-index - /// (If multiple children are positioned at the position then a random one will be recursed into) - pub fn hit(&self, x: f32, y: f32) -> Option { - let x = x - self.final_layout.location.x + self.scroll_offset.x as f32; - let y = y - self.final_layout.location.y + self.scroll_offset.y as f32; - - let size = self.final_layout.size; - /* - if x < 0.0 - || x > size.width + self.scroll_offset.x as f32 - || y < 0.0 - || y > size.height + self.scroll_offset.y as f32 - { - return None; - } - */ - todo!() - - // Call `.hit()` on each child in turn. If any return `Some` then return that value. Else return `Some(self.id). - /*self.layout_children - .borrow() - .iter() - .flatten() - .find_map(|&i| self.with(i).hit(x, y)) - .or(Some(HitResult { - node_id: self.id, - x, - y, - }))*/ - } - - /// Computes the Document-relative coordinates of the Node - pub fn absolute_position(&self, x: f32, y: f32) -> taffy::Point { - let x = x + self.final_layout.location.x - self.scroll_offset.x as f32; - let y = y + self.final_layout.location.y - self.scroll_offset.y as f32; - - /* - // Recurse up the layout hierarchy - self.layout_parent - .get() - .map(|i| self.with(i).absolute_position(x, y)) - .unwrap_or(taffy::Point { x, y }) - */ - todo!() - } - - /// Creates a synteh - pub fn synthetic_click_event(&self, mods: Modifiers) -> EventData { - let absolute_position = self.absolute_position(0.0, 0.0); - let x = absolute_position.x + (self.final_layout.size.width / 2.0); - let y = absolute_position.y + (self.final_layout.size.height / 2.0); - - EventData::Click { x, y, mods } - } } /// It might be wrong to expose this since what does *equality* mean outside the dom? diff --git a/packages/blitz-dom/src/stylo.rs b/packages/blitz-dom/src/stylo.rs index d53eaaeaf..dd909d3b4 100644 --- a/packages/blitz-dom/src/stylo.rs +++ b/packages/blitz-dom/src/stylo.rs @@ -1,10 +1,10 @@ //! Enable the dom to participate in styling by servo //! -use std::sync::atomic::Ordering; - +use super::stylo_to_taffy; +use crate::events::EventData; +use crate::events::HitResult; use crate::node::Node; - use crate::node::NodeData; use atomic_refcell::{AtomicRef, AtomicRefMut}; use html5ever::LocalNameStaticSet; @@ -17,6 +17,7 @@ use selectors::{ Element, OpaqueElement, }; use slab::Slab; +use std::sync::atomic::Ordering; use style::applicable_declarations::ApplicableDeclarationBlock; use style::color::AbsoluteColor; use style::invalidation::element::restyle_hints::RestyleHint; @@ -24,6 +25,7 @@ use style::properties::{Importance, PropertyDeclaration}; use style::rule_tree::CascadeLevel; use style::selector_parser::PseudoElement; use style::stylesheets::layer_rule::LayerOrder; +use style::values::computed::text::TextAlign as StyloTextAlign; use style::values::computed::Percentage; use style::values::specified::box_::DisplayOutside; use style::values::AtomString; @@ -47,9 +49,7 @@ use style::{ Atom, }; use style_dom::ElementState; - -use super::stylo_to_taffy; -use style::values::computed::text::TextAlign as StyloTextAlign; +use winit::event::Modifiers; impl crate::document::Document { /// Walk the whole tree, converting styles to layout @@ -113,7 +113,7 @@ impl crate::document::Document { ua_or_user: &guard.read(), }; - let root = TDocument::as_node(&BlitzNode { + let root = TDocument::as_node(&Handle { node: &self.nodes[0], tree: &self.nodes, }) @@ -151,7 +151,7 @@ impl crate::document::Document { let root = self.root_element(); // dbg!(root); let token = RecalcStyle::pre_traverse( - BlitzNode { + Handle { node: root, tree: self.tree(), }, @@ -173,12 +173,19 @@ impl crate::document::Document { /// Since BlitzNodes are not persistent (IE we don't keep the pointers around between frames), we choose to just implement /// the tree structure in the nodes themselves, and temporarily give out pointers during the layout phase. #[derive(Clone, Copy, Debug)] -pub struct BlitzNode<'a> { +pub struct Handle<'a> { pub node: &'a Node, pub tree: &'a Slab, } -impl BlitzNode<'_> { +impl Handle<'_> { + pub fn get(&self, id: usize) -> Self { + Self { + node: &self.tree[id], + tree: self.tree, + } + } + pub fn forward(&self, n: usize) -> Option { let child_idx = self.node.child_index().unwrap_or(0); self.tree[self.node.parent?] @@ -200,18 +207,81 @@ impl BlitzNode<'_> { tree: self.tree, }) } + + /// Computes the Document-relative coordinates of the Node + pub fn absolute_position(&self, x: f32, y: f32) -> taffy::Point { + let x = x + self.node.final_layout.location.x - self.node.scroll_offset.x as f32; + let y = y + self.node.final_layout.location.y - self.node.scroll_offset.y as f32; + + self.node + .layout_parent + .get() + .map(|i| { + Self { + node: &self.tree[i], + tree: self.tree, + } + .absolute_position(x, y) + }) + .unwrap_or(taffy::Point { x, y }) + } + + /// Creates a synteh + pub fn synthetic_click_event(&self, mods: Modifiers) -> EventData { + let absolute_position = self.absolute_position(0.0, 0.0); + let x = absolute_position.x + (self.node.final_layout.size.width / 2.0); + let y = absolute_position.y + (self.node.final_layout.size.height / 2.0); + + EventData::Click { x, y, mods } + } + + /// Takes an (x, y) position (relative to the *parent's* top-left corner) and returns: + /// - None if the position is outside of this node's bounds + /// - Some(HitResult) if the position is within the node but doesn't match any children + /// - The result of recursively calling child.hit() on the the child element that is + /// positioned at that position if there is one. + /// + /// TODO: z-index + /// (If multiple children are positioned at the position then a random one will be recursed into) + pub fn hit(&self, x: f32, y: f32) -> Option { + let x = x - self.node.final_layout.location.x + self.node.scroll_offset.x as f32; + let y = y - self.node.final_layout.location.y + self.node.scroll_offset.y as f32; + + let size = self.node.final_layout.size; + + if x < 0.0 + || x > size.width + self.node.scroll_offset.x as f32 + || y < 0.0 + || y > size.height + self.node.scroll_offset.y as f32 + { + return None; + } + + // Call `.hit()` on each child in turn. If any return `Some` then return that value. Else return `Some(self.id). + self.node + .layout_children + .borrow() + .iter() + .flatten() + .find_map(|&id| self.get(id).hit(x, y)) + .or(Some(HitResult { + node_id: self.node.id, + x, + y, + })) + } } -impl PartialEq for BlitzNode<'_> { +impl PartialEq for Handle<'_> { fn eq(&self, other: &Self) -> bool { self.node == other.node } } // TODO (Matt) -impl Eq for BlitzNode<'_> {} +impl Eq for Handle<'_> {} -impl<'a> TDocument for BlitzNode<'a> { +impl<'a> TDocument for Handle<'a> { type ConcreteNode = Self; fn as_node(&self) -> Self::ConcreteNode { @@ -231,7 +301,7 @@ impl<'a> TDocument for BlitzNode<'a> { } } -impl NodeInfo for BlitzNode<'_> { +impl NodeInfo for Handle<'_> { fn is_element(&self) -> bool { self.node.is_element() } @@ -241,7 +311,7 @@ impl NodeInfo for BlitzNode<'_> { } } -impl<'a> TShadowRoot for BlitzNode<'a> { +impl<'a> TShadowRoot for Handle<'a> { type ConcreteNode = Self; fn as_node(&self) -> Self::ConcreteNode { @@ -261,10 +331,10 @@ impl<'a> TShadowRoot for BlitzNode<'a> { } // components/styleaapper.rs: -impl<'a> TNode for BlitzNode<'a> { - type ConcreteElement = BlitzNode<'a>; - type ConcreteDocument = BlitzNode<'a>; - type ConcreteShadowRoot = BlitzNode<'a>; +impl<'a> TNode for Handle<'a> { + type ConcreteElement = Handle<'a>; + type ConcreteDocument = Handle<'a>; + type ConcreteShadowRoot = Handle<'a>; fn parent_node(&self) -> Option { //self.parent.map(|id| self.with(id)) @@ -335,7 +405,7 @@ impl<'a> TNode for BlitzNode<'a> { } } -impl selectors::Element for BlitzNode<'_> { +impl selectors::Element for Handle<'_> { type Impl = SelectorImpl; fn opaque(&self) -> selectors::OpaqueElement { @@ -614,8 +684,8 @@ impl selectors::Element for BlitzNode<'_> { } } -impl<'a> TElement for BlitzNode<'a> { - type ConcreteNode = BlitzNode<'a>; +impl<'a> TElement for Handle<'a> { + type ConcreteNode = Handle<'a>; type TraversalChildrenIterator = Traverser<'a>; @@ -1018,12 +1088,12 @@ impl<'a> TElement for BlitzNode<'a> { pub struct Traverser<'a> { // dom: &'a Slab, - parent: BlitzNode<'a>, + parent: Handle<'a>, child_index: usize, } impl<'a> Iterator for Traverser<'a> { - type Item = BlitzNode<'a>; + type Item = Handle<'a>; fn next(&mut self) -> Option { let node_id = self.parent.node.children.get(self.child_index)?; @@ -1031,14 +1101,14 @@ impl<'a> Iterator for Traverser<'a> { self.child_index += 1; - Some(BlitzNode { + Some(Handle { node, tree: self.parent.tree, }) } } -impl std::hash::Hash for BlitzNode<'_> { +impl std::hash::Hash for Handle<'_> { fn hash(&self, state: &mut H) { state.write_usize(self.node.id) } diff --git a/packages/blitz-renderer-vello/src/renderer/render.rs b/packages/blitz-renderer-vello/src/renderer/render.rs index 5ae0c6153..22ed51b25 100644 --- a/packages/blitz-renderer-vello/src/renderer/render.rs +++ b/packages/blitz-renderer-vello/src/renderer/render.rs @@ -10,7 +10,7 @@ use blitz_dom::node::{ ListItemLayout, ListItemLayoutPosition, Marker, NodeData, TextBrush, TextInputData, TextNodeData, }; -use blitz_dom::stylo::BlitzNode; +use blitz_dom::stylo::Handle; use blitz_dom::{local_name, Document, Node}; use parley::Line; @@ -339,7 +339,7 @@ impl VelloSceneGenerator<'_> { // - position, table, text, ui, // - custom_properties, writing_mode, rules, visited_style, flags, box_, column, counters, effects, // - inherited_box, inherited_table, inherited_text, inherited_ui, - let element = BlitzNode { + let element = Handle { node: &self.dom.as_ref().tree()[node_id], tree: self.dom.as_ref().tree(), }; @@ -567,7 +567,7 @@ impl VelloSceneGenerator<'_> { } } - fn element_cx<'w>(&'w self, element: BlitzNode<'w>, location: Point) -> ElementCx<'w> { + fn element_cx<'w>(&'w self, element: Handle<'w>, location: Point) -> ElementCx<'w> { let style = element .node .stylo_element_data @@ -622,7 +622,7 @@ struct ElementCx<'a> { style: style::servo_arc::Arc, pos: Point, scale: f64, - element: BlitzNode<'a>, + element: Handle<'a>, transform: Affine, image: Option<&'a DynamicImage>, svg: Option<&'a usvg::Tree>, diff --git a/packages/dioxus-native/src/documents/dioxus_document.rs b/packages/dioxus-native/src/documents/dioxus_document.rs index 49a1f9587..8b277a892 100644 --- a/packages/dioxus-native/src/documents/dioxus_document.rs +++ b/packages/dioxus-native/src/documents/dioxus_document.rs @@ -11,7 +11,7 @@ use blitz_dom::{ local_name, namespace_url, node::{Attribute, NodeSpecificData}, ns, - stylo::BlitzNode, + stylo::Handle, Atom, Document, DocumentLike, ElementNodeData, Node, NodeData, QualName, Viewport, DEFAULT_CSS, }; @@ -179,11 +179,11 @@ impl DocumentLike for DioxusDocument { let &EventData::Click { mods, .. } = &renderer_event.data else { unreachable!(); }; - let input_click_data = self - .inner - .get_node(node_id) - .unwrap() - .synthetic_click_event(mods); + let input_click_data = Handle { + node: self.inner.get_node(node_id).unwrap(), + tree: self.inner.tree(), + } + .synthetic_click_event(mods); let default_event = RendererEvent { target: node_id, data: input_click_data, @@ -688,7 +688,7 @@ impl WriteMutations for MutationWriter<'_> { let new_nodes = self.state.stack.split_off(self.state.stack.len() - m); let anchor_node_id = self.state.element_to_node_id(id); - let next_sibling_id = BlitzNode { + let next_sibling_id = Handle { node: self.doc.get_node(anchor_node_id).unwrap(), tree: self.doc.as_ref().tree(), } From a6d7124fef6cc4d02878720b6b3ea063d7fc6ccf Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 20:22:07 -0400 Subject: [PATCH 6/8] Move Handle struct to new module --- packages/blitz-dom/src/document.rs | 14 +- packages/blitz-dom/src/handle.rs | 1109 +++++++++++++++++ packages/blitz-dom/src/layout/construct.rs | 22 +- packages/blitz-dom/src/lib.rs | 4 + packages/blitz-dom/src/node.rs | 21 - packages/blitz-dom/src/stylo.rs | 1040 +--------------- .../src/renderer/render.rs | 3 +- packages/dioxus-native/src/accessibility.rs | 18 +- .../src/documents/dioxus_document.rs | 5 +- 9 files changed, 1150 insertions(+), 1086 deletions(-) create mode 100644 packages/blitz-dom/src/handle.rs diff --git a/packages/blitz-dom/src/document.rs b/packages/blitz-dom/src/document.rs index bd9142da7..827b75db1 100644 --- a/packages/blitz-dom/src/document.rs +++ b/packages/blitz-dom/src/document.rs @@ -1,7 +1,6 @@ use crate::events::{EventData, HitResult, RendererEvent}; use crate::node::{ImageData, NodeSpecificData, TextBrush}; -use crate::stylo::Handle; -use crate::{ElementNodeData, Node, NodeData, TextNodeData, Viewport}; +use crate::{ElementNodeData, Handle, Node, NodeData, TextNodeData, Viewport}; use app_units::Au; use html5ever::local_name; use parley::PlainEditorOp; @@ -431,6 +430,13 @@ impl Document { ctx } + pub fn get(&self, id: usize) -> Option { + self.nodes.get(id).map(|node| Handle { + node, + tree: &self.nodes, + }) + } + /// Set base url for resolving linked resources (stylesheets, images, fonts, etc) pub fn set_base_url(&mut self, url: &str) { self.base_url = Url::parse(url).ok(); @@ -679,7 +685,7 @@ impl Document { } pub fn process_style_element(&mut self, target_id: usize) { - let css = self.nodes[target_id].text_content(); + let css = self.get(target_id).unwrap().text_content(); let css = html_escape::decode_html_entities(&css); let sheet = self.make_stylesheet(&css, Origin::Author); self.add_stylesheet_for_node(sheet, target_id); @@ -718,7 +724,7 @@ impl Document { } pub fn upsert_stylesheet_for_node(&mut self, node_id: usize) { - let raw_styles = self.nodes[node_id].text_content(); + let raw_styles = self.get(node_id).unwrap().text_content(); let sheet = self.make_stylesheet(raw_styles, Origin::Author); self.add_stylesheet_for_node(sheet, node_id); } diff --git a/packages/blitz-dom/src/handle.rs b/packages/blitz-dom/src/handle.rs new file mode 100644 index 000000000..22c1e2c40 --- /dev/null +++ b/packages/blitz-dom/src/handle.rs @@ -0,0 +1,1109 @@ +//! Enable the dom to participate in styling by servo +//! + +use super::stylo_to_taffy; +use crate::events::EventData; +use crate::events::HitResult; +use crate::node::Node; +use crate::node::NodeData; +use atomic_refcell::{AtomicRef, AtomicRefMut}; +use html5ever::LocalNameStaticSet; +use html5ever::NamespaceStaticSet; +use html5ever::{local_name, LocalName, Namespace}; +use selectors::{ + attr::{AttrSelectorOperation, AttrSelectorOperator, NamespaceConstraint}, + matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode}, + sink::Push, + Element, OpaqueElement, +}; +use slab::Slab; +use std::sync::atomic::Ordering; +use style::applicable_declarations::ApplicableDeclarationBlock; +use style::color::AbsoluteColor; +use style::invalidation::element::restyle_hints::RestyleHint; +use style::properties::{Importance, PropertyDeclaration}; +use style::rule_tree::CascadeLevel; +use style::selector_parser::PseudoElement; +use style::stylesheets::layer_rule::LayerOrder; +use style::values::computed::text::TextAlign as StyloTextAlign; +use style::values::computed::Percentage; +use style::values::specified::box_::DisplayOutside; +use style::values::AtomString; +use style::CaseSensitivityExt; +use style::{ + animation::DocumentAnimationSet, + context::{ + QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, + SharedStyleContext, StyleContext, + }, + dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot}, + global_style_data::GLOBAL_STYLE_DATA, + properties::PropertyDeclarationBlock, + selector_parser::{NonTSPseudoClass, SelectorImpl}, + servo_arc::{Arc, ArcBorrow}, + shared_lock::{Locked, SharedRwLock, StylesheetGuards}, + thread_state::ThreadState, + traversal::{DomTraversal, PerLevelTraversalData}, + traversal_flags::TraversalFlags, + values::{AtomIdent, GenericAtomIdent}, + Atom, +}; +use style_dom::ElementState; +use winit::event::Modifiers; + +/// A handle to a node that Servo's style traits are implemented against +/// +/// Since BlitzNodes are not persistent (IE we don't keep the pointers around between frames), we choose to just implement +/// the tree structure in the nodes themselves, and temporarily give out pointers during the layout phase. +#[derive(Clone, Copy, Debug)] +pub struct Handle<'a> { + pub node: &'a Node, + pub tree: &'a Slab, +} + +impl Handle<'_> { + pub fn get(&self, id: usize) -> Self { + Self { + node: &self.tree[id], + tree: self.tree, + } + } + + pub fn forward(&self, n: usize) -> Option { + let child_idx = self.node.child_index().unwrap_or(0); + self.tree[self.node.parent?] + .children + .get(child_idx + n) + .map(|id| Self { + node: &self.tree[*id], + tree: self.tree, + }) + } + + pub fn backward(&self, n: usize) -> Option { + let child_idx = self.node.child_index().unwrap_or(0); + self.tree[self.node.parent?] + .children + .get(child_idx - n) + .map(|id| Self { + node: &self.tree[*id], + tree: self.tree, + }) + } + + /// Computes the Document-relative coordinates of the Node + pub fn absolute_position(&self, x: f32, y: f32) -> taffy::Point { + let x = x + self.node.final_layout.location.x - self.node.scroll_offset.x as f32; + let y = y + self.node.final_layout.location.y - self.node.scroll_offset.y as f32; + + self.node + .layout_parent + .get() + .map(|i| { + Self { + node: &self.tree[i], + tree: self.tree, + } + .absolute_position(x, y) + }) + .unwrap_or(taffy::Point { x, y }) + } + + /// Creates a synteh + pub fn synthetic_click_event(&self, mods: Modifiers) -> EventData { + let absolute_position = self.absolute_position(0.0, 0.0); + let x = absolute_position.x + (self.node.final_layout.size.width / 2.0); + let y = absolute_position.y + (self.node.final_layout.size.height / 2.0); + + EventData::Click { x, y, mods } + } + + /// Takes an (x, y) position (relative to the *parent's* top-left corner) and returns: + /// - None if the position is outside of this node's bounds + /// - Some(HitResult) if the position is within the node but doesn't match any children + /// - The result of recursively calling child.hit() on the the child element that is + /// positioned at that position if there is one. + /// + /// TODO: z-index + /// (If multiple children are positioned at the position then a random one will be recursed into) + pub fn hit(&self, x: f32, y: f32) -> Option { + let x = x - self.node.final_layout.location.x + self.node.scroll_offset.x as f32; + let y = y - self.node.final_layout.location.y + self.node.scroll_offset.y as f32; + + let size = self.node.final_layout.size; + + if x < 0.0 + || x > size.width + self.node.scroll_offset.x as f32 + || y < 0.0 + || y > size.height + self.node.scroll_offset.y as f32 + { + return None; + } + + // Call `.hit()` on each child in turn. If any return `Some` then return that value. Else return `Some(self.id). + self.node + .layout_children + .borrow() + .iter() + .flatten() + .find_map(|&id| self.get(id).hit(x, y)) + .or(Some(HitResult { + node_id: self.node.id, + x, + y, + })) + } + + pub fn text_content(&self) -> String { + let mut out = String::new(); + self.write_text_content(&mut out); + out + } + + fn write_text_content(&self, out: &mut String) { + match &self.node.raw_dom_data { + NodeData::Text(data) => { + out.push_str(&data.content); + } + NodeData::Element(..) | NodeData::AnonymousBlock(..) => { + for child_id in &self.node.children { + self.get(*child_id).write_text_content(out); + } + } + _ => {} + } + } +} + +impl PartialEq for Handle<'_> { + fn eq(&self, other: &Self) -> bool { + self.node == other.node + } +} + +// TODO (Matt) +impl Eq for Handle<'_> {} + +impl<'a> TDocument for Handle<'a> { + type ConcreteNode = Self; + + fn as_node(&self) -> Self::ConcreteNode { + *self + } + + fn is_html_document(&self) -> bool { + true + } + + fn quirks_mode(&self) -> QuirksMode { + QuirksMode::NoQuirks + } + + fn shared_lock(&self) -> &SharedRwLock { + &self.node.guard + } +} + +impl NodeInfo for Handle<'_> { + fn is_element(&self) -> bool { + self.node.is_element() + } + + fn is_text_node(&self) -> bool { + self.node.is_text_node() + } +} + +impl<'a> TShadowRoot for Handle<'a> { + type ConcreteNode = Self; + + fn as_node(&self) -> Self::ConcreteNode { + *self + } + + fn host(&self) -> ::ConcreteElement { + todo!("Shadow roots not implemented") + } + + fn style_data<'b>(&self) -> Option<&'b style::stylist::CascadeData> + where + Self: 'b, + { + todo!("Shadow roots not implemented") + } +} + +// components/styleaapper.rs: +impl<'a> TNode for Handle<'a> { + type ConcreteElement = Handle<'a>; + type ConcreteDocument = Handle<'a>; + type ConcreteShadowRoot = Handle<'a>; + + fn parent_node(&self) -> Option { + //self.parent.map(|id| self.with(id)) + todo!() + } + + fn first_child(&self) -> Option { + //self.children.first().map(|id| self.with(*id)) + todo!() + } + + fn last_child(&self) -> Option { + //self.children.last().map(|id| self.with(*id)) + todo!() + } + + fn prev_sibling(&self) -> Option { + //self.backward(1) + todo!() + } + + fn next_sibling(&self) -> Option { + //self.forward(1) + todo!() + } + + fn owner_doc(&self) -> Self::ConcreteDocument { + //self.with(1) + todo!() + } + + fn is_in_document(&self) -> bool { + true + } + + // I think this is the same as parent_node only in the cases when the direct parent is not a real element, forcing us + // to travel upwards + // + // For the sake of this demo, we're just going to return the parent node ann + fn traversal_parent(&self) -> Option { + self.parent_node().and_then(|node| node.as_element()) + } + + fn opaque(&self) -> OpaqueNode { + OpaqueNode(self.node.id) + } + + fn debug_id(self) -> usize { + self.node.id + } + + fn as_element(&self) -> Option { + match self.node.raw_dom_data { + NodeData::Element { .. } => Some(*self), + _ => None, + } + } + + fn as_document(&self) -> Option { + match self.node.raw_dom_data { + NodeData::Document { .. } => Some(*self), + _ => None, + } + } + + fn as_shadow_root(&self) -> Option { + todo!("Shadow roots aren't real, yet") + } +} + +impl selectors::Element for Handle<'_> { + type Impl = SelectorImpl; + + fn opaque(&self) -> selectors::OpaqueElement { + // FIXME: this is wrong in the case where pushing new elements casuses reallocations. + // We should see if selectors will accept a PR that allows creation from a usize + OpaqueElement::new(self) + } + + fn parent_element(&self) -> Option { + TElement::traversal_parent(self) + } + + fn parent_node_is_shadow_root(&self) -> bool { + false + } + + fn containing_shadow_host(&self) -> Option { + None + } + + fn is_pseudo_element(&self) -> bool { + matches!(self.node.raw_dom_data, NodeData::AnonymousBlock(_)) + } + + // These methods are implemented naively since we only threaded real nodes and not fake nodes + // we should try and use `find` instead of this foward/backward stuff since its ugly and slow + fn prev_sibling_element(&self) -> Option { + let mut n = 1; + while let Some(node) = self.backward(n) { + if node.is_element() { + return Some(node); + } + n += 1; + } + + None + } + + fn next_sibling_element(&self) -> Option { + let mut n = 1; + while let Some(node) = self.forward(n) { + if node.is_element() { + return Some(node); + } + n += 1; + } + + None + } + + fn first_element_child(&self) -> Option { + let mut children = self.dom_children(); + children.find(|child| child.is_element()) + } + + fn is_html_element_in_html_document(&self) -> bool { + true // self.has_namespace(ns!(html)) + } + + fn has_local_name(&self, local_name: &LocalName) -> bool { + self.node.raw_dom_data.is_element_with_tag_name(local_name) + } + + fn has_namespace(&self, ns: &Namespace) -> bool { + self.node.element_data().expect("Not an element").name.ns == *ns + } + + fn is_same_type(&self, _other: &Self) -> bool { + // FIXME: implementing this correctly currently triggers a debug_assert ("Invalid cache") in selectors + //self.local_name() == other.local_name() && self.namespace() == other.namespace() + false + } + + fn attr_matches( + &self, + _ns: &NamespaceConstraint<&GenericAtomIdent>, + local_name: &GenericAtomIdent, + operation: &AttrSelectorOperation<&AtomString>, + ) -> bool { + let Some(attr_value) = self.node.raw_dom_data.attr(local_name.0.clone()) else { + return false; + }; + + match operation { + AttrSelectorOperation::Exists => true, + AttrSelectorOperation::WithValue { + operator, + case_sensitivity: _, + value, + } => { + let value = value.as_ref(); + + // TODO: case sensitivity + match operator { + AttrSelectorOperator::Equal => attr_value == value, + AttrSelectorOperator::Includes => attr_value + .split_ascii_whitespace() + .any(|word| word == value), + AttrSelectorOperator::DashMatch => { + // Represents elements with an attribute name of attr whose value can be exactly value + // or can begin with value immediately followed by a hyphen, - (U+002D) + attr_value.starts_with(value) + && (attr_value.len() == value.len() + || attr_value.chars().nth(value.len()) == Some('-')) + } + AttrSelectorOperator::Prefix => attr_value.starts_with(value), + AttrSelectorOperator::Substring => attr_value.contains(value), + AttrSelectorOperator::Suffix => attr_value.ends_with(value), + } + } + } + } + + fn match_non_ts_pseudo_class( + &self, + pseudo_class: &::NonTSPseudoClass, + _context: &mut MatchingContext, + ) -> bool { + match *pseudo_class { + NonTSPseudoClass::Active => false, + NonTSPseudoClass::AnyLink => self + .node + .raw_dom_data + .downcast_element() + .map(|elem| { + (elem.name.local == local_name!("a") || elem.name.local == local_name!("area")) + && elem.attr(local_name!("href")).is_some() + }) + .unwrap_or(false), + NonTSPseudoClass::Checked => self + .node + .raw_dom_data + .downcast_element() + .and_then(|elem| elem.checkbox_input_checked()) + .unwrap_or(false), + NonTSPseudoClass::Valid => false, + NonTSPseudoClass::Invalid => false, + NonTSPseudoClass::Defined => false, + NonTSPseudoClass::Disabled => false, + NonTSPseudoClass::Enabled => false, + NonTSPseudoClass::Focus => self.node.element_state.contains(ElementState::FOCUS), + NonTSPseudoClass::FocusWithin => false, + NonTSPseudoClass::FocusVisible => false, + NonTSPseudoClass::Fullscreen => false, + NonTSPseudoClass::Hover => self.node.element_state.contains(ElementState::HOVER), + NonTSPseudoClass::Indeterminate => false, + NonTSPseudoClass::Lang(_) => false, + NonTSPseudoClass::CustomState(_) => false, + NonTSPseudoClass::Link => self + .node + .raw_dom_data + .downcast_element() + .map(|elem| { + (elem.name.local == local_name!("a") || elem.name.local == local_name!("area")) + && elem.attr(local_name!("href")).is_some() + }) + .unwrap_or(false), + NonTSPseudoClass::PlaceholderShown => false, + NonTSPseudoClass::ReadWrite => false, + NonTSPseudoClass::ReadOnly => false, + NonTSPseudoClass::ServoNonZeroBorder => false, + NonTSPseudoClass::Target => false, + NonTSPseudoClass::Visited => false, + NonTSPseudoClass::Autofill => false, + NonTSPseudoClass::Default => false, + + NonTSPseudoClass::InRange => false, + NonTSPseudoClass::Modal => false, + NonTSPseudoClass::Optional => false, + NonTSPseudoClass::OutOfRange => false, + NonTSPseudoClass::PopoverOpen => false, + NonTSPseudoClass::Required => false, + NonTSPseudoClass::UserInvalid => false, + NonTSPseudoClass::UserValid => false, + } + } + + fn match_pseudo_element( + &self, + pe: &PseudoElement, + _context: &mut MatchingContext, + ) -> bool { + match self.node.raw_dom_data { + NodeData::AnonymousBlock(_) => *pe == PseudoElement::ServoAnonymousBox, + _ => false, + } + } + + fn apply_selector_flags(&self, flags: ElementSelectorFlags) { + // Handle flags that apply to the element. + let self_flags = flags.for_self(); + if !self_flags.is_empty() { + *self.node.selector_flags.borrow_mut() |= self_flags; + } + + // Handle flags that apply to the parent. + let parent_flags = flags.for_parent(); + if !parent_flags.is_empty() { + if let Some(parent) = self.parent_node() { + *parent.node.selector_flags.borrow_mut() |= self_flags; + } + } + } + + fn is_link(&self) -> bool { + self.node + .raw_dom_data + .is_element_with_tag_name(&local_name!("a")) + } + + fn is_html_slot_element(&self) -> bool { + false + } + + fn has_id( + &self, + id: &::Identifier, + case_sensitivity: selectors::attr::CaseSensitivity, + ) -> bool { + self.node + .element_data() + .and_then(|data| data.id.as_ref()) + .map(|id_attr| case_sensitivity.eq_atom(id_attr, id)) + .unwrap_or(false) + } + + fn has_class( + &self, + search_name: &::Identifier, + case_sensitivity: selectors::attr::CaseSensitivity, + ) -> bool { + let class_attr = self.node.raw_dom_data.attr(local_name!("class")); + if let Some(class_attr) = class_attr { + // split the class attribute + for pheme in class_attr.split_ascii_whitespace() { + let atom = Atom::from(pheme); + if case_sensitivity.eq_atom(&atom, search_name) { + return true; + } + } + } + + false + } + + fn imported_part( + &self, + _name: &::Identifier, + ) -> Option<::Identifier> { + None + } + + fn is_part(&self, _name: &::Identifier) -> bool { + false + } + + fn is_empty(&self) -> bool { + self.dom_children().next().is_none() + } + + fn is_root(&self) -> bool { + self.parent_node() + .and_then(|parent| parent.parent_node()) + .is_none() + } + + fn has_custom_state( + &self, + _name: &::Identifier, + ) -> bool { + false + } + + fn add_element_unique_hashes(&self, _filter: &mut selectors::bloom::BloomFilter) -> bool { + false + } +} + +impl<'a> TElement for Handle<'a> { + type ConcreteNode = Handle<'a>; + + type TraversalChildrenIterator = Traverser<'a>; + + fn as_node(&self) -> Self::ConcreteNode { + *self + } + + fn unopaque(opaque: OpaqueElement) -> Self { + // FIXME: this is wrong in the case where pushing new elements casuses reallocations. + // We should see if selectors will accept a PR that allows creation from a usize + unsafe { *opaque.as_const_ptr() } + } + + fn traversal_children(&self) -> style::dom::LayoutIterator { + LayoutIterator(Traverser { + // dom: self.tree(), + parent: *self, + child_index: 0, + }) + } + + fn is_html_element(&self) -> bool { + self.is_element() + } + + // not implemented..... + fn is_mathml_element(&self) -> bool { + false + } + + // need to check the namespace + fn is_svg_element(&self) -> bool { + false + } + + fn style_attribute(&self) -> Option>> { + self.node + .element_data() + .expect("Not an element") + .style_attribute + .as_ref() + .map(|f| f.borrow_arc()) + } + + fn animation_rule( + &self, + _: &SharedStyleContext, + ) -> Option>> { + None + } + + fn transition_rule( + &self, + _context: &SharedStyleContext, + ) -> Option>> { + None + } + + fn state(&self) -> ElementState { + self.node.element_state + } + + fn has_part_attr(&self) -> bool { + false + } + + fn exports_any_part(&self) -> bool { + false + } + + fn id(&self) -> Option<&style::Atom> { + self.node.element_data().and_then(|data| data.id.as_ref()) + } + + fn each_class(&self, mut callback: F) + where + F: FnMut(&style::values::AtomIdent), + { + let class_attr = self.node.raw_dom_data.attr(local_name!("class")); + if let Some(class_attr) = class_attr { + // split the class attribute + for pheme in class_attr.split_ascii_whitespace() { + let atom = Atom::from(pheme); // interns the string + callback(AtomIdent::cast(&atom)); + } + } + } + + fn each_attr_name(&self, mut callback: F) + where + F: FnMut(&style::LocalName), + { + if let Some(attrs) = self.node.raw_dom_data.attrs() { + for attr in attrs.iter() { + callback(&GenericAtomIdent(attr.name.local.clone())); + } + } + } + + fn has_dirty_descendants(&self) -> bool { + true + } + + fn has_snapshot(&self) -> bool { + self.node.has_snapshot + } + + fn handled_snapshot(&self) -> bool { + self.node.snapshot_handled.load(Ordering::SeqCst) + } + + unsafe fn set_handled_snapshot(&self) { + self.node.snapshot_handled.store(true, Ordering::SeqCst); + } + + unsafe fn set_dirty_descendants(&self) {} + + unsafe fn unset_dirty_descendants(&self) {} + + fn store_children_to_process(&self, _n: isize) { + unimplemented!() + } + + fn did_process_child(&self) -> isize { + unimplemented!() + } + + unsafe fn ensure_data(&self) -> AtomicRefMut { + let mut stylo_data = self.node.stylo_element_data.borrow_mut(); + if stylo_data.is_none() { + *stylo_data = Some(Default::default()); + } + AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap()) + } + + unsafe fn clear_data(&self) { + *self.node.stylo_element_data.borrow_mut() = None; + } + + fn has_data(&self) -> bool { + self.node.stylo_element_data.borrow().is_some() + } + + fn borrow_data(&self) -> Option> { + let stylo_data = self.node.stylo_element_data.borrow(); + if stylo_data.is_some() { + Some(AtomicRef::map(stylo_data, |sd| sd.as_ref().unwrap())) + } else { + None + } + } + + fn mutate_data(&self) -> Option> { + let stylo_data = self.node.stylo_element_data.borrow_mut(); + if stylo_data.is_some() { + Some(AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap())) + } else { + None + } + } + + fn skip_item_display_fixup(&self) -> bool { + false + } + + fn may_have_animations(&self) -> bool { + false + } + + fn has_animations(&self, _context: &SharedStyleContext) -> bool { + false + } + + fn has_css_animations( + &self, + _context: &SharedStyleContext, + _pseudo_element: Option, + ) -> bool { + false + } + + fn has_css_transitions( + &self, + _context: &SharedStyleContext, + _pseudo_element: Option, + ) -> bool { + false + } + + fn shadow_root(&self) -> Option<::ConcreteShadowRoot> { + None + } + + fn containing_shadow(&self) -> Option<::ConcreteShadowRoot> { + None + } + + fn lang_attr(&self) -> Option { + None + } + + fn match_element_lang( + &self, + _override_lang: Option>, + _value: &style::selector_parser::Lang, + ) -> bool { + false + } + + fn is_html_document_body_element(&self) -> bool { + // Check node is a element + let is_body_element = self + .node + .raw_dom_data + .is_element_with_tag_name(&local_name!("body")); + + // If it isn't then return early + if !is_body_element { + return false; + } + + /* + // If it is then check if it is a child of the root () element + let root_node = &self.tree()[0]; + let root_element = TDocument::as_node(&root_node) + .first_element_child() + .unwrap(); + root_element.children.contains(&self.id) + */ + todo!() + } + + fn synthesize_presentational_hints_for_legacy_attributes( + &self, + _visited_handling: VisitedHandlingMode, + hints: &mut V, + ) where + V: Push, + { + let Some(elem) = self.node.raw_dom_data.downcast_element() else { + return; + }; + + let mut push_style = |decl: PropertyDeclaration| { + hints.push(ApplicableDeclarationBlock::from_declarations( + Arc::new( + self.node + .guard + .wrap(PropertyDeclarationBlock::with_one(decl, Importance::Normal)), + ), + CascadeLevel::PresHints, + LayerOrder::root(), + )); + }; + + fn parse_color_attr(value: &str) -> Option<(u8, u8, u8, f32)> { + if !value.starts_with('#') { + return None; + } + + let value = &value[1..]; + if value.len() == 3 { + let r = u8::from_str_radix(&value[0..1], 16).ok()?; + let g = u8::from_str_radix(&value[1..2], 16).ok()?; + let b = u8::from_str_radix(&value[2..3], 16).ok()?; + return Some((r, g, b, 1.0)); + } + + if value.len() == 6 { + let r = u8::from_str_radix(&value[0..2], 16).ok()?; + let g = u8::from_str_radix(&value[2..4], 16).ok()?; + let b = u8::from_str_radix(&value[4..6], 16).ok()?; + return Some((r, g, b, 1.0)); + } + + None + } + + fn parse_size_attr(value: &str) -> Option { + use style::values::specified::{AbsoluteLength, LengthPercentage, NoCalcLength}; + if let Some(value) = value.strip_suffix("px") { + let val: f32 = value.parse().ok()?; + return Some(LengthPercentage::Length(NoCalcLength::Absolute( + AbsoluteLength::Px(val), + ))); + } + + if let Some(value) = value.strip_suffix("%") { + let val: f32 = value.parse().ok()?; + return Some(LengthPercentage::Percentage(Percentage(val / 100.0))); + } + + let val: f32 = value.parse().ok()?; + Some(LengthPercentage::Length(NoCalcLength::Absolute( + AbsoluteLength::Px(val), + ))) + } + + for attr in elem.attrs() { + let name = &attr.name.local; + let value = attr.value.as_str(); + + if *name == local_name!("align") { + use style::values::specified::TextAlign; + let keyword = match value { + "left" => Some(StyloTextAlign::MozLeft), + "right" => Some(StyloTextAlign::MozRight), + "center" => Some(StyloTextAlign::MozCenter), + _ => None, + }; + + if let Some(keyword) = keyword { + push_style(PropertyDeclaration::TextAlign(TextAlign::Keyword(keyword))); + } + } + + if *name == local_name!("width") { + if let Some(width) = parse_size_attr(value) { + use style::values::generics::{length::Size, NonNegative}; + push_style(PropertyDeclaration::Width(Size::LengthPercentage( + NonNegative(width), + ))); + } + } + + if *name == local_name!("height") { + if let Some(height) = parse_size_attr(value) { + use style::values::generics::{length::Size, NonNegative}; + push_style(PropertyDeclaration::Height(Size::LengthPercentage( + NonNegative(height), + ))); + } + } + + if *name == local_name!("bgcolor") { + use style::values::specified::Color; + if let Some((r, g, b, a)) = parse_color_attr(value) { + push_style(PropertyDeclaration::BackgroundColor( + Color::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a)), + )); + } + } + } + } + + fn local_name(&self) -> &LocalName { + &self.node.element_data().expect("Not an element").name.local + } + + fn namespace(&self) -> &Namespace { + &self.node.element_data().expect("Not an element").name.ns + } + + fn query_container_size( + &self, + _display: &style::values::specified::Display, + ) -> euclid::default::Size2D> { + // FIXME: Implement container queries. For now this effectively disables them without panicking. + Default::default() + } + + fn each_custom_state(&self, _callback: F) + where + F: FnMut(&AtomIdent), + { + todo!() + } + + fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool { + self.node.selector_flags.borrow().contains(flags) + } + + fn relative_selector_search_direction(&self) -> ElementSelectorFlags { + self.node + .selector_flags + .borrow() + .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING) + } + + // fn update_animations( + // &self, + // before_change_style: Option>, + // tasks: style::context::UpdateAnimationsTasks, + // ) { + // todo!() + // } + + // fn process_post_animation(&self, tasks: style::context::PostAnimationTasks) { + // todo!() + // } + + // fn needs_transitions_update( + // &self, + // before_change_style: &ComputedValues, + // after_change_style: &ComputedValues, + // ) -> bool { + // todo!() + // } +} + +pub struct Traverser<'a> { + // dom: &'a Slab, + parent: Handle<'a>, + child_index: usize, +} + +impl<'a> Iterator for Traverser<'a> { + type Item = Handle<'a>; + + fn next(&mut self) -> Option { + let node_id = self.parent.node.children.get(self.child_index)?; + let node = self.parent.tree.get(*node_id)?; + + self.child_index += 1; + + Some(Handle { + node, + tree: self.parent.tree, + }) + } +} + +impl std::hash::Hash for Handle<'_> { + fn hash(&self, state: &mut H) { + state.write_usize(self.node.id) + } +} + +/// Handle custom painters like images for layouting +/// +/// todo: actually implement this +pub struct RegisteredPaintersImpl; +impl RegisteredSpeculativePainters for RegisteredPaintersImpl { + fn get(&self, _name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> { + None + } +} + +use style::traversal::recalc_style_at; + +pub struct RecalcStyle<'a> { + context: SharedStyleContext<'a>, +} + +impl<'a> RecalcStyle<'a> { + pub fn new(context: SharedStyleContext<'a>) -> Self { + RecalcStyle { context } + } +} + +#[allow(unsafe_code)] +impl DomTraversal for RecalcStyle<'_> +where + E: TElement, +{ + fn process_preorder( + &self, + traversal_data: &PerLevelTraversalData, + context: &mut StyleContext, + node: E::ConcreteNode, + note_child: F, + ) { + // Don't process textnodees in this traversal + if node.is_text_node() { + return; + } + + let el = node.as_element().unwrap(); + // let mut data = el.mutate_data().unwrap(); + let mut data = unsafe { el.ensure_data() }; + recalc_style_at(self, traversal_data, context, el, &mut data, note_child); + + // Gets set later on + unsafe { el.unset_dirty_descendants() } + } + + #[inline] + fn needs_postorder_traversal() -> bool { + false + } + + fn process_postorder(&self, _style_context: &mut StyleContext, _node: E::ConcreteNode) { + panic!("this should never be called") + } + + #[inline] + fn shared_context(&self) -> &SharedStyleContext { + &self.context + } +} + +#[test] +fn assert_size_of_equals() { + // use std::mem; + + // fn assert_layout() { + // assert_eq!( + // mem::size_of::>(), + // mem::size_of::() + // ); + // assert_eq!( + // mem::align_of::>(), + // mem::align_of::() + // ); + // } + + // let size = mem::size_of::>(); + // dbg!(size); +} + +#[test] +fn parse_inline() { + // let attrs = style::attr::AttrValue::from_serialized_tokenlist( + // r#"visibility: hidden; left: 1306.5px; top: 50px; display: none;"#.to_string(), + // ); + + // let val = CSSInlineStyleDeclaration(); +} diff --git a/packages/blitz-dom/src/layout/construct.rs b/packages/blitz-dom/src/layout/construct.rs index 228d78a47..58314ec6a 100644 --- a/packages/blitz-dom/src/layout/construct.rs +++ b/packages/blitz-dom/src/layout/construct.rs @@ -1,9 +1,16 @@ +use super::table::build_table_context; +use crate::{ + node::{ + ListItemLayout, ListItemLayoutPosition, Marker, NodeKind, NodeSpecificData, TextBrush, + TextInputData, TextLayout, + }, + stylo_to_parley, Document, ElementNodeData, Handle, Node, NodeData, +}; use core::str; -use std::sync::Arc; - use html5ever::{local_name, namespace_url, ns, QualName}; use parley::{FontStack, InlineBox, PlainEditorOp, StyleProperty, TreeBuilder, WhiteSpaceCollapse}; use slab::Slab; +use std::sync::Arc; use style::{ data::ElementData, properties::longhands::{ @@ -17,17 +24,6 @@ use style::{ }, }; -use crate::{ - node::{ - ListItemLayout, ListItemLayoutPosition, Marker, NodeKind, NodeSpecificData, TextBrush, - TextInputData, TextLayout, - }, - stylo::Handle, - stylo_to_parley, Document, ElementNodeData, Node, NodeData, -}; - -use super::table::build_table_context; - pub(crate) fn collect_layout_children( doc: &mut Document, container_node_id: usize, diff --git a/packages/blitz-dom/src/lib.rs b/packages/blitz-dom/src/lib.rs index 446e50a76..a88679113 100644 --- a/packages/blitz-dom/src/lib.rs +++ b/packages/blitz-dom/src/lib.rs @@ -15,6 +15,10 @@ /// This is the primary entry point for this crate. pub mod document; +/// Node handle. +pub mod handle; +pub use self::handle::Handle; + /// HTML document data structure. pub mod html_document; /// An implementation for Html5ever's sink trait, allowing us to parse HTML into a DOM. diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index 76a9db309..b71030a37 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -814,27 +814,6 @@ impl Node { } } - pub fn text_content(&self) -> String { - let mut out = String::new(); - self.write_text_content(&mut out); - out - } - - fn write_text_content(&self, out: &mut String) { - match &self.raw_dom_data { - NodeData::Text(data) => { - out.push_str(&data.content); - } - NodeData::Element(..) | NodeData::AnonymousBlock(..) => { - for child_id in self.children.iter() { - // self.with(*child_id).write_text_content(out); - todo!() - } - } - _ => {} - } - } - pub fn flush_style_attribute(&mut self) { if let NodeData::Element(ref mut elem_data) = self.raw_dom_data { elem_data.flush_style_attribute(&self.guard); diff --git a/packages/blitz-dom/src/stylo.rs b/packages/blitz-dom/src/stylo.rs index dd909d3b4..6b59d5a77 100644 --- a/packages/blitz-dom/src/stylo.rs +++ b/packages/blitz-dom/src/stylo.rs @@ -4,8 +4,11 @@ use super::stylo_to_taffy; use crate::events::EventData; use crate::events::HitResult; +use crate::handle::RecalcStyle; +use crate::handle::RegisteredPaintersImpl; use crate::node::Node; use crate::node::NodeData; +use crate::Handle; use atomic_refcell::{AtomicRef, AtomicRefMut}; use html5ever::LocalNameStaticSet; use html5ever::NamespaceStaticSet; @@ -167,1040 +170,3 @@ impl crate::document::Document { style::thread_state::exit(ThreadState::LAYOUT); } } - -/// A handle to a node that Servo's style traits are implemented against -/// -/// Since BlitzNodes are not persistent (IE we don't keep the pointers around between frames), we choose to just implement -/// the tree structure in the nodes themselves, and temporarily give out pointers during the layout phase. -#[derive(Clone, Copy, Debug)] -pub struct Handle<'a> { - pub node: &'a Node, - pub tree: &'a Slab, -} - -impl Handle<'_> { - pub fn get(&self, id: usize) -> Self { - Self { - node: &self.tree[id], - tree: self.tree, - } - } - - pub fn forward(&self, n: usize) -> Option { - let child_idx = self.node.child_index().unwrap_or(0); - self.tree[self.node.parent?] - .children - .get(child_idx + n) - .map(|id| Self { - node: &self.tree[*id], - tree: self.tree, - }) - } - - pub fn backward(&self, n: usize) -> Option { - let child_idx = self.node.child_index().unwrap_or(0); - self.tree[self.node.parent?] - .children - .get(child_idx - n) - .map(|id| Self { - node: &self.tree[*id], - tree: self.tree, - }) - } - - /// Computes the Document-relative coordinates of the Node - pub fn absolute_position(&self, x: f32, y: f32) -> taffy::Point { - let x = x + self.node.final_layout.location.x - self.node.scroll_offset.x as f32; - let y = y + self.node.final_layout.location.y - self.node.scroll_offset.y as f32; - - self.node - .layout_parent - .get() - .map(|i| { - Self { - node: &self.tree[i], - tree: self.tree, - } - .absolute_position(x, y) - }) - .unwrap_or(taffy::Point { x, y }) - } - - /// Creates a synteh - pub fn synthetic_click_event(&self, mods: Modifiers) -> EventData { - let absolute_position = self.absolute_position(0.0, 0.0); - let x = absolute_position.x + (self.node.final_layout.size.width / 2.0); - let y = absolute_position.y + (self.node.final_layout.size.height / 2.0); - - EventData::Click { x, y, mods } - } - - /// Takes an (x, y) position (relative to the *parent's* top-left corner) and returns: - /// - None if the position is outside of this node's bounds - /// - Some(HitResult) if the position is within the node but doesn't match any children - /// - The result of recursively calling child.hit() on the the child element that is - /// positioned at that position if there is one. - /// - /// TODO: z-index - /// (If multiple children are positioned at the position then a random one will be recursed into) - pub fn hit(&self, x: f32, y: f32) -> Option { - let x = x - self.node.final_layout.location.x + self.node.scroll_offset.x as f32; - let y = y - self.node.final_layout.location.y + self.node.scroll_offset.y as f32; - - let size = self.node.final_layout.size; - - if x < 0.0 - || x > size.width + self.node.scroll_offset.x as f32 - || y < 0.0 - || y > size.height + self.node.scroll_offset.y as f32 - { - return None; - } - - // Call `.hit()` on each child in turn. If any return `Some` then return that value. Else return `Some(self.id). - self.node - .layout_children - .borrow() - .iter() - .flatten() - .find_map(|&id| self.get(id).hit(x, y)) - .or(Some(HitResult { - node_id: self.node.id, - x, - y, - })) - } -} - -impl PartialEq for Handle<'_> { - fn eq(&self, other: &Self) -> bool { - self.node == other.node - } -} - -// TODO (Matt) -impl Eq for Handle<'_> {} - -impl<'a> TDocument for Handle<'a> { - type ConcreteNode = Self; - - fn as_node(&self) -> Self::ConcreteNode { - *self - } - - fn is_html_document(&self) -> bool { - true - } - - fn quirks_mode(&self) -> QuirksMode { - QuirksMode::NoQuirks - } - - fn shared_lock(&self) -> &SharedRwLock { - &self.node.guard - } -} - -impl NodeInfo for Handle<'_> { - fn is_element(&self) -> bool { - self.node.is_element() - } - - fn is_text_node(&self) -> bool { - self.node.is_text_node() - } -} - -impl<'a> TShadowRoot for Handle<'a> { - type ConcreteNode = Self; - - fn as_node(&self) -> Self::ConcreteNode { - *self - } - - fn host(&self) -> ::ConcreteElement { - todo!("Shadow roots not implemented") - } - - fn style_data<'b>(&self) -> Option<&'b style::stylist::CascadeData> - where - Self: 'b, - { - todo!("Shadow roots not implemented") - } -} - -// components/styleaapper.rs: -impl<'a> TNode for Handle<'a> { - type ConcreteElement = Handle<'a>; - type ConcreteDocument = Handle<'a>; - type ConcreteShadowRoot = Handle<'a>; - - fn parent_node(&self) -> Option { - //self.parent.map(|id| self.with(id)) - todo!() - } - - fn first_child(&self) -> Option { - //self.children.first().map(|id| self.with(*id)) - todo!() - } - - fn last_child(&self) -> Option { - //self.children.last().map(|id| self.with(*id)) - todo!() - } - - fn prev_sibling(&self) -> Option { - //self.backward(1) - todo!() - } - - fn next_sibling(&self) -> Option { - //self.forward(1) - todo!() - } - - fn owner_doc(&self) -> Self::ConcreteDocument { - //self.with(1) - todo!() - } - - fn is_in_document(&self) -> bool { - true - } - - // I think this is the same as parent_node only in the cases when the direct parent is not a real element, forcing us - // to travel upwards - // - // For the sake of this demo, we're just going to return the parent node ann - fn traversal_parent(&self) -> Option { - self.parent_node().and_then(|node| node.as_element()) - } - - fn opaque(&self) -> OpaqueNode { - OpaqueNode(self.node.id) - } - - fn debug_id(self) -> usize { - self.node.id - } - - fn as_element(&self) -> Option { - match self.node.raw_dom_data { - NodeData::Element { .. } => Some(*self), - _ => None, - } - } - - fn as_document(&self) -> Option { - match self.node.raw_dom_data { - NodeData::Document { .. } => Some(*self), - _ => None, - } - } - - fn as_shadow_root(&self) -> Option { - todo!("Shadow roots aren't real, yet") - } -} - -impl selectors::Element for Handle<'_> { - type Impl = SelectorImpl; - - fn opaque(&self) -> selectors::OpaqueElement { - // FIXME: this is wrong in the case where pushing new elements casuses reallocations. - // We should see if selectors will accept a PR that allows creation from a usize - OpaqueElement::new(self) - } - - fn parent_element(&self) -> Option { - TElement::traversal_parent(self) - } - - fn parent_node_is_shadow_root(&self) -> bool { - false - } - - fn containing_shadow_host(&self) -> Option { - None - } - - fn is_pseudo_element(&self) -> bool { - matches!(self.node.raw_dom_data, NodeData::AnonymousBlock(_)) - } - - // These methods are implemented naively since we only threaded real nodes and not fake nodes - // we should try and use `find` instead of this foward/backward stuff since its ugly and slow - fn prev_sibling_element(&self) -> Option { - let mut n = 1; - while let Some(node) = self.backward(n) { - if node.is_element() { - return Some(node); - } - n += 1; - } - - None - } - - fn next_sibling_element(&self) -> Option { - let mut n = 1; - while let Some(node) = self.forward(n) { - if node.is_element() { - return Some(node); - } - n += 1; - } - - None - } - - fn first_element_child(&self) -> Option { - let mut children = self.dom_children(); - children.find(|child| child.is_element()) - } - - fn is_html_element_in_html_document(&self) -> bool { - true // self.has_namespace(ns!(html)) - } - - fn has_local_name(&self, local_name: &LocalName) -> bool { - self.node.raw_dom_data.is_element_with_tag_name(local_name) - } - - fn has_namespace(&self, ns: &Namespace) -> bool { - self.node.element_data().expect("Not an element").name.ns == *ns - } - - fn is_same_type(&self, _other: &Self) -> bool { - // FIXME: implementing this correctly currently triggers a debug_assert ("Invalid cache") in selectors - //self.local_name() == other.local_name() && self.namespace() == other.namespace() - false - } - - fn attr_matches( - &self, - _ns: &NamespaceConstraint<&GenericAtomIdent>, - local_name: &GenericAtomIdent, - operation: &AttrSelectorOperation<&AtomString>, - ) -> bool { - let Some(attr_value) = self.node.raw_dom_data.attr(local_name.0.clone()) else { - return false; - }; - - match operation { - AttrSelectorOperation::Exists => true, - AttrSelectorOperation::WithValue { - operator, - case_sensitivity: _, - value, - } => { - let value = value.as_ref(); - - // TODO: case sensitivity - match operator { - AttrSelectorOperator::Equal => attr_value == value, - AttrSelectorOperator::Includes => attr_value - .split_ascii_whitespace() - .any(|word| word == value), - AttrSelectorOperator::DashMatch => { - // Represents elements with an attribute name of attr whose value can be exactly value - // or can begin with value immediately followed by a hyphen, - (U+002D) - attr_value.starts_with(value) - && (attr_value.len() == value.len() - || attr_value.chars().nth(value.len()) == Some('-')) - } - AttrSelectorOperator::Prefix => attr_value.starts_with(value), - AttrSelectorOperator::Substring => attr_value.contains(value), - AttrSelectorOperator::Suffix => attr_value.ends_with(value), - } - } - } - } - - fn match_non_ts_pseudo_class( - &self, - pseudo_class: &::NonTSPseudoClass, - _context: &mut MatchingContext, - ) -> bool { - match *pseudo_class { - NonTSPseudoClass::Active => false, - NonTSPseudoClass::AnyLink => self - .node - .raw_dom_data - .downcast_element() - .map(|elem| { - (elem.name.local == local_name!("a") || elem.name.local == local_name!("area")) - && elem.attr(local_name!("href")).is_some() - }) - .unwrap_or(false), - NonTSPseudoClass::Checked => self - .node - .raw_dom_data - .downcast_element() - .and_then(|elem| elem.checkbox_input_checked()) - .unwrap_or(false), - NonTSPseudoClass::Valid => false, - NonTSPseudoClass::Invalid => false, - NonTSPseudoClass::Defined => false, - NonTSPseudoClass::Disabled => false, - NonTSPseudoClass::Enabled => false, - NonTSPseudoClass::Focus => self.node.element_state.contains(ElementState::FOCUS), - NonTSPseudoClass::FocusWithin => false, - NonTSPseudoClass::FocusVisible => false, - NonTSPseudoClass::Fullscreen => false, - NonTSPseudoClass::Hover => self.node.element_state.contains(ElementState::HOVER), - NonTSPseudoClass::Indeterminate => false, - NonTSPseudoClass::Lang(_) => false, - NonTSPseudoClass::CustomState(_) => false, - NonTSPseudoClass::Link => self - .node - .raw_dom_data - .downcast_element() - .map(|elem| { - (elem.name.local == local_name!("a") || elem.name.local == local_name!("area")) - && elem.attr(local_name!("href")).is_some() - }) - .unwrap_or(false), - NonTSPseudoClass::PlaceholderShown => false, - NonTSPseudoClass::ReadWrite => false, - NonTSPseudoClass::ReadOnly => false, - NonTSPseudoClass::ServoNonZeroBorder => false, - NonTSPseudoClass::Target => false, - NonTSPseudoClass::Visited => false, - NonTSPseudoClass::Autofill => false, - NonTSPseudoClass::Default => false, - - NonTSPseudoClass::InRange => false, - NonTSPseudoClass::Modal => false, - NonTSPseudoClass::Optional => false, - NonTSPseudoClass::OutOfRange => false, - NonTSPseudoClass::PopoverOpen => false, - NonTSPseudoClass::Required => false, - NonTSPseudoClass::UserInvalid => false, - NonTSPseudoClass::UserValid => false, - } - } - - fn match_pseudo_element( - &self, - pe: &PseudoElement, - _context: &mut MatchingContext, - ) -> bool { - match self.node.raw_dom_data { - NodeData::AnonymousBlock(_) => *pe == PseudoElement::ServoAnonymousBox, - _ => false, - } - } - - fn apply_selector_flags(&self, flags: ElementSelectorFlags) { - // Handle flags that apply to the element. - let self_flags = flags.for_self(); - if !self_flags.is_empty() { - *self.node.selector_flags.borrow_mut() |= self_flags; - } - - // Handle flags that apply to the parent. - let parent_flags = flags.for_parent(); - if !parent_flags.is_empty() { - if let Some(parent) = self.parent_node() { - *parent.node.selector_flags.borrow_mut() |= self_flags; - } - } - } - - fn is_link(&self) -> bool { - self.node - .raw_dom_data - .is_element_with_tag_name(&local_name!("a")) - } - - fn is_html_slot_element(&self) -> bool { - false - } - - fn has_id( - &self, - id: &::Identifier, - case_sensitivity: selectors::attr::CaseSensitivity, - ) -> bool { - self.node - .element_data() - .and_then(|data| data.id.as_ref()) - .map(|id_attr| case_sensitivity.eq_atom(id_attr, id)) - .unwrap_or(false) - } - - fn has_class( - &self, - search_name: &::Identifier, - case_sensitivity: selectors::attr::CaseSensitivity, - ) -> bool { - let class_attr = self.node.raw_dom_data.attr(local_name!("class")); - if let Some(class_attr) = class_attr { - // split the class attribute - for pheme in class_attr.split_ascii_whitespace() { - let atom = Atom::from(pheme); - if case_sensitivity.eq_atom(&atom, search_name) { - return true; - } - } - } - - false - } - - fn imported_part( - &self, - _name: &::Identifier, - ) -> Option<::Identifier> { - None - } - - fn is_part(&self, _name: &::Identifier) -> bool { - false - } - - fn is_empty(&self) -> bool { - self.dom_children().next().is_none() - } - - fn is_root(&self) -> bool { - self.parent_node() - .and_then(|parent| parent.parent_node()) - .is_none() - } - - fn has_custom_state( - &self, - _name: &::Identifier, - ) -> bool { - false - } - - fn add_element_unique_hashes(&self, _filter: &mut selectors::bloom::BloomFilter) -> bool { - false - } -} - -impl<'a> TElement for Handle<'a> { - type ConcreteNode = Handle<'a>; - - type TraversalChildrenIterator = Traverser<'a>; - - fn as_node(&self) -> Self::ConcreteNode { - *self - } - - fn unopaque(opaque: OpaqueElement) -> Self { - // FIXME: this is wrong in the case where pushing new elements casuses reallocations. - // We should see if selectors will accept a PR that allows creation from a usize - unsafe { *opaque.as_const_ptr() } - } - - fn traversal_children(&self) -> style::dom::LayoutIterator { - LayoutIterator(Traverser { - // dom: self.tree(), - parent: *self, - child_index: 0, - }) - } - - fn is_html_element(&self) -> bool { - self.is_element() - } - - // not implemented..... - fn is_mathml_element(&self) -> bool { - false - } - - // need to check the namespace - fn is_svg_element(&self) -> bool { - false - } - - fn style_attribute(&self) -> Option>> { - self.node - .element_data() - .expect("Not an element") - .style_attribute - .as_ref() - .map(|f| f.borrow_arc()) - } - - fn animation_rule( - &self, - _: &SharedStyleContext, - ) -> Option>> { - None - } - - fn transition_rule( - &self, - _context: &SharedStyleContext, - ) -> Option>> { - None - } - - fn state(&self) -> ElementState { - self.node.element_state - } - - fn has_part_attr(&self) -> bool { - false - } - - fn exports_any_part(&self) -> bool { - false - } - - fn id(&self) -> Option<&style::Atom> { - self.node.element_data().and_then(|data| data.id.as_ref()) - } - - fn each_class(&self, mut callback: F) - where - F: FnMut(&style::values::AtomIdent), - { - let class_attr = self.node.raw_dom_data.attr(local_name!("class")); - if let Some(class_attr) = class_attr { - // split the class attribute - for pheme in class_attr.split_ascii_whitespace() { - let atom = Atom::from(pheme); // interns the string - callback(AtomIdent::cast(&atom)); - } - } - } - - fn each_attr_name(&self, mut callback: F) - where - F: FnMut(&style::LocalName), - { - if let Some(attrs) = self.node.raw_dom_data.attrs() { - for attr in attrs.iter() { - callback(&GenericAtomIdent(attr.name.local.clone())); - } - } - } - - fn has_dirty_descendants(&self) -> bool { - true - } - - fn has_snapshot(&self) -> bool { - self.node.has_snapshot - } - - fn handled_snapshot(&self) -> bool { - self.node.snapshot_handled.load(Ordering::SeqCst) - } - - unsafe fn set_handled_snapshot(&self) { - self.node.snapshot_handled.store(true, Ordering::SeqCst); - } - - unsafe fn set_dirty_descendants(&self) {} - - unsafe fn unset_dirty_descendants(&self) {} - - fn store_children_to_process(&self, _n: isize) { - unimplemented!() - } - - fn did_process_child(&self) -> isize { - unimplemented!() - } - - unsafe fn ensure_data(&self) -> AtomicRefMut { - let mut stylo_data = self.node.stylo_element_data.borrow_mut(); - if stylo_data.is_none() { - *stylo_data = Some(Default::default()); - } - AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap()) - } - - unsafe fn clear_data(&self) { - *self.node.stylo_element_data.borrow_mut() = None; - } - - fn has_data(&self) -> bool { - self.node.stylo_element_data.borrow().is_some() - } - - fn borrow_data(&self) -> Option> { - let stylo_data = self.node.stylo_element_data.borrow(); - if stylo_data.is_some() { - Some(AtomicRef::map(stylo_data, |sd| sd.as_ref().unwrap())) - } else { - None - } - } - - fn mutate_data(&self) -> Option> { - let stylo_data = self.node.stylo_element_data.borrow_mut(); - if stylo_data.is_some() { - Some(AtomicRefMut::map(stylo_data, |sd| sd.as_mut().unwrap())) - } else { - None - } - } - - fn skip_item_display_fixup(&self) -> bool { - false - } - - fn may_have_animations(&self) -> bool { - false - } - - fn has_animations(&self, _context: &SharedStyleContext) -> bool { - false - } - - fn has_css_animations( - &self, - _context: &SharedStyleContext, - _pseudo_element: Option, - ) -> bool { - false - } - - fn has_css_transitions( - &self, - _context: &SharedStyleContext, - _pseudo_element: Option, - ) -> bool { - false - } - - fn shadow_root(&self) -> Option<::ConcreteShadowRoot> { - None - } - - fn containing_shadow(&self) -> Option<::ConcreteShadowRoot> { - None - } - - fn lang_attr(&self) -> Option { - None - } - - fn match_element_lang( - &self, - _override_lang: Option>, - _value: &style::selector_parser::Lang, - ) -> bool { - false - } - - fn is_html_document_body_element(&self) -> bool { - // Check node is a element - let is_body_element = self - .node - .raw_dom_data - .is_element_with_tag_name(&local_name!("body")); - - // If it isn't then return early - if !is_body_element { - return false; - } - - /* - // If it is then check if it is a child of the root () element - let root_node = &self.tree()[0]; - let root_element = TDocument::as_node(&root_node) - .first_element_child() - .unwrap(); - root_element.children.contains(&self.id) - */ - todo!() - } - - fn synthesize_presentational_hints_for_legacy_attributes( - &self, - _visited_handling: VisitedHandlingMode, - hints: &mut V, - ) where - V: Push, - { - let Some(elem) = self.node.raw_dom_data.downcast_element() else { - return; - }; - - let mut push_style = |decl: PropertyDeclaration| { - hints.push(ApplicableDeclarationBlock::from_declarations( - Arc::new( - self.node - .guard - .wrap(PropertyDeclarationBlock::with_one(decl, Importance::Normal)), - ), - CascadeLevel::PresHints, - LayerOrder::root(), - )); - }; - - fn parse_color_attr(value: &str) -> Option<(u8, u8, u8, f32)> { - if !value.starts_with('#') { - return None; - } - - let value = &value[1..]; - if value.len() == 3 { - let r = u8::from_str_radix(&value[0..1], 16).ok()?; - let g = u8::from_str_radix(&value[1..2], 16).ok()?; - let b = u8::from_str_radix(&value[2..3], 16).ok()?; - return Some((r, g, b, 1.0)); - } - - if value.len() == 6 { - let r = u8::from_str_radix(&value[0..2], 16).ok()?; - let g = u8::from_str_radix(&value[2..4], 16).ok()?; - let b = u8::from_str_radix(&value[4..6], 16).ok()?; - return Some((r, g, b, 1.0)); - } - - None - } - - fn parse_size_attr(value: &str) -> Option { - use style::values::specified::{AbsoluteLength, LengthPercentage, NoCalcLength}; - if let Some(value) = value.strip_suffix("px") { - let val: f32 = value.parse().ok()?; - return Some(LengthPercentage::Length(NoCalcLength::Absolute( - AbsoluteLength::Px(val), - ))); - } - - if let Some(value) = value.strip_suffix("%") { - let val: f32 = value.parse().ok()?; - return Some(LengthPercentage::Percentage(Percentage(val / 100.0))); - } - - let val: f32 = value.parse().ok()?; - Some(LengthPercentage::Length(NoCalcLength::Absolute( - AbsoluteLength::Px(val), - ))) - } - - for attr in elem.attrs() { - let name = &attr.name.local; - let value = attr.value.as_str(); - - if *name == local_name!("align") { - use style::values::specified::TextAlign; - let keyword = match value { - "left" => Some(StyloTextAlign::MozLeft), - "right" => Some(StyloTextAlign::MozRight), - "center" => Some(StyloTextAlign::MozCenter), - _ => None, - }; - - if let Some(keyword) = keyword { - push_style(PropertyDeclaration::TextAlign(TextAlign::Keyword(keyword))); - } - } - - if *name == local_name!("width") { - if let Some(width) = parse_size_attr(value) { - use style::values::generics::{length::Size, NonNegative}; - push_style(PropertyDeclaration::Width(Size::LengthPercentage( - NonNegative(width), - ))); - } - } - - if *name == local_name!("height") { - if let Some(height) = parse_size_attr(value) { - use style::values::generics::{length::Size, NonNegative}; - push_style(PropertyDeclaration::Height(Size::LengthPercentage( - NonNegative(height), - ))); - } - } - - if *name == local_name!("bgcolor") { - use style::values::specified::Color; - if let Some((r, g, b, a)) = parse_color_attr(value) { - push_style(PropertyDeclaration::BackgroundColor( - Color::from_absolute_color(AbsoluteColor::srgb_legacy(r, g, b, a)), - )); - } - } - } - } - - fn local_name(&self) -> &LocalName { - &self.node.element_data().expect("Not an element").name.local - } - - fn namespace(&self) -> &Namespace { - &self.node.element_data().expect("Not an element").name.ns - } - - fn query_container_size( - &self, - _display: &style::values::specified::Display, - ) -> euclid::default::Size2D> { - // FIXME: Implement container queries. For now this effectively disables them without panicking. - Default::default() - } - - fn each_custom_state(&self, _callback: F) - where - F: FnMut(&AtomIdent), - { - todo!() - } - - fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool { - self.node.selector_flags.borrow().contains(flags) - } - - fn relative_selector_search_direction(&self) -> ElementSelectorFlags { - self.node - .selector_flags - .borrow() - .intersection(ElementSelectorFlags::RELATIVE_SELECTOR_SEARCH_DIRECTION_ANCESTOR_SIBLING) - } - - // fn update_animations( - // &self, - // before_change_style: Option>, - // tasks: style::context::UpdateAnimationsTasks, - // ) { - // todo!() - // } - - // fn process_post_animation(&self, tasks: style::context::PostAnimationTasks) { - // todo!() - // } - - // fn needs_transitions_update( - // &self, - // before_change_style: &ComputedValues, - // after_change_style: &ComputedValues, - // ) -> bool { - // todo!() - // } -} - -pub struct Traverser<'a> { - // dom: &'a Slab, - parent: Handle<'a>, - child_index: usize, -} - -impl<'a> Iterator for Traverser<'a> { - type Item = Handle<'a>; - - fn next(&mut self) -> Option { - let node_id = self.parent.node.children.get(self.child_index)?; - let node = self.parent.tree.get(*node_id)?; - - self.child_index += 1; - - Some(Handle { - node, - tree: self.parent.tree, - }) - } -} - -impl std::hash::Hash for Handle<'_> { - fn hash(&self, state: &mut H) { - state.write_usize(self.node.id) - } -} - -/// Handle custom painters like images for layouting -/// -/// todo: actually implement this -pub struct RegisteredPaintersImpl; -impl RegisteredSpeculativePainters for RegisteredPaintersImpl { - fn get(&self, _name: &Atom) -> Option<&dyn RegisteredSpeculativePainter> { - None - } -} - -use style::traversal::recalc_style_at; - -pub struct RecalcStyle<'a> { - context: SharedStyleContext<'a>, -} - -impl<'a> RecalcStyle<'a> { - pub fn new(context: SharedStyleContext<'a>) -> Self { - RecalcStyle { context } - } -} - -#[allow(unsafe_code)] -impl DomTraversal for RecalcStyle<'_> -where - E: TElement, -{ - fn process_preorder( - &self, - traversal_data: &PerLevelTraversalData, - context: &mut StyleContext, - node: E::ConcreteNode, - note_child: F, - ) { - // Don't process textnodees in this traversal - if node.is_text_node() { - return; - } - - let el = node.as_element().unwrap(); - // let mut data = el.mutate_data().unwrap(); - let mut data = unsafe { el.ensure_data() }; - recalc_style_at(self, traversal_data, context, el, &mut data, note_child); - - // Gets set later on - unsafe { el.unset_dirty_descendants() } - } - - #[inline] - fn needs_postorder_traversal() -> bool { - false - } - - fn process_postorder(&self, _style_context: &mut StyleContext, _node: E::ConcreteNode) { - panic!("this should never be called") - } - - #[inline] - fn shared_context(&self) -> &SharedStyleContext { - &self.context - } -} - -#[test] -fn assert_size_of_equals() { - // use std::mem; - - // fn assert_layout() { - // assert_eq!( - // mem::size_of::>(), - // mem::size_of::() - // ); - // assert_eq!( - // mem::align_of::>(), - // mem::align_of::() - // ); - // } - - // let size = mem::size_of::>(); - // dbg!(size); -} - -#[test] -fn parse_inline() { - // let attrs = style::attr::AttrValue::from_serialized_tokenlist( - // r#"visibility: hidden; left: 1306.5px; top: 50px; display: none;"#.to_string(), - // ); - - // let val = CSSInlineStyleDeclaration(); -} diff --git a/packages/blitz-renderer-vello/src/renderer/render.rs b/packages/blitz-renderer-vello/src/renderer/render.rs index 22ed51b25..10227e29b 100644 --- a/packages/blitz-renderer-vello/src/renderer/render.rs +++ b/packages/blitz-renderer-vello/src/renderer/render.rs @@ -10,8 +10,7 @@ use blitz_dom::node::{ ListItemLayout, ListItemLayoutPosition, Marker, NodeData, TextBrush, TextInputData, TextNodeData, }; -use blitz_dom::stylo::Handle; -use blitz_dom::{local_name, Document, Node}; +use blitz_dom::{local_name, Document, Handle}; use parley::Line; use style::{ diff --git a/packages/dioxus-native/src/accessibility.rs b/packages/dioxus-native/src/accessibility.rs index 8787a2b59..ca7a0be1c 100644 --- a/packages/dioxus-native/src/accessibility.rs +++ b/packages/dioxus-native/src/accessibility.rs @@ -1,6 +1,6 @@ use crate::waker::BlitzEvent; use accesskit::{NodeBuilder, NodeId, Role, Tree, TreeUpdate}; -use blitz_dom::{local_name, Document, Node}; +use blitz_dom::{local_name, Document, Handle, Node}; use winit::{event_loop::EventLoopProxy, window::Window}; /// State of the accessibility node tree and platform adapter. @@ -29,7 +29,13 @@ impl AccessibilityState { .and_then(|parent_id| nodes.get_mut(&parent_id)) .map(|(_, parent)| parent) .unwrap_or(&mut window); - let (id, node_builder) = self.build_node(node, parent); + let (id, node_builder) = self.build_node( + Handle { + node, + tree: doc.tree(), + }, + parent, + ); nodes.insert(node_id, (id, node_builder)); }); @@ -50,13 +56,13 @@ impl AccessibilityState { self.adapter.update_if_active(|| tree_update) } - fn build_node(&mut self, node: &Node, parent: &mut NodeBuilder) -> (NodeId, NodeBuilder) { + fn build_node(&mut self, handle: Handle, parent: &mut NodeBuilder) -> (NodeId, NodeBuilder) { let mut node_builder = NodeBuilder::default(); let id = NodeId(self.next_id); self.next_id += 1; - if let Some(element_data) = node.element_data() { + if let Some(element_data) = handle.node.element_data() { let name = element_data.name.local.to_string(); // TODO match more roles @@ -80,9 +86,9 @@ impl AccessibilityState { node_builder.set_role(role); node_builder.set_html_tag(name); - } else if node.is_text_node() { + } else if handle.node.is_text_node() { node_builder.set_role(Role::StaticText); - node_builder.set_name(node.text_content()); + node_builder.set_name(handle.text_content()); parent.push_labelled_by(id) } diff --git a/packages/dioxus-native/src/documents/dioxus_document.rs b/packages/dioxus-native/src/documents/dioxus_document.rs index 8b277a892..92d9e3c5c 100644 --- a/packages/dioxus-native/src/documents/dioxus_document.rs +++ b/packages/dioxus-native/src/documents/dioxus_document.rs @@ -10,9 +10,8 @@ use blitz_dom::{ events::{EventData, RendererEvent}, local_name, namespace_url, node::{Attribute, NodeSpecificData}, - ns, - stylo::Handle, - Atom, Document, DocumentLike, ElementNodeData, Node, NodeData, QualName, Viewport, DEFAULT_CSS, + ns, Atom, Document, DocumentLike, ElementNodeData, Handle, Node, NodeData, QualName, Viewport, + DEFAULT_CSS, }; use dioxus::{ From e0611c75648fcaf06707110cbde924e0a01a0e91 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 20:35:41 -0400 Subject: [PATCH 7/8] Finish up todos --- packages/blitz-dom/src/document.rs | 34 +++---- packages/blitz-dom/src/handle.rs | 90 +++++++++++++------ packages/blitz-dom/src/layout/construct.rs | 9 +- packages/blitz-dom/src/node.rs | 48 ---------- packages/blitz-dom/src/stylo.rs | 45 ++-------- packages/blitz-dom/src/util.rs | 22 ++--- packages/dioxus-native/src/accessibility.rs | 2 +- .../src/documents/dioxus_document.rs | 2 +- 8 files changed, 100 insertions(+), 152 deletions(-) diff --git a/packages/blitz-dom/src/document.rs b/packages/blitz-dom/src/document.rs index 827b75db1..1a4af0b8b 100644 --- a/packages/blitz-dom/src/document.rs +++ b/packages/blitz-dom/src/document.rs @@ -512,29 +512,23 @@ impl Document { *is_checked = !*is_checked; } - pub fn root_node(&self) -> &Node { - &self.nodes[0] + pub fn root_node(&self) -> Handle { + self.get(0).unwrap() } pub fn try_root_element(&self) -> Option<&Node> { - TDocument::as_node(&Handle { - node: self.root_node(), - tree: self.tree(), - }) - .first_element_child() - .map(|blitz_node| blitz_node.node) + TDocument::as_node(&self.root_node()) + .first_element_child() + .map(|blitz_node| blitz_node.node) } pub fn root_element(&self) -> &Node { - TDocument::as_node(&Handle { - node: self.root_node(), - tree: self.tree(), - }) - .first_element_child() - .unwrap() - .as_element() - .unwrap() - .node + TDocument::as_node(&self.root_node()) + .first_element_child() + .unwrap() + .as_element() + .unwrap() + .node } pub fn create_node(&mut self, node_data: NodeData) -> usize { @@ -681,7 +675,7 @@ impl Document { } pub fn print_subtree(&self, node_id: usize) { - crate::util::walk_tree(0, &self.nodes[node_id]); + crate::util::walk_tree(0, self.get(node_id).unwrap()); } pub fn process_style_element(&mut self, target_id: usize) { @@ -929,7 +923,7 @@ impl Document { // Continue search from the root else { look_in_children = true; - self.root_node() + self.root_node().node }; if filter(next) { @@ -1031,7 +1025,7 @@ impl Document { /// Ensure that the layout_children field is populated for all nodes pub fn resolve_layout_children(&mut self) { - let root_node_id = self.root_node().id; + let root_node_id = self.root_node().node.id; resolve_layout_children_recursive(self, root_node_id); pub fn resolve_layout_children_recursive(doc: &mut Document, node_id: usize) { diff --git a/packages/blitz-dom/src/handle.rs b/packages/blitz-dom/src/handle.rs index 22c1e2c40..2da9a1933 100644 --- a/packages/blitz-dom/src/handle.rs +++ b/packages/blitz-dom/src/handle.rs @@ -1,7 +1,6 @@ //! Enable the dom to participate in styling by servo //! -use super::stylo_to_taffy; use crate::events::EventData; use crate::events::HitResult; use crate::node::Node; @@ -20,31 +19,28 @@ use slab::Slab; use std::sync::atomic::Ordering; use style::applicable_declarations::ApplicableDeclarationBlock; use style::color::AbsoluteColor; -use style::invalidation::element::restyle_hints::RestyleHint; use style::properties::{Importance, PropertyDeclaration}; use style::rule_tree::CascadeLevel; use style::selector_parser::PseudoElement; use style::stylesheets::layer_rule::LayerOrder; use style::values::computed::text::TextAlign as StyloTextAlign; +use style::values::computed::Display; use style::values::computed::Percentage; +use style::values::specified::box_::DisplayInside; use style::values::specified::box_::DisplayOutside; use style::values::AtomString; use style::CaseSensitivityExt; use style::{ - animation::DocumentAnimationSet, context::{ QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, SharedStyleContext, StyleContext, }, dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot}, - global_style_data::GLOBAL_STYLE_DATA, properties::PropertyDeclarationBlock, selector_parser::{NonTSPseudoClass, SelectorImpl}, servo_arc::{Arc, ArcBorrow}, - shared_lock::{Locked, SharedRwLock, StylesheetGuards}, - thread_state::ThreadState, + shared_lock::{Locked, SharedRwLock}, traversal::{DomTraversal, PerLevelTraversalData}, - traversal_flags::TraversalFlags, values::{AtomIdent, GenericAtomIdent}, Atom, }; @@ -69,8 +65,18 @@ impl Handle<'_> { } } + // Get the index of the current node in the parents child list + pub fn child_index(&self) -> Option { + self.node.parent.and_then(|parent_id| { + self.tree[parent_id] + .children + .iter() + .position(|id| *id == self.node.id) + }) + } + pub fn forward(&self, n: usize) -> Option { - let child_idx = self.node.child_index().unwrap_or(0); + let child_idx = self.child_index().unwrap_or(0); self.tree[self.node.parent?] .children .get(child_idx + n) @@ -81,7 +87,7 @@ impl Handle<'_> { } pub fn backward(&self, n: usize) -> Option { - let child_idx = self.node.child_index().unwrap_or(0); + let child_idx = self.child_index().unwrap_or(0); self.tree[self.node.parent?] .children .get(child_idx - n) @@ -173,6 +179,43 @@ impl Handle<'_> { _ => {} } } + + /// Returns true if this node, or any of its children, is a block. + pub fn is_or_contains_block(&self) -> bool { + let display = self.node.display_style().unwrap_or(Display::inline()); + + match display.outside() { + DisplayOutside::None => false, + DisplayOutside::Block => true, + _ => { + if display.inside() == DisplayInside::Flow { + self.node + .children + .iter() + .copied() + .any(|child_id| self.get(child_id).is_or_contains_block()) + } else { + false + } + } + } + } + + pub fn print_tree(&self, level: usize) { + println!( + "{} {} {:?} {} {:?}", + " ".repeat(level), + self.node.id, + self.node.parent, + self.node.node_debug_str().replace('\n', ""), + self.node.children + ); + + for child_id in &self.node.children { + let child = self.get(*child_id); + child.print_tree(level + 1) + } + } } impl PartialEq for Handle<'_> { @@ -240,33 +283,27 @@ impl<'a> TNode for Handle<'a> { type ConcreteShadowRoot = Handle<'a>; fn parent_node(&self) -> Option { - //self.parent.map(|id| self.with(id)) - todo!() + self.node.parent.map(|id| self.get(id)) } fn first_child(&self) -> Option { - //self.children.first().map(|id| self.with(*id)) - todo!() + self.node.children.first().map(|id| self.get(*id)) } fn last_child(&self) -> Option { - //self.children.last().map(|id| self.with(*id)) - todo!() + self.node.children.last().map(|id| self.get(*id)) } fn prev_sibling(&self) -> Option { - //self.backward(1) - todo!() + self.backward(1) } fn next_sibling(&self) -> Option { - //self.forward(1) - todo!() + self.forward(1) } fn owner_doc(&self) -> Self::ConcreteDocument { - //self.with(1) - todo!() + self.get(1) } fn is_in_document(&self) -> bool { @@ -810,15 +847,10 @@ impl<'a> TElement for Handle<'a> { return false; } - /* // If it is then check if it is a child of the root () element - let root_node = &self.tree()[0]; - let root_element = TDocument::as_node(&root_node) - .first_element_child() - .unwrap(); - root_element.children.contains(&self.id) - */ - todo!() + let root_node = &self.get(0); + let root_element = TDocument::as_node(root_node).first_element_child().unwrap(); + root_element.node.children.contains(&self.node.id) } fn synthesize_presentational_hints_for_legacy_attributes( diff --git a/packages/blitz-dom/src/layout/construct.rs b/packages/blitz-dom/src/layout/construct.rs index 58314ec6a..c16b2cdbe 100644 --- a/packages/blitz-dom/src/layout/construct.rs +++ b/packages/blitz-dom/src/layout/construct.rs @@ -116,7 +116,12 @@ pub(crate) fn collect_layout_children( all_block = false; // We need the "complex" tree fixing when an inline contains a block - if child.is_or_contains_block() { + if (Handle { + node: child, + tree: doc.tree(), + }) + .is_or_contains_block() + { all_inline = false; } } @@ -404,7 +409,7 @@ fn collect_complex_layout_children( let child_node_kind = doc.nodes[child_id].raw_dom_data.kind(); // Get Display style. Default to inline because nodes without styles are probably text nodes - let contains_block = doc.nodes[child_id].is_or_contains_block(); + let contains_block = doc.get(child_id).unwrap().is_or_contains_block(); let child_display = &doc.nodes[child_id] .display_style() .unwrap_or(Display::inline()); diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index b71030a37..d4ae25664 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -13,7 +13,6 @@ use style::invalidation::element::restyle_hints::RestyleHint; use style::properties::ComputedValues; use style::stylesheets::UrlExtraData; use style::values::computed::Display; -use style::values::specified::box_::{DisplayInside, DisplayOutside}; use style::Atom; use style::{ data::ElementData, @@ -172,27 +171,6 @@ impl Node { ) } - /// Returns true if this node, or any of its children, is a block. - pub fn is_or_contains_block(&self) -> bool { - let display = self.display_style().unwrap_or(Display::inline()); - - match display.outside() { - DisplayOutside::None => false, - DisplayOutside::Block => true, - _ => { - if display.inside() == DisplayInside::Flow { - /*self.children - .iter() - .copied() - .any(|child_id| self.tree()[child_id].is_or_contains_block())*/ - todo!() - } else { - false - } - } - } - } - /// Returns true if this node is able to focused. pub fn is_focussable(&self) -> bool { self.raw_dom_data @@ -684,32 +662,6 @@ impl TextNodeData { // } impl Node { - pub fn print_tree(&self, level: usize) { - println!( - "{} {} {:?} {} {:?}", - " ".repeat(level), - self.id, - self.parent, - self.node_debug_str().replace('\n', ""), - self.children - ); - // println!("{} {:?}", " ".repeat(level), self.children); - for child_id in self.children.iter() { - //let child = self.with(*child_id); - todo!(); - //child.print_tree(level + 1) - } - } - - // Get the index of the current node in the parents child list - pub fn child_index(&self) -> Option { - /*self.tree()[self.parent?] - .children - .iter() - .position(|id| *id == self.id)*/ - todo!() - } - pub fn is_element(&self) -> bool { matches!(self.raw_dom_data, NodeData::Element { .. }) } diff --git a/packages/blitz-dom/src/stylo.rs b/packages/blitz-dom/src/stylo.rs index 6b59d5a77..ac51f334a 100644 --- a/packages/blitz-dom/src/stylo.rs +++ b/packages/blitz-dom/src/stylo.rs @@ -2,57 +2,22 @@ //! use super::stylo_to_taffy; -use crate::events::EventData; -use crate::events::HitResult; use crate::handle::RecalcStyle; use crate::handle::RegisteredPaintersImpl; -use crate::node::Node; -use crate::node::NodeData; use crate::Handle; -use atomic_refcell::{AtomicRef, AtomicRefMut}; -use html5ever::LocalNameStaticSet; -use html5ever::NamespaceStaticSet; -use html5ever::{local_name, LocalName, Namespace}; -use selectors::{ - attr::{AttrSelectorOperation, AttrSelectorOperator, NamespaceConstraint}, - matching::{ElementSelectorFlags, MatchingContext, VisitedHandlingMode}, - sink::Push, - Element, OpaqueElement, -}; -use slab::Slab; -use std::sync::atomic::Ordering; -use style::applicable_declarations::ApplicableDeclarationBlock; -use style::color::AbsoluteColor; +use selectors::Element; use style::invalidation::element::restyle_hints::RestyleHint; -use style::properties::{Importance, PropertyDeclaration}; -use style::rule_tree::CascadeLevel; -use style::selector_parser::PseudoElement; -use style::stylesheets::layer_rule::LayerOrder; -use style::values::computed::text::TextAlign as StyloTextAlign; -use style::values::computed::Percentage; use style::values::specified::box_::DisplayOutside; -use style::values::AtomString; -use style::CaseSensitivityExt; use style::{ animation::DocumentAnimationSet, - context::{ - QuirksMode, RegisteredSpeculativePainter, RegisteredSpeculativePainters, - SharedStyleContext, StyleContext, - }, - dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot}, + context::SharedStyleContext, + dom::{TDocument, TNode}, global_style_data::GLOBAL_STYLE_DATA, - properties::PropertyDeclarationBlock, - selector_parser::{NonTSPseudoClass, SelectorImpl}, - servo_arc::{Arc, ArcBorrow}, - shared_lock::{Locked, SharedRwLock, StylesheetGuards}, + shared_lock::StylesheetGuards, thread_state::ThreadState, - traversal::{DomTraversal, PerLevelTraversalData}, + traversal::DomTraversal, traversal_flags::TraversalFlags, - values::{AtomIdent, GenericAtomIdent}, - Atom, }; -use style_dom::ElementState; -use winit::event::Modifiers; impl crate::document::Document { /// Walk the whole tree, converting styles to layout diff --git a/packages/blitz-dom/src/util.rs b/packages/blitz-dom/src/util.rs index de1b5df44..633adac2c 100644 --- a/packages/blitz-dom/src/util.rs +++ b/packages/blitz-dom/src/util.rs @@ -1,4 +1,5 @@ -use crate::node::{Node, NodeData}; +use crate::node::NodeData; +use crate::Handle; use image::DynamicImage; use selectors::context::QuirksMode; use std::str::FromStr; @@ -182,17 +183,17 @@ impl RequestHandler for ImageHandler { } // Debug print an RcDom -pub fn walk_tree(indent: usize, node: &Node) { +pub fn walk_tree(indent: usize, handle: Handle) { // Skip all-whitespace text nodes entirely - if let NodeData::Text(data) = &node.raw_dom_data { + if let NodeData::Text(data) = &handle.node.raw_dom_data { if data.content.chars().all(|c| c.is_ascii_whitespace()) { return; } } print!("{}", " ".repeat(indent)); - let id = node.id; - match &node.raw_dom_data { + let id = handle.node.id; + match &handle.node.raw_dom_data { NodeData::Document => println!("#Document {id}"), NodeData::Text(data) => { @@ -223,7 +224,7 @@ pub fn walk_tree(indent: usize, node: &Node) { for attr in data.attrs.iter() { print!(" {}=\"{}\"", attr.name.local, attr.value); } - if !node.children.is_empty() { + if !handle.node.children.is_empty() { println!(">"); } else { println!("/>"); @@ -236,13 +237,12 @@ pub fn walk_tree(indent: usize, node: &Node) { // NodeData::ProcessingInstruction { .. } => unreachable!(), } - if !node.children.is_empty() { - for child_id in node.children.iter() { - //walk_tree(indent + 2, node.with(*child_id)); - todo!() + if !handle.node.children.is_empty() { + for child_id in handle.node.children.iter() { + walk_tree(indent + 2, handle.get(*child_id)); } - if let NodeData::Element(data) = &node.raw_dom_data { + if let NodeData::Element(data) = &handle.node.raw_dom_data { println!("{}", " ".repeat(indent), data.name.local); } } diff --git a/packages/dioxus-native/src/accessibility.rs b/packages/dioxus-native/src/accessibility.rs index ca7a0be1c..0eb64a522 100644 --- a/packages/dioxus-native/src/accessibility.rs +++ b/packages/dioxus-native/src/accessibility.rs @@ -1,6 +1,6 @@ use crate::waker::BlitzEvent; use accesskit::{NodeBuilder, NodeId, Role, Tree, TreeUpdate}; -use blitz_dom::{local_name, Document, Handle, Node}; +use blitz_dom::{local_name, Document, Handle}; use winit::{event_loop::EventLoopProxy, window::Window}; /// State of the accessibility node tree and platform adapter. diff --git a/packages/dioxus-native/src/documents/dioxus_document.rs b/packages/dioxus-native/src/documents/dioxus_document.rs index 92d9e3c5c..0d14062e7 100644 --- a/packages/dioxus-native/src/documents/dioxus_document.rs +++ b/packages/dioxus-native/src/documents/dioxus_document.rs @@ -391,7 +391,7 @@ impl DioxusDocument { qual_name("html", None), Vec::new(), ))); - let root_node_id = doc.root_node().id; + let root_node_id = doc.root_node().node.id; let html_element = doc.get_node_mut(html_element_id).unwrap(); html_element.parent = Some(root_node_id); let root_node = doc.get_node_mut(root_node_id).unwrap(); From 6df1a71d11f2e70f102c0ee5e43192cd9de5c768 Mon Sep 17 00:00:00 2001 From: Matt Hunzinger Date: Sat, 26 Oct 2024 20:49:15 -0400 Subject: [PATCH 8/8] Pass clippy --- packages/blitz-dom/src/handle.rs | 12 ++++++------ packages/blitz-dom/src/node.rs | 7 ------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/packages/blitz-dom/src/handle.rs b/packages/blitz-dom/src/handle.rs index 2da9a1933..05119c530 100644 --- a/packages/blitz-dom/src/handle.rs +++ b/packages/blitz-dom/src/handle.rs @@ -227,7 +227,7 @@ impl PartialEq for Handle<'_> { // TODO (Matt) impl Eq for Handle<'_> {} -impl<'a> TDocument for Handle<'a> { +impl TDocument for Handle<'_> { type ConcreteNode = Self; fn as_node(&self) -> Self::ConcreteNode { @@ -257,7 +257,7 @@ impl NodeInfo for Handle<'_> { } } -impl<'a> TShadowRoot for Handle<'a> { +impl TShadowRoot for Handle<'_> { type ConcreteNode = Self; fn as_node(&self) -> Self::ConcreteNode { @@ -277,10 +277,10 @@ impl<'a> TShadowRoot for Handle<'a> { } // components/styleaapper.rs: -impl<'a> TNode for Handle<'a> { - type ConcreteElement = Handle<'a>; - type ConcreteDocument = Handle<'a>; - type ConcreteShadowRoot = Handle<'a>; +impl TNode for Handle<'_> { + type ConcreteElement = Self; + type ConcreteDocument = Self; + type ConcreteShadowRoot = Self; fn parent_node(&self) -> Option { self.node.parent.map(|id| self.get(id)) diff --git a/packages/blitz-dom/src/node.rs b/packages/blitz-dom/src/node.rs index d4ae25664..38522e6aa 100644 --- a/packages/blitz-dom/src/node.rs +++ b/packages/blitz-dom/src/node.rs @@ -40,13 +40,6 @@ pub enum DisplayOuter { /// A node in a [`Document`](crate::Document). pub struct Node { - /// The tree of nodes containing this node. - /// - /// # Safety - /// This is a raw pointer to the slab containing this node. - /// By using this pointer you must gurantee that this node outlives the tree. - //pub tree: *mut Slab, - /// This node's ID. pub id: usize,