Skip to content

Commit

Permalink
Merge pull request #600 from Sharktheone/css/attr-function
Browse files Browse the repository at this point in the history
Implement some css function, most notably `attr()`
  • Loading branch information
Sharktheone authored Sep 15, 2024
2 parents 5a163d1 + 47bfe91 commit 503c4fb
Show file tree
Hide file tree
Showing 9 changed files with 176 additions and 20 deletions.
43 changes: 36 additions & 7 deletions crates/gosub_css3/src/convert/ast_converter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::stylesheet::{
};
use anyhow::anyhow;
use gosub_shared::types::Result;
use log::warn;

/*
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 2 additions & 3 deletions crates/gosub_css3/src/stylesheet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -447,7 +447,6 @@ mod test {
use std::vec;

use super::*;
use std::vec;

// #[test]
// fn test_css_value_to_color() {
Expand Down
8 changes: 7 additions & 1 deletion crates/gosub_styling/resources/useragent.css
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
/* noscript is handled internally, as it depends on settings. */


[bgcolor] {
background-color: attr(bgcolor);
}

7 changes: 7 additions & 0 deletions crates/gosub_styling/src/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub use attr::*;
pub use calc::*;
pub use var::*;

mod attr;
mod calc;
mod var;
26 changes: 26 additions & 0 deletions crates/gosub_styling/src/functions/attr.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use gosub_css3::stylesheet::CssValue;
use gosub_html5::node::Node;

pub fn resolve_attr(values: &[CssValue], node: &Node) -> Vec<CssValue> {
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]
}
5 changes: 5 additions & 0 deletions crates/gosub_styling/src/functions/calc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use gosub_css3::stylesheet::CssValue;

pub fn resolve_calc(_values: &[CssValue]) -> Vec<CssValue> {
todo!()
}
59 changes: 59 additions & 0 deletions crates/gosub_styling/src/functions/var.rs
Original file line number Diff line number Diff line change
@@ -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<String, CssValue>,
}

impl VariableEnvironment {
pub fn get(&self, name: &str, _doc: &Document, _node: &Node) -> Option<CssValue> {
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<CssValue> {
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()]
}
1 change: 1 addition & 0 deletions crates/gosub_styling/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
42 changes: 33 additions & 9 deletions crates/gosub_styling/src/render_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -286,10 +287,6 @@ impl<L: Layouter> RenderTree<L> {
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(
Expand All @@ -315,9 +312,10 @@ impl<L: Layouter> RenderTree<L> {
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;
Expand All @@ -327,7 +325,7 @@ impl<L: Layouter> RenderTree<L> {
let property_name = declaration.property.clone();
let decl = CssDeclaration {
property: property_name.to_string(),
value: declaration.value.clone(),
value,
important: declaration.important,
};

Expand Down Expand Up @@ -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<CssValue> {
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<dyn TreeVisitor<RenderTreeNode>>) {
// let root = tree.get_root();
// internal_walk_render_tree(tree, root, visitor);
Expand Down

0 comments on commit 503c4fb

Please sign in to comment.