Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement some css function, most notably attr() #600

Merged
merged 5 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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