From 7e878508b398957c14d23fb7471e433cdfe2b29f Mon Sep 17 00:00:00 2001 From: Shark Date: Fri, 13 Sep 2024 12:58:44 +0200 Subject: [PATCH 1/5] implement some css functions (e.g `attr()`) --- crates/gosub_styling/src/functions.rs | 7 +++++ crates/gosub_styling/src/functions/attr.rs | 17 ++++++++++++ crates/gosub_styling/src/functions/calc.rs | 5 ++++ crates/gosub_styling/src/functions/var.rs | 1 + crates/gosub_styling/src/lib.rs | 1 + crates/gosub_styling/src/render_tree.rs | 30 +++++++++++++++++++--- 6 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 crates/gosub_styling/src/functions.rs create mode 100644 crates/gosub_styling/src/functions/attr.rs create mode 100644 crates/gosub_styling/src/functions/calc.rs create mode 100644 crates/gosub_styling/src/functions/var.rs 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..e0406325c --- /dev/null +++ b/crates/gosub_styling/src/functions/attr.rs @@ -0,0 +1,17 @@ +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 Some(attr_value) = node.get_attribute(&attr_name) else { + 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..37c2a6e74 --- /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..8b1378917 --- /dev/null +++ b/crates/gosub_styling/src/functions/var.rs @@ -0,0 +1 @@ + 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..474a6c46f 100644 --- a/crates/gosub_styling/src/render_tree.rs +++ b/crates/gosub_styling/src/render_tree.rs @@ -11,6 +11,7 @@ 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}; use crate::property_definitions::get_css_definitions; use crate::shorthands::FixList; use crate::styling::{ @@ -315,9 +316,10 @@ impl RenderTree { continue; }; + let value = resolve_functions(&declaration.value, node); + // 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 +329,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 +886,28 @@ pub fn node_is_undernderable(node: &gosub_html5::node::Node) -> bool { false } +pub fn resolve_functions(value: &[CssValue], node: &gosub_html5::node::Node) -> 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(func, 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); From 5836e80456f42a8599beab70cc4e70f8a1c7eb40 Mon Sep 17 00:00:00 2001 From: Shark Date: Fri, 13 Sep 2024 21:15:50 +0200 Subject: [PATCH 2/5] do some work on variables begin css variables --- crates/gosub_styling/src/functions/attr.rs | 11 +++- crates/gosub_styling/src/functions/calc.rs | 2 +- crates/gosub_styling/src/functions/var.rs | 58 ++++++++++++++++++++++ crates/gosub_styling/src/render_tree.rs | 16 +++--- 4 files changed, 79 insertions(+), 8 deletions(-) diff --git a/crates/gosub_styling/src/functions/attr.rs b/crates/gosub_styling/src/functions/attr.rs index e0406325c..56ca22797 100644 --- a/crates/gosub_styling/src/functions/attr.rs +++ b/crates/gosub_styling/src/functions/attr.rs @@ -5,11 +5,20 @@ 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 { + let Ok(value) = CssValue::parse_str(attr_value) else { return vec![]; }; diff --git a/crates/gosub_styling/src/functions/calc.rs b/crates/gosub_styling/src/functions/calc.rs index 37c2a6e74..5e24bd79f 100644 --- a/crates/gosub_styling/src/functions/calc.rs +++ b/crates/gosub_styling/src/functions/calc.rs @@ -1,5 +1,5 @@ use gosub_css3::stylesheet::CssValue; -pub fn resolve_calc(values: &[CssValue]) -> Vec { +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 index 8b1378917..1180456ca 100644 --- a/crates/gosub_styling/src/functions/var.rs +++ b/crates/gosub_styling/src/functions/var.rs @@ -1 +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/render_tree.rs b/crates/gosub_styling/src/render_tree.rs index 474a6c46f..ab06652ee 100644 --- a/crates/gosub_styling/src/render_tree.rs +++ b/crates/gosub_styling/src/render_tree.rs @@ -3,15 +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}; +use crate::functions::{resolve_attr, resolve_calc, resolve_var}; use crate::property_definitions::get_css_definitions; use crate::shorthands::FixList; use crate::styling::{ @@ -316,7 +316,7 @@ impl RenderTree { continue; }; - let value = resolve_functions(&declaration.value, node); + 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(&value, &mut fix_list); @@ -886,7 +886,11 @@ pub fn node_is_undernderable(node: &gosub_html5::node::Node) -> bool { false } -pub fn resolve_functions(value: &[CssValue], node: &gosub_html5::node::Node) -> Vec { +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 { @@ -895,7 +899,7 @@ pub fn resolve_functions(value: &[CssValue], node: &gosub_html5::node::Node) -> let resolved = match func.as_str() { "calc" => resolve_calc(values), "attr" => resolve_attr(values, node), - // "var" => resolve_var(func, node), + "var" => resolve_var(values, doc, node), _ => vec![val.clone()], }; From 3e66c789b1b817ba8c74e0304a4534650ec2227d Mon Sep 17 00:00:00 2001 From: Shark Date: Sat, 14 Sep 2024 16:24:14 +0200 Subject: [PATCH 3/5] support different attribute matchers --- .../gosub_css3/src/convert/ast_converter.rs | 43 ++++++++++++++++--- crates/gosub_css3/src/stylesheet.rs | 5 +-- 2 files changed, 38 insertions(+), 10 deletions(-) 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() { From 60470c6cf80cac1a0edf954522efef1f3560bb74 Mon Sep 17 00:00:00 2001 From: Shark Date: Sat, 14 Sep 2024 16:26:04 +0200 Subject: [PATCH 4/5] support `bgcolor` attribute --- crates/gosub_styling/resources/useragent.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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); +} + From 47bfe91146510a7bac078b464669b007be87fae5 Mon Sep 17 00:00:00 2001 From: Shark Date: Sat, 14 Sep 2024 16:27:01 +0200 Subject: [PATCH 5/5] remove skipping of useragent stylesheet --- crates/gosub_styling/src/render_tree.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/gosub_styling/src/render_tree.rs b/crates/gosub_styling/src/render_tree.rs index ab06652ee..14761552a 100644 --- a/crates/gosub_styling/src/render_tree.rs +++ b/crates/gosub_styling/src/render_tree.rs @@ -287,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(