[librsvg: 2/3] CssRules.get_matches() - Move CSS matching logic to here



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]