diff --git a/crates/gosub_html5/src/node.rs b/crates/gosub_html5/src/node.rs index 69e39095e..0a4f08bff 100644 --- a/crates/gosub_html5/src/node.rs +++ b/crates/gosub_html5/src/node.rs @@ -130,6 +130,12 @@ pub struct Node { pub is_registered: bool, } +impl Node { + pub fn is_root(&self) -> bool { + self.id.is_root() + } +} + impl PartialEq for Node { fn eq(&self, other: &Node) -> bool { self.id == other.id diff --git a/crates/gosub_styling/src/css_node_tree.rs b/crates/gosub_styling/src/css_node_tree.rs index 3f6a85f85..18d6b78e2 100644 --- a/crates/gosub_styling/src/css_node_tree.rs +++ b/crates/gosub_styling/src/css_node_tree.rs @@ -6,7 +6,6 @@ use gosub_css3::stylesheet::{ use gosub_html5::node::NodeId; use gosub_html5::parser::document::{DocumentHandle, TreeIterator}; use gosub_shared::types::Result; -use std::cell::RefCell; use std::cmp::Ordering; use std::collections::HashMap; @@ -38,25 +37,25 @@ pub fn generate_css_node_tree(document: DocumentHandle) -> Result { // Selector matched, so we add all declared values to the map for declaration in rule.declarations().iter() { - let property = declaration.property.clone(); + let prop_name = declaration.property.clone(); let declaration = DeclarationProperty { value: CssValue::String(declaration.value.clone()), // @TODO: parse the value into the correct CSSValue origin: sheet.origin.clone(), important: declaration.important, - location: "".into(), + location: sheet.location.clone(), specificity: selector.specificity(), }; if let std::collections::hash_map::Entry::Vacant(e) = - css_map_entry.properties.entry(property.clone()) + css_map_entry.properties.entry(prop_name.clone()) { - let mut entry = CssProperty::new(); + let mut entry = CssProperty::new(prop_name.as_str()); entry.declared.push(declaration); - e.insert(RefCell::new(entry)); + e.insert(entry); } else { - let entry = css_map_entry.properties.get_mut(&property).unwrap(); - entry.get_mut().declared.push(declaration); + let entry = css_map_entry.properties.get_mut(&prop_name).unwrap(); + entry.declared.push(declaration); } } } @@ -66,10 +65,21 @@ pub fn generate_css_node_tree(document: DocumentHandle) -> Result { css_node_tree.nodes.insert(current_node_id, css_map_entry); } + for (node_id, props) in css_node_tree.nodes.iter() { + println!("Node: {:?}", node_id); + for (prop, values) in props.properties.iter() { + println!(" {}", prop); + if prop == "color" { + for decl in values.declared.iter() { + println!(" {:?} {:?} {:?} {:?}", decl.origin, decl.location, decl.value, decl.specificity); + } + } + } + } + Ok(css_node_tree) } -#[allow(dead_code)] // Matches a complete selector (all parts) against the given node(id) fn match_selector(document: DocumentHandle, node_id: NodeId, selector: &CssSelector) -> bool { let mut parts = selector.parts.clone(); @@ -77,7 +87,6 @@ fn match_selector(document: DocumentHandle, node_id: NodeId, selector: &CssSelec match_selector_part(document, node_id, &mut parts) } -#[allow(dead_code)] /// Returns true when the given node matches the part(s) fn match_selector_part( document: DocumentHandle, @@ -92,6 +101,10 @@ fn match_selector_part( return false; } let current_node = next_current_node.expect("current_node not found"); + if current_node.is_root() { + return false; + } + let part = selector_parts.remove(0); match part.type_ { @@ -303,6 +316,8 @@ impl Ord for DeclarationProperty { /// all the computed values. #[derive(Debug, Clone)] pub struct CssProperty { + /// The name of the property + pub name: String, /// True when this property needs to be recalculated pub dirty: bool, /// List of all declared values for this property @@ -318,15 +333,10 @@ pub struct CssProperty { pub actual: CssValue, } -impl Default for CssProperty { - fn default() -> Self { - Self::new() - } -} - impl CssProperty { - pub fn new() -> Self { + pub fn new(prop_name: &str) -> Self { Self { + name: prop_name.to_string(), dirty: true, declared: Vec::new(), cascaded: None, @@ -345,38 +355,99 @@ impl CssProperty { self.dirty = false; } - pub fn get_value(&mut self) -> &CssValue { + /// Returns the actual value of the property. Will compute the value when needed + pub fn compute_value(&mut self) -> &CssValue { if self.dirty { - self.dirty = false; self.calculate_value(); + self.dirty = false; } &self.actual } fn calculate_value(&mut self) { - // Step 1: Find cascaded value + self.cascaded = self.find_cascaded_value(); + self.specified = self.find_specified_value(); + self.computed = self.find_computed_value(); + self.used = self.find_used_value(); + self.actual = self.find_actual_value(); + } - // Step 2: Find specified value + fn find_cascaded_value(&self) -> Option { + let mut declared = self.declared.clone(); - // Step 3: Find computed value (needs viewport?) + declared.sort(); + declared.sort_by(|a, b| { + if a.priority() == b.priority() { + return Ordering::Equal; + } - // Step 4: Find used value (??) + a.specificity.cmp(&b.specificity) + }); - // Step 5: Find actual value by rounding numbers (when possible) + declared.last().map(|d| d.value.clone()) + } + + fn find_specified_value(&self) -> CssValue { + match self.declared.iter().max() { + Some(decl) => decl.value.clone(), + None => CssValue::None, + } + } + + fn find_computed_value(&self) -> CssValue { + if self.specified != CssValue::None { + return self.specified.clone(); + } + + if self.is_inheritable() { + while let Some(parent) = self.get_parent() { + if let Some(parent_value) = parent { + return parent_value.find_computed_value(); + } + } + } + + self.get_initial_value() + } + + fn find_used_value(&self) -> CssValue { + self.computed.clone() + } + + fn find_actual_value(&self) -> CssValue { // @TODO: stuff like clipping and such should occur as well - self.actual = match &self.used { + match &self.used { CssValue::Number(len) => CssValue::Number(len.round()), CssValue::Percentage(perc) => CssValue::Percentage(perc.round()), CssValue::Unit(value, unit) => CssValue::Unit(value.round(), unit.clone()), _ => self.used.clone(), } } + + + // Returns true when the property is inheritable, false otherwise + fn is_inheritable(&self) -> bool { + crate::property_list::PROPERTY_TABLE + .iter() + .find(|entry| entry.name == self.name) + .map(|entry| entry.inheritable) + .unwrap_or(false) + } + + // Returns the initial value for the property, if any + fn get_initial_value(&self) -> Option { + crate::property_list::PROPERTY_TABLE + .iter() + .find(|entry| entry.name == self.name) + .map(|entry| entry.initial) + } } -/// Map of all declared values for a single node +/// Map of all declared values for a single node. Note that these are only the defined properties, not +/// the non-existing properties. pub struct CssProperties { - properties: HashMap>, + properties: HashMap, } impl Default for CssProperties { @@ -395,7 +466,7 @@ impl CssProperties { /// Actual CSS value, can be a color, length, percentage, string or unit. Some relative values will be computed /// from other values (ie: Percent(50) will convert to Length(100) when the parent width is 200) -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum CssValue { None, Color(RgbColor), @@ -405,15 +476,27 @@ pub enum CssValue { Unit(f32, String), } +impl CssValue { + /// Converts the value to a string + pub fn to_string(&self) -> String { + match self { + CssValue::None => "".into(), + CssValue::Color(col) => format!("#{:02x}{:02x}{:02x}{:02x}", col.r, col.g, col.b, col.a), + CssValue::Number(num) => format!("{}", num), + CssValue::Percentage(p) => format!("{}%", p), + CssValue::String(s) => s.clone(), + CssValue::Unit(val, unit) => format!("{}{}", val, unit), + } + } +} + /// Map of all declared values for all nodes in the document pub struct CssNodeTree { nodes: HashMap, - #[allow(dead_code)] document: DocumentHandle, } impl CssNodeTree { - #[allow(dead_code)] /// Creates a new CssNodeTree for the given document pub fn new(doc: DocumentHandle) -> Self { Self { @@ -422,34 +505,44 @@ impl CssNodeTree { } } + /// Returns a clone of the document handle + pub fn get_document(&self) -> DocumentHandle { + DocumentHandle::clone(&self.document) + } + /// Mark the given node as dirty, so it will be recalculated pub fn mark_dirty(&mut self, node_id: NodeId) { - for prop in self.nodes[&node_id].properties.values() { - prop.borrow_mut().mark_dirty(); + match self.nodes.get_mut(&node_id) { + Some(props) => { + for prop in props.properties.values_mut() { + prop.mark_dirty(); + } + } + None => {} } } /// Mark the given node as clean, so it will not be recalculated - pub fn mark_clean(&self, node_id: NodeId) { - for prop in self.nodes[&node_id].properties.values() { - prop.borrow_mut().mark_clean(); + pub fn mark_clean(&mut self, node_id: NodeId) { + match self.nodes.get_mut(&node_id) { + Some(props) => { + for prop in props.properties.values_mut() { + prop.mark_clean(); + } + } + None => {} } } /// Retrieves the property for the given node, or None when not found pub fn get_property(&self, node_id: NodeId, prop_name: &str) -> Option { let props = self.nodes.get(&node_id); - match props { - Some(p) => { - let entry = p.properties.get(prop_name); - match entry { - Some(e) => { - let prop = e.take().clone(); - Some(prop) - } - None => None, - } - } + if props.is_none() { + return None; + } + + match props.expect("props").properties.get(prop_name) { + Some(entry) => Some(entry.clone()), None => None, } } @@ -458,23 +551,6 @@ impl CssNodeTree { pub fn get_all_properties(&self, node_id: NodeId) -> Option<&CssProperties> { self.nodes.get(&node_id) } - - // Returns true when the property is inheritable, false otherwise - pub fn is_inheritable(&self, prop_name: &str) -> bool { - crate::property_list::PROPERTY_TABLE - .iter() - .find(|entry| entry.name == prop_name) - .map(|entry| entry.inheritable) - .unwrap_or(false) - } - - // Returns the initial value for the property, if any - pub fn get_initial_value(&self, prop_name: &str) -> Option<&str> { - crate::property_list::PROPERTY_TABLE - .iter() - .find(|entry| entry.name == prop_name) - .map(|entry| entry.initial) - } } #[cfg(test)] diff --git a/crates/gosub_styling/src/pipeline.rs b/crates/gosub_styling/src/pipeline.rs deleted file mode 100644 index b439fec8b..000000000 --- a/crates/gosub_styling/src/pipeline.rs +++ /dev/null @@ -1,53 +0,0 @@ -use gosub_html5::node::NodeId; -use gosub_html5::parser::document::DocumentHandle; - -/// The rendering pipeline to convert a document and stylesheets into a rendered page -/// It's a very simple pipeline with a single step (generate_render_tree). But more -/// will follow later. -pub struct Pipeline {} - -impl Default for Pipeline { - fn default() -> Self { - Self::new() - } -} - -impl Pipeline { - pub fn new() -> Self { - Self {} - } - - /// Generates a render tree by duplicating the DOM tree and removing all nodes that are not renderable or hidden. - pub fn generate_render_tree(&self, doc_handle: DocumentHandle) -> DocumentHandle { - // Create a complete copy of the document tree into the render tree - let mut rendertree_handle = doc_handle.deep_clone(); - - // Iterate tree and remove elements that are hidden or not renderable - remove_unrenderable_nodes(&mut rendertree_handle, NodeId::root()); - - DocumentHandle::clone(&rendertree_handle) - } -} - -fn remove_unrenderable_nodes(rendertree_handle: &mut DocumentHandle, node_id: NodeId) { - let node; - { - let binding = rendertree_handle.get(); - node = binding.get_node_by_id(node_id).unwrap().clone(); - } - - // There are more elements that are not renderable, but for now we only remove the most common ones - let removable_elements = ["head", "script", "style", "svg"]; - - if node.is_element() && removable_elements.contains(&node.as_element().name.as_str()) { - rendertree_handle.get_mut().delete_node(&node); - return; - } - - // Check CSS styles and remove if not renderable - - // Iterate all children from this node - for &child_id in &node.children { - remove_unrenderable_nodes(rendertree_handle, child_id); - } -} diff --git a/crates/gosub_styling/src/property_list.rs b/crates/gosub_styling/src/property_list.rs index 87903bc0b..cec538123 100644 --- a/crates/gosub_styling/src/property_list.rs +++ b/crates/gosub_styling/src/property_list.rs @@ -1,11 +1,12 @@ use lazy_static::lazy_static; +use crate::css_node_tree::CssValue; // Values for this table is taken from https://www.w3.org/TR/CSS21/propidx.html // Probably not the complete list, but it will do for now pub struct PropertyTableEntry { pub(crate) name: &'static str, - pub(crate) initial: &'static str, + pub(crate) initial: CssValue, pub(crate) inheritable: bool, } @@ -13,562 +14,562 @@ lazy_static! { pub static ref PROPERTY_TABLE: &'static [PropertyTableEntry] = &[ PropertyTableEntry { name: "azimuth", - initial: "center", + initial: CssValue::String("center".into()), inheritable: true, }, PropertyTableEntry { name: "background-attachment", - initial: "scroll", + initial: CssValue::String("scroll".into()), inheritable: false, }, PropertyTableEntry { name: "background-color", - initial: "transparent", + initial: CssValue::String("transparent".into()), inheritable: false, }, PropertyTableEntry { name: "background-image", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "background-position", - initial: "0% 0%", + initial: CssValue::String("0% 0%".into()), inheritable: false, }, PropertyTableEntry { name: "background-repeat", - initial: "repeat", + initial: CssValue::String("repeat".into()), inheritable: false, }, PropertyTableEntry { name: "border-collapse", - initial: "separate", + initial: CssValue::String("separate".into()), inheritable: false, }, PropertyTableEntry { name: "border-color", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-spacing", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "border-style", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "border-top", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-right", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-bottom", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-left", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-top-color", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-right-color", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-bottom-color", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-left-color", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "border-top-style", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "border-right-style", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "border-bottom-style", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "border-left-style", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "border-top-width", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "border-right-width", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "border-bottom-width", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "border-left-width", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "border-width", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "bottom", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "caption-side", - initial: "top", + initial: CssValue::String("top".into()), inheritable: false, }, PropertyTableEntry { name: "clear", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "clip", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "color", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: true, }, PropertyTableEntry { name: "content", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: false, }, PropertyTableEntry { name: "counter-increment", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "counter-reset", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "cue", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "cue-after", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "cue-before", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "cursor", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "direction", - initial: "ltr", + initial: CssValue::String("ltr".into()), inheritable: true, }, PropertyTableEntry { name: "display", - initial: "inline", + initial: CssValue::String("inline".into()), inheritable: false, }, PropertyTableEntry { name: "elevation", - initial: "level", + initial: CssValue::String("level".into()), inheritable: false, }, PropertyTableEntry { name: "empty-cells", - initial: "show", + initial: CssValue::String("show".into()), inheritable: false, }, PropertyTableEntry { name: "float", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "font", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "font-family", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "font-size", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: true, }, PropertyTableEntry { name: "font-style", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "font-variant", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "font-weight", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "height", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "left", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "letter-spacing", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "line-height", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "list-style", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "list-style-image", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "list-style-position", - initial: "outside", + initial: CssValue::String("outside".into()), inheritable: false, }, PropertyTableEntry { name: "list-style-type", - initial: "disc", + initial: CssValue::String("disc".into()), inheritable: false, }, PropertyTableEntry { name: "margin", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "margin-top", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "margin-right", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "margin-bottom", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "margin-left", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "max-height", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "max-width", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "min-height", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "min-width", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "orphans", - initial: "2", + initial: CssValue::Number(2_f32), inheritable: true, }, PropertyTableEntry { name: "outline", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "outline-color", - initial: "invert", + initial: CssValue::String("invert".into()), inheritable: false, }, PropertyTableEntry { name: "outline-style", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "outline-width", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "overflow", - initial: "visible", + initial: CssValue::String("visible".into()), inheritable: false, }, PropertyTableEntry { name: "padding", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "padding-top", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "padding-right", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "padding-bottom", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "padding-left", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "page-break-after", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "page-break-before", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "page-break-inside", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "pause-after", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "pause-before", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: false, }, PropertyTableEntry { name: "pitch", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "pitch-range", - initial: "50", + initial: CssValue::Number(50_f32), inheritable: false, }, PropertyTableEntry { name: "play-during", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "position", - initial: "static", + initial: CssValue::String("static".into()), inheritable: false, }, PropertyTableEntry { name: "quotes", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "richness", - initial: "50", + initial: CssValue::Number(50_f32), inheritable: false, }, PropertyTableEntry { name: "right", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "speak", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: false, }, PropertyTableEntry { name: "speak-header", - initial: "once", + initial: CssValue::String("once".into()), inheritable: false, }, PropertyTableEntry { name: "speak-numeral", - initial: "continuous", + initial: CssValue::String("continuous".into()), inheritable: false, }, PropertyTableEntry { name: "speak-punctuation", - initial: "none", + initial: CssValue::String("none".into()), inheritable: false, }, PropertyTableEntry { name: "speech-rate", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "stress", - initial: "50", + initial: CssValue::Number(50_f32), inheritable: false, }, PropertyTableEntry { name: "table-layout", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "text-align", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: true, }, PropertyTableEntry { name: "text-decoration", - initial: "none", + initial: CssValue::String("none".into()), inheritable: true, }, PropertyTableEntry { name: "text-indent", - initial: "0", + initial: CssValue::Number(0_f32), inheritable: true, }, PropertyTableEntry { name: "text-transform", - initial: "none", + initial: CssValue::String("none".into()), inheritable: true, }, PropertyTableEntry { name: "top", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "unicode-bidi", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "vertical-align", - initial: "baseline", + initial: CssValue::String("baseline".into()), inheritable: true, }, PropertyTableEntry { name: "visibility", - initial: "visible", + initial: CssValue::String("visible".into()), inheritable: false, }, PropertyTableEntry { name: "voice-family", - initial: "initial", + initial: CssValue::String("initial".into()), inheritable: false, }, PropertyTableEntry { name: "volume", - initial: "medium", + initial: CssValue::String("medium".into()), inheritable: false, }, PropertyTableEntry { name: "white-space", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "widows", - initial: "2", + initial: CssValue::Number(2_f32), inheritable: true, }, PropertyTableEntry { name: "width", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, PropertyTableEntry { name: "word-spacing", - initial: "normal", + initial: CssValue::String("normal".into()), inheritable: true, }, PropertyTableEntry { name: "z-index", - initial: "auto", + initial: CssValue::String("auto".into()), inheritable: false, }, ];