diff --git a/crates/gosub_css3/src/convert/ast_converter.rs b/crates/gosub_css3/src/convert/ast_converter.rs index 3ea8436fa..ba05aa2ca 100644 --- a/crates/gosub_css3/src/convert/ast_converter.rs +++ b/crates/gosub_css3/src/convert/ast_converter.rs @@ -5,6 +5,7 @@ use crate::stylesheet::{ }; use anyhow::anyhow; use gosub_shared::types::Result; +use log::warn; /* @@ -129,13 +130,41 @@ pub fn convert_ast_to_stylesheet( CssSelectorPart::Type(value.clone()) } NodeType::AttributeSelector { - name, value, flags, .. - } => CssSelectorPart::Attribute(Box::new(AttributeSelector { - name: name.clone(), - matcher: MatcherType::Equals, // @todo: this needs to be parsed - value: value.clone(), - case_insensitive: flags.eq_ignore_ascii_case("i"), - })), + name, + value, + flags, + matcher, + } => { + let matcher = match matcher { + None => MatcherType::None, + + Some(matcher) => match &*matcher.node_type { + NodeType::Operator(op) => match op.as_str() { + "=" => MatcherType::Equals, + "~=" => MatcherType::Includes, + "|=" => MatcherType::DashMatch, + "^=" => MatcherType::PrefixMatch, + "$=" => MatcherType::SuffixMatch, + "*=" => MatcherType::SubstringMatch, + _ => { + warn!("Unsupported matcher: {:?}", matcher); + MatcherType::Equals + } + }, + _ => { + warn!("Unsupported matcher: {:?}", matcher); + MatcherType::Equals + } + }, + }; + + CssSelectorPart::Attribute(Box::new(AttributeSelector { + name: name.clone(), + matcher, + value: value.clone(), + case_insensitive: flags.eq_ignore_ascii_case("i"), + })) + } NodeType::Comma => { selector.parts.push(vec![]); continue; diff --git a/crates/gosub_css3/src/stylesheet.rs b/crates/gosub_css3/src/stylesheet.rs index 0965e07d3..d64fa7349 100644 --- a/crates/gosub_css3/src/stylesheet.rs +++ b/crates/gosub_css3/src/stylesheet.rs @@ -91,7 +91,7 @@ pub enum CssSelectorPart { Type(String), } -#[derive(PartialEq, Clone, Default)] +#[derive(PartialEq, Clone, Default, Debug)] pub struct AttributeSelector { pub name: String, pub matcher: MatcherType, @@ -172,7 +172,7 @@ pub enum CssSelectorType { } /// Represents which type of matcher is used (in case of an attribute selector type) -#[derive(Default, PartialEq, Clone)] +#[derive(Default, PartialEq, Clone, Debug)] pub enum MatcherType { #[default] None, // No matcher @@ -447,7 +447,6 @@ mod test { use std::vec; use super::*; - use std::vec; // #[test] // fn test_css_value_to_color() { diff --git a/crates/gosub_styling/resources/useragent.css b/crates/gosub_styling/resources/useragent.css index 7bc87e069..36d5f5f31 100644 --- a/crates/gosub_styling/resources/useragent.css +++ b/crates/gosub_styling/resources/useragent.css @@ -1305,4 +1305,10 @@ html::spelling-error { html::grammar-error { text-decoration: -internal-grammar-error-color grammar-error; } -/* noscript is handled internally, as it depends on settings. */ \ No newline at end of file +/* noscript is handled internally, as it depends on settings. */ + + +[bgcolor] { + background-color: attr(bgcolor); +} + diff --git a/crates/gosub_styling/src/functions.rs b/crates/gosub_styling/src/functions.rs new file mode 100644 index 000000000..7e0dfe812 --- /dev/null +++ b/crates/gosub_styling/src/functions.rs @@ -0,0 +1,7 @@ +pub use attr::*; +pub use calc::*; +pub use var::*; + +mod attr; +mod calc; +mod var; diff --git a/crates/gosub_styling/src/functions/attr.rs b/crates/gosub_styling/src/functions/attr.rs new file mode 100644 index 000000000..56ca22797 --- /dev/null +++ b/crates/gosub_styling/src/functions/attr.rs @@ -0,0 +1,26 @@ +use gosub_css3::stylesheet::CssValue; +use gosub_html5::node::Node; + +pub fn resolve_attr(values: &[CssValue], node: &Node) -> Vec { + let Some(attr_name) = values.first().map(|v| v.to_string()) else { + return vec![]; + }; + + let ty = values.get(1).cloned(); + + let Some(attr_value) = node.get_attribute(&attr_name) else { + let _default_value = values.get(2).cloned(); + + if let Some(ty) = ty { + return vec![ty]; + } + + return vec![]; + }; + + let Ok(value) = CssValue::parse_str(attr_value) else { + return vec![]; + }; + + vec![value] +} diff --git a/crates/gosub_styling/src/functions/calc.rs b/crates/gosub_styling/src/functions/calc.rs new file mode 100644 index 000000000..5e24bd79f --- /dev/null +++ b/crates/gosub_styling/src/functions/calc.rs @@ -0,0 +1,5 @@ +use gosub_css3::stylesheet::CssValue; + +pub fn resolve_calc(_values: &[CssValue]) -> Vec { + todo!() +} diff --git a/crates/gosub_styling/src/functions/var.rs b/crates/gosub_styling/src/functions/var.rs new file mode 100644 index 000000000..1180456ca --- /dev/null +++ b/crates/gosub_styling/src/functions/var.rs @@ -0,0 +1,59 @@ +use std::collections::HashMap; + +use gosub_css3::stylesheet::CssValue; +use gosub_html5::node::Node; +use gosub_html5::parser::document::Document; + +#[derive(Clone, Debug, Default)] +pub struct VariableEnvironment { + pub values: HashMap, +} + +impl VariableEnvironment { + pub fn get(&self, name: &str, _doc: &Document, _node: &Node) -> Option { + let mut current = Some(self); + + while let Some(env) = current { + if let Some(value) = env.values.get(name) { + return Some(value.clone()); + } + + current = None; + + //TODO: give node a variable env + // let node = doc.get_parent(node); + // current = node.get_variable_env(); + } + + None + } +} + +pub fn resolve_var(values: &[CssValue], doc: &Document, node: &Node) -> Vec { + let Some(name) = values.first().map(|v| { + let mut str = v.to_string(); + + if str.starts_with("--") { + str.remove(0); + str.remove(0); + } + + str + }) else { + return vec![]; + }; + + // let environment = doc.get_variable_env(node); + + let environment = VariableEnvironment::default(); //TODO: get from node + + let Some(value) = environment.get(&name, doc, node) else { + let Some(default) = values.get(1).cloned() else { + return vec![]; + }; + + return vec![default]; + }; + + vec![value.clone()] +} diff --git a/crates/gosub_styling/src/lib.rs b/crates/gosub_styling/src/lib.rs index 4fd03c32b..3ca26bbe9 100644 --- a/crates/gosub_styling/src/lib.rs +++ b/crates/gosub_styling/src/lib.rs @@ -9,6 +9,7 @@ use gosub_css3::stylesheet::{CssOrigin, CssStylesheet}; use gosub_css3::Css3; mod errors; +mod functions; pub mod property_definitions; pub mod render_tree; mod shorthands; diff --git a/crates/gosub_styling/src/render_tree.rs b/crates/gosub_styling/src/render_tree.rs index 4957f21a8..14761552a 100644 --- a/crates/gosub_styling/src/render_tree.rs +++ b/crates/gosub_styling/src/render_tree.rs @@ -3,14 +3,15 @@ use std::fmt::{Debug, Formatter}; use log::warn; -use gosub_css3::stylesheet::{CssDeclaration, CssOrigin, CssStylesheet, CssValue, Specificity}; +use gosub_css3::stylesheet::{CssDeclaration, CssStylesheet, CssValue, Specificity}; use gosub_html5::node::data::element::ElementData; use gosub_html5::node::{NodeData, NodeId}; -use gosub_html5::parser::document::{DocumentHandle, TreeIterator}; +use gosub_html5::parser::document::{Document, DocumentHandle, TreeIterator}; use gosub_render_backend::geo::Size; use gosub_render_backend::layout::{HasTextLayout, Layout, LayoutTree, Layouter, Node, TextLayout}; use gosub_shared::types::Result; +use crate::functions::{resolve_attr, resolve_calc, resolve_var}; use crate::property_definitions::get_css_definitions; use crate::shorthands::FixList; use crate::styling::{ @@ -286,10 +287,6 @@ impl RenderTree { let mut fix_list = FixList::new(); for sheet in document.get().stylesheets.iter() { - if sheet.origin == CssOrigin::UserAgent { - continue; - } - for rule in sheet.rules.iter() { for selector in rule.selectors().iter() { let (matched, specificity) = match_selector( @@ -315,9 +312,10 @@ impl RenderTree { continue; }; + let value = resolve_functions(&declaration.value, node, &doc); + // Check if the declaration matches the definition and return the "expanded" order - let res = definition - .matches_and_shorthands(&declaration.value, &mut fix_list); + let res = definition.matches_and_shorthands(&value, &mut fix_list); if !res { warn!("Declaration does not match definition: {:?}", declaration); continue; @@ -327,7 +325,7 @@ impl RenderTree { let property_name = declaration.property.clone(); let decl = CssDeclaration { property: property_name.to_string(), - value: declaration.value.clone(), + value, important: declaration.important, }; @@ -884,6 +882,32 @@ pub fn node_is_undernderable(node: &gosub_html5::node::Node) -> bool { false } +pub fn resolve_functions( + value: &[CssValue], + node: &gosub_html5::node::Node, + doc: &Document, +) -> Vec { + let mut result = Vec::with_capacity(value.len()); //TODO: we could give it a &mut Vec and reuse the allocation + + for val in value { + match val { + CssValue::Function(func, values) => { + let resolved = match func.as_str() { + "calc" => resolve_calc(values), + "attr" => resolve_attr(values, node), + "var" => resolve_var(values, doc, node), + _ => vec![val.clone()], + }; + + result.extend(resolved); + } + _ => result.push(val.clone()), + } + } + + result +} + // pub fn walk_render_tree(tree: &RenderTree, visitor: &mut Box>) { // let root = tree.get_root(); // internal_walk_render_tree(tree, root, visitor);