[librsvg: 16/26] Implement cssparser::QualifiedRuleParser and do matching with the selectors crate



commit 644da9670925f089eec850050be4e8f834894ccc
Author: Federico Mena Quintero <federico gnome org>
Date:   Fri Nov 8 17:40:55 2019 -0600

    Implement cssparser::QualifiedRuleParser and do matching with the selectors crate
    
    WOOOOOOOOOOOOOOOHOOOOOOOOOOOOOOO!!!!!
    
    Only one test fails, the one for @import in the stylesheet.  We'll
    take care of that next.
    
    In this commit:
    
    - A new dummy QualRuleParser struct, which has impl QualifiedRuleParser
      and an initial dummy AtRuleParser.
    
    - A Stylesheet is now just Vec<Rule>.
    
    - Stylesheet is parsed with a cssparser::RuleListParser, which takes
      in our QualRuleParser.  It just stores the successfully parsed rules.
    
    - Remove our own struct Selector, since we now use selectors::SelectorList and
      its internal representation for selectors.
    
    - Instead of having a node request the list of matched selectors, and
      then the declarations for that selector's rule, we now have a lower-level
      NodeData.apply_style_declaration(declaration).
    
    - Matching happens through Stylesheet::apply_matches_to_node(node).
      This tries each rule's selector in the stylesheet, and for those
      that match, applies the rule's declarations to the node.

 rsvg_internals/src/croco.rs    | 135 ----------------
 rsvg_internals/src/css.rs      | 341 +++++------------------------------------
 rsvg_internals/src/document.rs |   6 +-
 rsvg_internals/src/node.rs     |  16 +-
 4 files changed, 43 insertions(+), 455 deletions(-)
---
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
index 2ed4eaf3..39a0289c 100644
--- a/rsvg_internals/src/css.rs
+++ b/rsvg_internals/src/css.rs
@@ -8,34 +8,28 @@ use cssparser::{
     Parser,
     ParserInput,
     QualifiedRuleParser,
+    RuleListParser,
     SourceLocation,
     ToCss,
 };
 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
-use selectors::matching::{ElementSelectorFlags, MatchingContext};
+use selectors::matching::{ElementSelectorFlags, MatchingContext, MatchingMode, QuirksMode};
 use selectors::{self, OpaqueElement, SelectorImpl, SelectorList};
 
-use std::collections::hash_map::{Entry, Iter as HashMapIter};
+use std::collections::hash_map::{Iter as HashMapIter};
 use std::collections::HashMap;
 use std::fmt;
-use std::ptr;
 use std::str;
 
-use libc;
 use markup5ever::{namespace_url, ns, LocalName, Namespace, Prefix, QualName};
 use url::Url;
 
-use glib::translate::*;
-use glib_sys::{gboolean, gpointer, GList};
-
 use crate::allowed_url::AllowedUrl;
-use crate::croco::*;
 use crate::error::*;
 use crate::io::{self, BinaryData};
-use crate::node::{NodeData, NodeType, RsvgNode};
+use crate::node::{NodeType, RsvgNode};
 use crate::properties::{parse_attribute_value_into_parsed_property, ParsedProperty};
 use crate::text::NodeChars;
-use crate::util::utf8_cstr;
 
 /// A parsed CSS declaration (`name: value [!important]`)
 pub struct Declaration {
@@ -160,6 +154,13 @@ impl<'i> QualifiedRuleParser<'i> for QualRuleParser {
     }
 }
 
+impl<'i> AtRuleParser<'i> for QualRuleParser {
+    type PreludeNoBlock = ();
+    type PreludeBlock = ();
+    type AtRule = Rule;
+    type Error = CssParseErrorKind<'i>;
+}
+
 /// Dummy type required by the SelectorImpl trait
 #[derive(Clone, Debug, Eq, PartialEq)]
 pub struct NonTSPseudoClass;
@@ -406,45 +407,13 @@ impl selectors::Element for RsvgElement {
     }
 }
 
-#[derive(Clone, Hash, PartialEq, Eq)]
-pub struct Selector {
-    name: String,
-    specificity: u64,
-}
-
-impl Selector {
-    fn new(s: &str, specificity: u64) -> Selector {
-        Selector {
-            name: s.to_string(),
-            specificity,
-        }
-    }
-}
-
 /// A parsed CSS stylesheet
 #[derive(Default)]
 pub struct Stylesheet {
-    /// Maps a selector name to a list of property/value declarations
-    selectors_to_declarations: HashMap<Selector, DeclarationList>,
+    rules: Vec<Rule>,
 }
 
 impl DeclarationList {
-    fn add_declaration(&mut self, declaration: Declaration) {
-        match self.declarations.entry(declaration.attribute.clone()) {
-            Entry::Occupied(mut e) => {
-                let decl = e.get_mut();
-
-                if !decl.important {
-                    *decl = declaration;
-                }
-            }
-
-            Entry::Vacant(v) => {
-                v.insert(declaration);
-            }
-        }
-    }
-
     pub fn iter(&self) -> DeclarationListIter {
         DeclarationListIter(self.declarations.iter())
     }
@@ -462,38 +431,16 @@ impl<'a> Iterator for DeclarationListIter<'a> {
 
 impl Stylesheet {
     pub fn parse(&mut self, base_url: Option<&Url>, buf: &str) {
-        if buf.is_empty() {
-            return; // libcroco doesn't like empty strings :(
-        }
-
-        unsafe {
-            let mut handler_data = DocHandlerData {
-                base_url,
-                stylesheet: self,
-                selector: ptr::null_mut(),
-            };
-
-            let doc_handler = cr_doc_handler_new();
-            init_cr_doc_handler(&mut *doc_handler);
-
-            (*doc_handler).app_data = &mut handler_data as *mut _ as gpointer;
+        let mut input = ParserInput::new(buf);
+        let mut parser = Parser::new(&mut input);
 
-            let buf_ptr = buf.as_ptr() as *mut _;
-            let buf_len = buf.len() as libc::c_ulong;
+        let rule_parser = RuleListParser::new_for_stylesheet(&mut parser, QualRuleParser);
 
-            let parser = cr_parser_new_from_buf(buf_ptr, buf_len, CR_UTF_8, false.to_glib());
-            if parser.is_null() {
-                cr_doc_handler_unref(doc_handler);
-                return;
+        for rule_result in rule_parser {
+            // Ignore invalid rules
+            if let Ok(rule) = rule_result {
+                self.rules.push(rule);
             }
-
-            cr_parser_set_sac_handler(parser, doc_handler);
-            cr_doc_handler_unref(doc_handler);
-
-            cr_parser_set_use_core_grammar(parser, false.to_glib());
-            cr_parser_parse(parser);
-
-            cr_parser_destroy(parser);
         }
     }
 
@@ -527,245 +474,29 @@ impl Stylesheet {
             })
     }
 
-    fn add_declaration(&mut self, selector: Selector, declaration: Declaration) {
-        let decl_list = self
-            .selectors_to_declarations
-            .entry(selector)
-            .or_insert_with(DeclarationList::default);
-
-        decl_list.add_declaration(declaration);
-    }
-
-    pub fn get_declarations(&self, selector: &Selector) -> Option<&DeclarationList> {
-        self.selectors_to_declarations.get(selector)
-    }
-
-    fn selector_matches_node(&self, selector: &Selector, node_data: &NodeData) -> 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_data.element_name().local.as_ref();
-        let id = node_data.get_id();
-
-        // *
-        if selector.name == "*" {
-            return true;
-        }
-
-        // tag
-        if selector.name == element_name {
-            return true;
-        }
-
-        if let Some(class) = node_data.get_class() {
-            for cls in class.split_whitespace() {
-                if !cls.is_empty() {
-                    // tag.class#id
-                    if let Some(id) = id {
-                        let target = format!("{}.{}#{}", element_name, cls, id);
-                        if selector.name == target {
-                            return true;
-                        }
-                    }
-
-                    // .class#id
-                    if let Some(id) = id {
-                        let target = format!(".{}#{}", cls, id);
-                        if selector.name == target {
-                            return true;
-                        }
-                    }
-
-                    // tag.class
-                    let target = format!("{}.{}", element_name, cls);
-                    if selector.name == target {
-                        return true;
-                    }
-
-                    // didn't find anything more specific, just apply the class style
-                    let target = format!(".{}", cls);
-                    if selector.name == target {
-                        return true;
-                    }
-                }
-            }
-        }
-
-        if let Some(id) = id {
-            // id
-            let target = format!("#{}", id);
-            if selector.name == target {
-                return true;
-            }
-
-            // tag#id
-            let target = format!("{}#{}", element_name, id);
-            if selector.name == target {
-                return true;
-            }
-        }
-
-        false
-    }
-
-    pub fn get_matches(&self, node_data: &NodeData) -> Vec<Selector> {
-        let mut matches: Vec<_> = self.selectors_to_declarations
-            .iter()
-            .filter_map(|(selector, _)| {
-                if self.selector_matches_node(selector, node_data) {
-                    Some(selector)
-                } else {
-                    None
-                }
-            })
-            .map(Selector::clone)
-            .collect();
-
-        matches.as_mut_slice().sort_by(|sel_a, sel_b| sel_a.specificity.cmp(&sel_b.specificity));
-
-        matches
-    }
-}
-
-struct DocHandlerData<'a> {
-    base_url: Option<&'a Url>,
-    stylesheet: &'a mut Stylesheet,
-    selector: *mut CRSelector,
-}
-
-macro_rules! get_doc_handler_data {
-    ($doc_handler:expr) => {
-        &mut *((*$doc_handler).app_data as *mut DocHandlerData)
-    };
-}
-
-fn init_cr_doc_handler(handler: &mut CRDocHandler) {
-    handler.import_style = Some(css_import_style);
-    handler.start_selector = Some(css_start_selector);
-    handler.end_selector = Some(css_end_selector);
-    handler.property = Some(css_property);
-    handler.error = Some(css_error);
-    handler.unrecoverable_error = Some(css_unrecoverable_error);
-}
-
-unsafe extern "C" fn css_import_style(
-    a_this: *mut CRDocHandler,
-    _a_media_list: *mut GList,
-    a_uri: CRString,
-    _a_uri_default_ns: CRString,
-    _a_location: CRParsingLocation,
-) {
-    let handler_data = get_doc_handler_data!(a_this);
-
-    if a_uri.is_null() {
-        return;
-    }
-
-    let raw_uri = cr_string_peek_raw_str(a_uri);
-    let uri = utf8_cstr(raw_uri);
+    pub fn apply_matches_to_node(&self, node: &mut RsvgNode) {
+        let mut match_ctx = MatchingContext::new(
+            MatchingMode::Normal,
 
-    if let Ok(aurl) = AllowedUrl::from_href(uri, handler_data.base_url) {
-        // FIXME: handle CSS errors
-        let _ = handler_data.stylesheet.load_css(&aurl);
-    } else {
-        rsvg_log!("disallowed URL \"{}\" for importing CSS", uri);
-    }
-}
+            // FIXME: how the fuck does one set up a bloom filter here?
+            None,
 
-unsafe extern "C" fn css_start_selector(
-    a_this: *mut CRDocHandler,
-    a_selector_list: *mut CRSelector,
-) {
-    let handler_data = get_doc_handler_data!(a_this);
+            // n_index_cache,
+            None,
 
-    cr_selector_ref(a_selector_list);
-    handler_data.selector = a_selector_list;
-}
+            QuirksMode::NoQuirks,
+        );
 
-unsafe extern "C" fn css_end_selector(
-    a_this: *mut CRDocHandler,
-    _a_selector_list: *mut CRSelector,
-) {
-    let handler_data = get_doc_handler_data!(a_this);
-
-    cr_selector_unref(handler_data.selector);
-    handler_data.selector = ptr::null_mut();
-}
-
-unsafe extern "C" fn css_property(
-    a_this: *mut CRDocHandler,
-    a_name: CRString,
-    a_expression: CRTerm,
-    a_is_important: gboolean,
-) {
-    let handler_data = get_doc_handler_data!(a_this);
-
-    if a_name.is_null() || a_expression.is_null() || handler_data.selector.is_null() {
-        return;
-    }
-
-    let mut cur_sel = handler_data.selector;
-    while !cur_sel.is_null() {
-        let simple_sel = (*cur_sel).simple_sel;
-
-        cur_sel = (*cur_sel).next;
-
-        if !simple_sel.is_null() {
-            if cr_simple_sel_compute_specificity(simple_sel) != CR_OK {
-                continue;
-            }
-
-            let specificity = u64::from((*simple_sel).specificity);
-
-            let raw_selector_name = cr_simple_sel_to_string(simple_sel) as *mut libc::c_char;
-
-            if !raw_selector_name.is_null() {
-                let raw_prop_name = cr_string_peek_raw_str(a_name);
-                let prop_name = utf8_cstr(raw_prop_name);
-
-                let prop_value =
-                    <String as FromGlibPtrFull<_>>::from_glib_full(cr_term_to_string(a_expression));
-
-                let selector_name =
-                    <String as FromGlibPtrFull<_>>::from_glib_full(raw_selector_name);
-
-                let important = from_glib(a_is_important);
-
-                let attribute = QualName::new(None, ns!(svg), LocalName::from(prop_name));
-
-                let mut input = ParserInput::new(&prop_value);
-                let mut parser = Parser::new(&mut input);
-
-                match parse_attribute_value_into_parsed_property(&attribute, &mut parser, true) {
-                    Ok(property) => {
-                        let declaration = Declaration {
-                            attribute,
-                            property,
-                            important,
-                        };
-
-                        handler_data
-                            .stylesheet
-                            .add_declaration(Selector::new(&selector_name, specificity), declaration);
-                    }
-                    Err(_) => (), // invalid property name or invalid value; ignore
+        for rule in &self.rules {
+            if selectors::matching::matches_selector_list(
+                &rule.selectors,
+                &RsvgElement(node.clone()),
+                &mut match_ctx,
+            ) {
+                for decl in rule.declarations.iter() {
+                    node.borrow_mut().apply_style_declaration(decl);
                 }
             }
         }
     }
 }
-
-unsafe extern "C" fn css_error(_a_this: *mut CRDocHandler) {
-    println!("CSS parsing error");
-}
-
-unsafe extern "C" fn css_unrecoverable_error(_a_this: *mut CRDocHandler) {
-    println!("CSS unrecoverable error");
-}
diff --git a/rsvg_internals/src/document.rs b/rsvg_internals/src/document.rs
index 3d80f3b5..c779187c 100644
--- a/rsvg_internals/src/document.rs
+++ b/rsvg_internals/src/document.rs
@@ -328,13 +328,11 @@ impl DocumentBuilder {
             Some(mut root) => {
                 if root.borrow().get_type() == NodeType::Svg {
                     for mut node in root.descendants() {
-                        let mut node_borrow = node.borrow_mut();
-
                         for stylesheet in &stylesheets {
-                            node_borrow.set_css_styles(stylesheet);
+                            stylesheet.apply_matches_to_node(&mut node);
                         }
 
-                        node_borrow.set_style_attribute();
+                        node.borrow_mut().set_style_attribute();
                     }
 
                     let values = ComputedValues::default();
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index be6c8502..fd94dd59 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -7,7 +7,7 @@ use std::fmt;
 
 use crate::bbox::BoundingBox;
 use crate::cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
-use crate::css::Stylesheet;
+use crate::css::Declaration;
 use crate::drawing_ctx::DrawingCtx;
 use crate::error::*;
 use crate::filters::FilterEffect;
@@ -209,16 +209,10 @@ impl NodeData {
         }
     }
 
-    /// Applies the CSS rules that match into the node's specified_values
-    pub fn set_css_styles(&mut self, stylesheet: &Stylesheet) {
-        for selector in &stylesheet.get_matches(self) {
-            if let Some(decl_list) = stylesheet.get_declarations(selector) {
-                for declaration in decl_list.iter() {
-                    self.specified_values
-                        .set_property_from_declaration(declaration, &mut self.important_styles);
-                }
-            }
-        }
+    // Applies a style declaration to the node's specified_values
+    pub fn apply_style_declaration(&mut self, declaration: &Declaration) {
+        self.specified_values
+            .set_property_from_declaration(declaration, &mut self.important_styles);
     }
 
     /// Applies CSS styles from the saved value of the "style" attribute


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]