[librsvg: 2/3] CssRules.get_matches() - Move CSS matching logic to here
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 2/3] CssRules.get_matches() - Move CSS matching logic to here
- Date: Mon, 6 May 2019 22:34:23 +0000 (UTC)
commit 670a423f20dda1bb3685ddb067cabb441225aaf4
Author: Federico Mena Quintero <federico gnome org>
Date: Mon May 6 16:58:49 2019 -0500
CssRules.get_matches() - Move CSS matching logic to here
This is done in about the worst way possible, but it's because of our
stringified representation of selectors. It will get better soon.
rsvg_internals/src/css.rs | 100 ++++++++++++++++++++++++++++++++++++--
rsvg_internals/src/node.rs | 116 +++++----------------------------------------
rsvg_internals/src/xml.rs | 2 +-
3 files changed, 111 insertions(+), 107 deletions(-)
---
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
index fb7f41d9..384c8b49 100644
--- a/rsvg_internals/src/css.rs
+++ b/rsvg_internals/src/css.rs
@@ -17,6 +17,7 @@ use crate::attributes::Attribute;
use crate::croco::*;
use crate::error::*;
use crate::io::{self, BinaryData};
+use crate::node::RsvgNode;
use crate::properties::{parse_attribute_value_into_parsed_property, ParsedProperty};
use crate::util::utf8_cstr;
@@ -67,8 +68,8 @@ impl<'i> AtRuleParser<'i> for DeclParser {
type Error = ValueErrorKind;
}
-#[derive(Hash, PartialEq, Eq)]
-struct Selector(String);
+#[derive(Clone, Hash, PartialEq, Eq)]
+pub struct Selector(String);
impl Selector {
fn new(s: &str) -> Selector {
@@ -203,7 +204,100 @@ impl CssRules {
}
pub fn lookup(&self, selector: &str) -> Option<&DeclarationList> {
- self.selectors_to_declarations.get(&Selector::new(selector))
+ self.get_declarations(&Selector::new(selector))
+ }
+
+ pub fn get_declarations(&self, selector: &Selector) -> Option<&DeclarationList> {
+ self.selectors_to_declarations.get(selector)
+ }
+
+ fn selector_matches_node(&self, selector: &Selector, node: &RsvgNode) -> bool {
+ // Try to properly support all of the following, including inheritance:
+ // *
+ // #id
+ // tag
+ // tag#id
+ // tag.class
+ // tag.class#id
+ //
+ // This is basically a semi-compliant CSS2 selection engine
+
+ let element_name = node.get_type().element_name();
+ let id = node.get_id();
+
+ // *
+ if *selector == Selector::new("*") {
+ return true;
+ }
+
+ // tag
+ if *selector == Selector::new(element_name) {
+ return true;
+ }
+
+ if let Some(klazz) = node.get_class() {
+ for cls in klazz.split_whitespace() {
+ if !cls.is_empty() {
+ // tag.class#id
+ if let Some(id) = id {
+ let target = format!("{}.{}#{}", element_name, cls, id);
+ if *selector == Selector::new(&target) {
+ return true;
+ }
+ }
+
+ // .class#id
+ if let Some(id) = id {
+ let target = format!(".{}#{}", cls, id);
+ if *selector == Selector::new(&target) {
+ return true;
+ }
+ }
+
+ // tag.class
+ let target = format!("{}.{}", element_name, cls);
+ if *selector == Selector::new(&target) {
+ return true;
+ }
+
+ // didn't find anything more specific, just apply the class style
+ let target = format!(".{}", cls);
+ if *selector == Selector::new(&target) {
+ return true;
+ }
+ }
+ }
+ }
+
+ if let Some(id) = id {
+ // id
+ let target = format!("#{}", id);
+ if *selector == Selector::new(&target) {
+ return true;
+ }
+
+ // tag#id
+ let target = format!("{}#{}", element_name, id);
+ if *selector == Selector::new(&target) {
+ return true;
+ }
+ }
+
+ false
+ }
+
+ pub fn get_matches(&self, node: &RsvgNode) -> Vec<Selector> {
+ self.selectors_to_declarations
+ .iter()
+ .filter_map(|(selector, _)| {
+ if self.selector_matches_node(selector, node) {
+ Some(selector)
+ } else {
+ None
+ }
+ })
+ .map(Selector::clone)
+ .collect()
}
}
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index e474ffd4..1746bb8a 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -218,7 +218,7 @@ pub enum NodeType {
}
impl NodeType {
- fn element_name(&self) -> &'static str {
+ pub fn element_name(&self) -> &'static str {
match self {
NodeType::Chars => "rsvg-chars", // Dummy element name for chars
NodeType::Circle => "circle",
@@ -349,11 +349,11 @@ impl Node {
}
}
- pub fn set_styles_recursively(&self, css_rules: &CssRules) {
- self.set_style(css_rules);
+ pub fn set_styles_recursively(&self, node: &RsvgNode, css_rules: &CssRules) {
+ self.set_style(node, css_rules);
for child in self.children() {
- child.set_styles_recursively(css_rules);
+ child.set_styles_recursively(&child, css_rules);
}
}
@@ -469,90 +469,19 @@ impl Node {
}
}
- /// Implements a very limited CSS selection engine
- fn set_css_styles(&self, css_rules: &CssRules) {
- // Try to properly support all of the following, including inheritance:
- // *
- // #id
- // tag
- // tag#id
- // tag.class
- // tag.class#id
- //
- // This is basically a semi-compliant CSS2 selection engine
-
- let element_name = self.get_type().element_name();
+ /// Applies the CSS rules that match into the node's specified_values
+ fn set_css_styles(&self, node: &RsvgNode, css_rules: &CssRules) {
let mut specified_values = self.data.specified_values.borrow_mut();
let mut important_styles = self.data.important_styles.borrow_mut();
- // *
- try_apply_by_selector(css_rules, "*", &mut specified_values, &mut important_styles);
-
- // tag
- try_apply_by_selector(css_rules, element_name, &mut specified_values, &mut important_styles);
-
- if let Some(klazz) = self.get_class() {
- for cls in klazz.split_whitespace() {
- let mut found = false;
-
- if !cls.is_empty() {
- // tag.class#id
- if let Some(id) = self.get_id() {
- let target = format!("{}.{}#{}", element_name, cls, id);
- found = found
- || try_apply_by_selector(
- css_rules,
- &target,
- &mut specified_values,
- &mut important_styles,
- );
- }
-
- // .class#id
- if let Some(id) = self.get_id() {
- let target = format!(".{}#{}", cls, id);
- found = found
- || try_apply_by_selector(
- css_rules,
- &target,
- &mut specified_values,
- &mut important_styles,
- );
- }
-
- // tag.class
- let target = format!("{}.{}", element_name, cls);
- found = found
- || try_apply_by_selector(
- css_rules,
- &target,
- &mut specified_values,
- &mut important_styles,
- );
-
- if !found {
- // didn't find anything more specific, just apply the class style
- let target = format!(".{}", cls);
- try_apply_by_selector(
- css_rules,
- &target,
- &mut specified_values,
- &mut important_styles,
- );
- }
+ for selector in &css_rules.get_matches(node) {
+ if let Some(decl_list) = css_rules.get_declarations(selector) {
+ for declaration in decl_list.iter() {
+ specified_values
+ .set_property_from_declaration(declaration, &mut important_styles);
}
}
}
-
- if let Some(id) = self.get_id() {
- // id
- let target = format!("#{}", id);
- try_apply_by_selector(css_rules, &target, &mut specified_values, &mut important_styles);
-
- // tag#id
- let target = format!("{}#{}", element_name, id);
- try_apply_by_selector(css_rules, &target, &mut specified_values, &mut important_styles);
- }
}
/// Applies CSS styles from the saved value of the "style" attribute
@@ -578,8 +507,8 @@ impl Node {
// Sets the node's specified values from the style-related attributes in the pbag.
// Also applies CSS rules in our limited way based on the node's tag/class/id.
- fn set_style(&self, css_rules: &CssRules) {
- self.set_css_styles(css_rules);
+ fn set_style(&self, node: &RsvgNode, css_rules: &CssRules) {
+ self.set_css_styles(node, css_rules);
self.set_style_attribute();
}
@@ -704,22 +633,3 @@ pub fn node_new(
node_impl,
))
}
-
-/// takes CSS rules which match the given `selector` name and applies them
-/// to the `values`.
-pub fn try_apply_by_selector(
- css_rules: &CssRules,
- selector: &str,
- values: &mut SpecifiedValues,
- important_styles: &mut HashSet<Attribute>,
-) -> bool {
- if let Some(decl_list) = css_rules.lookup(selector) {
- for declaration in decl_list.iter() {
- values.set_property_from_declaration(declaration, important_styles);
- }
-
- true
- } else {
- false
- }
-}
diff --git a/rsvg_internals/src/xml.rs b/rsvg_internals/src/xml.rs
index d5a68188..740dafea 100644
--- a/rsvg_internals/src/xml.rs
+++ b/rsvg_internals/src/xml.rs
@@ -119,7 +119,7 @@ impl XmlState {
if root.get_type() == NodeType::Svg {
let root = self.tree_root.take().unwrap();
- root.set_styles_recursively(self.css_rules.as_ref().unwrap());
+ root.set_styles_recursively(&root, self.css_rules.as_ref().unwrap());
Ok(Svg::new(
root,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]