[librsvg] css.rs: New file; start porting the CSS processing code to Rust



commit 08f28816868878962647651118d5a1eb95d06d17
Author: Federico Mena Quintero <federico gnome org>
Date:   Mon Sep 24 18:54:07 2018 -0500

    css.rs: New file; start porting the CSS processing code to Rust
    
    This commit moves the bookkeeping of CSS-derived style declarations
    into Rust.  The parsing code that calls libcroco is still in C.

 librsvg/rsvg-handle.c        |  13 +++--
 librsvg/rsvg-private.h       |  23 ++++++++-
 librsvg/rsvg-styles.c        | 104 ++++------------------------------------
 librsvg/rsvg-styles.h        |   3 --
 rsvg_internals/src/css.rs    | 111 +++++++++++++++++++++++++++++++++++++++++++
 rsvg_internals/src/handle.rs |   7 +++
 rsvg_internals/src/lib.rs    |   5 +-
 rsvg_internals/src/node.rs   | 110 +++++++++++++++++-------------------------
 rsvg_internals/src/state.rs  |  35 +-------------
 9 files changed, 206 insertions(+), 205 deletions(-)
---
diff --git a/librsvg/rsvg-handle.c b/librsvg/rsvg-handle.c
index 25eca410..912197b9 100644
--- a/librsvg/rsvg-handle.c
+++ b/librsvg/rsvg-handle.c
@@ -160,10 +160,7 @@ rsvg_handle_init (RsvgHandle * self)
     self->priv->dpi_x = rsvg_internal_dpi_x;
     self->priv->dpi_y = rsvg_internal_dpi_y;
 
-    self->priv->css_props = g_hash_table_new_full (g_str_hash,
-                                                   g_str_equal,
-                                                   g_free,
-                                                   (GDestroyNotify) g_hash_table_destroy);
+    self->priv->css_styles = rsvg_css_styles_new ();
 
     self->priv->tree = NULL;
 
@@ -191,7 +188,7 @@ rsvg_handle_dispose (GObject *instance)
 
     g_clear_pointer (&self->priv->load, rsvg_load_free);
     g_clear_pointer (&self->priv->defs, rsvg_defs_free);
-    g_clear_pointer (&self->priv->css_props, g_hash_table_destroy);
+    g_clear_pointer (&self->priv->css_styles, rsvg_css_styles_free);
     g_clear_pointer (&self->priv->tree, rsvg_tree_free);
     g_clear_pointer (&self->priv->base_uri, g_free);
     g_clear_object (&self->priv->base_gfile);
@@ -1001,6 +998,12 @@ rsvg_handle_get_defs (RsvgHandle *handle)
     return handle->priv->defs;
 }
 
+RsvgCssStyles *
+rsvg_handle_get_css_styles (RsvgHandle *handle)
+{
+    return handle->priv->css_styles;
+}
+
 RsvgHandle *
 rsvg_handle_load_extern (RsvgHandle *handle, const char *uri)
 {
diff --git a/librsvg/rsvg-private.h b/librsvg/rsvg-private.h
index 4efdebbe..5c2c61e1 100644
--- a/librsvg/rsvg-private.h
+++ b/librsvg/rsvg-private.h
@@ -83,6 +83,8 @@ typedef struct RsvgLoad RsvgLoad;
 
 typedef struct RsvgTree RsvgTree;
 
+typedef struct RsvgCssStyles RsvgCssStyles;
+
 struct RsvgHandlePrivate {
     RsvgHandleFlags flags;
 
@@ -98,7 +100,7 @@ struct RsvgHandlePrivate {
 
     RsvgDefs *defs; /* lookup table for nodes that have an id="foo" attribute */
 
-    GHashTable *css_props;
+    RsvgCssStyles *css_styles;
 
     GCancellable *cancellable;
 
@@ -188,6 +190,22 @@ gboolean rsvg_tree_root_is_svg (RsvgTree *tree);
 G_GNUC_INTERNAL
 void rsvg_tree_cascade (RsvgTree *tree);
 
+/* Implemented in rsvg_internals/src/css.rs */
+G_GNUC_INTERNAL
+RsvgCssStyles *rsvg_css_styles_new (void);
+
+/* Implemented in rsvg_internals/src/css.rs */
+G_GNUC_INTERNAL
+void rsvg_css_styles_free (RsvgCssStyles *styles);
+
+/* Implemented in rsvg_internals/src/css.rs */
+G_GNUC_INTERNAL
+void rsvg_css_styles_define (RsvgCssStyles *styles,
+                             const char *selector,
+                             const char *style_name,
+                             const char *style_value,
+                             gboolean important);
+
 /* Implemented in rsvg_internals/src/structure.rs */
 G_GNUC_INTERNAL
 gboolean rsvg_node_svg_get_size (RsvgNode *node, double dpi_x, double dpi_y, int *out_width, int 
*out_height);
@@ -294,6 +312,9 @@ RsvgNode *rsvg_defs_lookup (const RsvgDefs * defs, const char *name);
 G_GNUC_INTERNAL
 RsvgDefs *rsvg_handle_get_defs (RsvgHandle *handle);
 
+G_GNUC_INTERNAL
+RsvgCssStyles *rsvg_handle_get_css_styles (RsvgHandle *handle);
+
 G_GNUC_INTERNAL
 char *rsvg_handle_resolve_uri (RsvgHandle *handle,
                                const char *uri);
diff --git a/librsvg/rsvg-styles.c b/librsvg/rsvg-styles.c
index 005780ff..e296fb50 100644
--- a/librsvg/rsvg-styles.c
+++ b/librsvg/rsvg-styles.c
@@ -36,64 +36,6 @@
 
 #include <libcroco/libcroco.h>
 
-/* Defined in rsvg_internals/src/state.rs */
-extern gboolean rsvg_state_parse_style_pair(RsvgState *state, const char *attr, const char *value, gboolean 
important) G_GNUC_WARN_UNUSED_RESULT;
-
-typedef struct _StyleValueData {
-    gchar *value;
-    gboolean important;
-} StyleValueData;
-
-static StyleValueData *
-style_value_data_new (const gchar *value, gboolean important)
-{
-    StyleValueData *ret;
-
-    ret = g_new0 (StyleValueData, 1);
-    ret->value = g_strdup (value);
-    ret->important = important;
-
-    return ret;
-}
-
-static void
-style_value_data_free (StyleValueData *value)
-{
-    if (!value)
-        return;
-    g_free (value->value);
-    g_free (value);
-}
-
-static void
-rsvg_css_define_style (RsvgHandle *handle,
-                       const gchar *selector,
-                       const gchar *style_name,
-                       const gchar *style_value,
-                       gboolean important)
-{
-    GHashTable *styles;
-    gboolean need_insert = FALSE;
-
-    /* push name/style pair into HT */
-    styles = g_hash_table_lookup (handle->priv->css_props, selector);
-    if (styles == NULL) {
-        styles = g_hash_table_new_full (g_str_hash, g_str_equal,
-                                        g_free, (GDestroyNotify) style_value_data_free);
-        g_hash_table_insert (handle->priv->css_props, (gpointer) g_strdup (selector), styles);
-        need_insert = TRUE;
-    } else {
-        StyleValueData *current_value;
-        current_value = g_hash_table_lookup (styles, style_name);
-        if (current_value == NULL || !current_value->important)
-            need_insert = TRUE;
-    }
-    if (need_insert) {
-        g_hash_table_insert (styles,
-                             (gpointer) g_strdup (style_name),
-                             (gpointer) style_value_data_new (style_value, important));
-    }
-}
 
 typedef struct _CSSUserData {
     RsvgHandle *handle;
@@ -133,7 +75,7 @@ ccss_end_selector (CRDocHandler * a_handler, CRSelector * a_selector_list)
 }
 
 static void
-ccss_property (CRDocHandler * a_handler, CRString * a_name, CRTerm * a_expr, gboolean a_important)
+ccss_property (CRDocHandler * a_handler, CRString * a_name, CRTerm * a_expr, gboolean important)
 {
     CSSUserData *user_data;
     gchar *name = NULL;
@@ -149,19 +91,19 @@ ccss_property (CRDocHandler * a_handler, CRString * a_name, CRTerm * a_expr, gbo
             if (cur->simple_sel) {
                 gchar *selector = (gchar *) cr_simple_sel_to_string (cur->simple_sel);
                 if (selector) {
-                    gchar *style_name, *style_value;
+                    gchar *prop_name, *prop_value;
                     name = (gchar *) cr_string_peek_raw_str (a_name);
                     len = cr_string_peek_raw_str_len (a_name);
-                    style_name = g_strndup (name, len);
-                    style_value = (gchar *)cr_term_to_string (a_expr);
-                    rsvg_css_define_style (user_data->handle,
+                    prop_name = g_strndup (name, len);
+                    prop_value = (gchar *)cr_term_to_string (a_expr);
+                    rsvg_css_styles_define (user_data->handle->priv->css_styles,
                                            selector,
-                                           style_name,
-                                           style_value,
-                                           a_important);
+                                           prop_name,
+                                           prop_value,
+                                           important);
                     g_free (selector);
-                    g_free (style_name);
-                    g_free (style_value);
+                    g_free (prop_name);
+                    g_free (prop_value);
                 }
             }
         }
@@ -278,32 +220,6 @@ ccss_import_style (CRDocHandler * a_this,
     g_free (mime_type);
 }
 
-static void
-apply_style (const gchar *key, StyleValueData *value, gpointer user_data)
-{
-    RsvgState *state = user_data;
-
-    /* FIXME: this is ignoring errors */
-    gboolean success = rsvg_state_parse_style_pair (state,
-                                                    key,
-                                                    value->value,
-                                                    value->important);
-}
-
-gboolean
-rsvg_lookup_apply_css_style (RsvgHandle *handle, const char *target, RsvgState * state)
-{
-    GHashTable *styles;
-
-    styles = g_hash_table_lookup (handle->priv->css_props, target);
-
-    if (styles != NULL) {
-        g_hash_table_foreach (styles, (GHFunc) apply_style, state);
-        return TRUE;
-    }
-    return FALSE;
-}
-
 /* This is defined like this so that we can export the Rust function... just for
  * the benefit of rsvg-convert.c
  */
diff --git a/librsvg/rsvg-styles.h b/librsvg/rsvg-styles.h
index 7ba81672..7b6a3dbc 100644
--- a/librsvg/rsvg-styles.h
+++ b/librsvg/rsvg-styles.h
@@ -35,9 +35,6 @@ G_BEGIN_DECLS
 G_GNUC_INTERNAL
 void rsvg_parse_cssbuffer (RsvgHandle *handle, const char *buff, size_t buflen);
 
-G_GNUC_INTERNAL
-gboolean rsvg_lookup_apply_css_style (RsvgHandle *handle, const char *target, RsvgState * state);
-
 G_END_DECLS
 
 #endif /* RSVG_STYLES_H */
diff --git a/rsvg_internals/src/css.rs b/rsvg_internals/src/css.rs
new file mode 100644
index 00000000..09e372bd
--- /dev/null
+++ b/rsvg_internals/src/css.rs
@@ -0,0 +1,111 @@
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::str::FromStr;
+
+use libc;
+
+use glib::translate::*;
+use glib_sys;
+
+use attributes::Attribute;
+use state::State;
+use util::utf8_cstr;
+
+pub enum RsvgCssStyles {}
+
+struct Declaration {
+    prop_value: String,
+    important: bool,
+}
+
+// Maps property_name -> Declaration
+type DeclarationList = HashMap<String, Declaration>;
+
+pub struct CssStyles {
+    selectors_to_declarations: HashMap<String, DeclarationList>,
+}
+
+impl CssStyles {
+    fn new() -> CssStyles {
+        CssStyles {
+            selectors_to_declarations: HashMap::new(),
+        }
+    }
+
+    fn define(&mut self, selector: &str, prop_name: &str, prop_value: &str, important: bool) {
+        let decl_list = self
+            .selectors_to_declarations
+            .entry(selector.to_string())
+            .or_insert_with(|| DeclarationList::new());
+
+        match decl_list.entry(prop_name.to_string()) {
+            Entry::Occupied(mut e) => {
+                let decl = e.get_mut();
+
+                if !decl.important {
+                    decl.prop_value = prop_value.to_string();
+                    decl.important = important;
+                }
+            }
+
+            Entry::Vacant(v) => {
+                v.insert(Declaration {
+                    prop_value: prop_value.to_string(),
+                    important,
+                });
+            }
+        }
+    }
+
+    pub fn lookup_apply(&self, selector: &str, state: &mut State) -> bool {
+        if let Some(decl_list) = self.selectors_to_declarations.get(selector) {
+            for (prop_name, declaration) in decl_list.iter() {
+                if let Ok(attr) = Attribute::from_str(prop_name) {
+                    // FIXME: this is ignoring errors
+                    let _ = state.parse_style_pair(
+                        attr,
+                        &declaration.prop_value,
+                        declaration.important,
+                    );
+                }
+            }
+
+            true
+        } else {
+            false
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn rsvg_css_styles_new() -> *mut RsvgCssStyles {
+    Box::into_raw(Box::new(CssStyles::new())) as *mut RsvgCssStyles
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn rsvg_css_styles_free(raw_styles: *mut RsvgCssStyles) {
+    assert!(!raw_styles.is_null());
+
+    Box::from_raw(raw_styles as *mut CssStyles);
+}
+
+#[no_mangle]
+pub extern "C" fn rsvg_css_styles_define(
+    raw_styles: *mut RsvgCssStyles,
+    selector: *const libc::c_char,
+    prop_name: *const libc::c_char,
+    prop_value: *const libc::c_char,
+    important: glib_sys::gboolean,
+) {
+    assert!(!raw_styles.is_null());
+    assert!(!selector.is_null());
+    assert!(!prop_name.is_null());
+    assert!(!prop_value.is_null());
+
+    let styles = unsafe { &mut *(raw_styles as *mut CssStyles) };
+    let selector = unsafe { utf8_cstr(selector) };
+    let prop_name = unsafe { utf8_cstr(prop_name) };
+    let prop_value = unsafe { utf8_cstr(prop_value) };
+
+    styles.define(selector, prop_name, prop_value, from_glib(important));
+}
diff --git a/rsvg_internals/src/handle.rs b/rsvg_internals/src/handle.rs
index aabaa023..c17b6bad 100644
--- a/rsvg_internals/src/handle.rs
+++ b/rsvg_internals/src/handle.rs
@@ -1,6 +1,7 @@
 use glib::translate::*;
 use libc;
 
+use css::{CssStyles, RsvgCssStyles};
 use defs::{Defs, RsvgDefs};
 
 pub enum RsvgHandle {}
@@ -18,6 +19,8 @@ extern "C" {
         handle: *const RsvgHandle,
         uri: *const libc::c_char,
     ) -> *const RsvgHandle;
+
+    fn rsvg_handle_get_css_styles(handle: *const RsvgHandle) -> *mut RsvgCssStyles;
 }
 
 pub fn get_defs<'a>(handle: *const RsvgHandle) -> &'a Defs {
@@ -41,3 +44,7 @@ pub fn resolve_uri(handle: *const RsvgHandle, uri: &str) -> Option<String> {
 pub fn load_extern(handle: *const RsvgHandle, uri: &str) -> *const RsvgHandle {
     unsafe { rsvg_handle_load_extern(handle, uri.to_glib_none().0) }
 }
+
+pub fn get_css_styles<'a>(handle: *const RsvgHandle) -> &'a CssStyles {
+    unsafe { &*(rsvg_handle_get_css_styles(handle) as *const CssStyles) }
+}
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index d4ac2cd7..08668927 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -27,6 +27,8 @@ extern crate downcast_rs;
 
 pub use color::{rsvg_css_parse_color, ColorKind, ColorSpec};
 
+pub use css::{rsvg_css_styles_define, rsvg_css_styles_free, rsvg_css_styles_new};
+
 pub use defs::{rsvg_defs_free, rsvg_defs_lookup, rsvg_defs_new};
 
 pub use drawing_ctx::{
@@ -67,8 +69,6 @@ pub use property_bag::{
     rsvg_property_bag_new,
 };
 
-pub use state::rsvg_state_parse_style_pair;
-
 pub use structure::rsvg_node_svg_get_size;
 
 pub use text::{rsvg_node_chars_append, rsvg_node_chars_new};
@@ -91,6 +91,7 @@ mod bbox;
 mod clip_path;
 mod color;
 mod cond;
+mod css;
 mod defs;
 mod drawing_ctx;
 mod error;
diff --git a/rsvg_internals/src/node.rs b/rsvg_internals/src/node.rs
index f0c2c810..0e394cad 100644
--- a/rsvg_internals/src/node.rs
+++ b/rsvg_internals/src/node.rs
@@ -3,7 +3,6 @@ use downcast_rs::*;
 use glib;
 use glib::translate::*;
 use glib_sys;
-use libc;
 use std::cell::{Cell, Ref, RefCell};
 use std::ptr;
 use std::rc::{Rc, Weak};
@@ -12,18 +11,10 @@ use attributes::Attribute;
 use cond::{RequiredExtensions, RequiredFeatures, SystemLanguage};
 use drawing_ctx::DrawingCtx;
 use error::*;
-use handle::RsvgHandle;
+use handle::{self, RsvgHandle};
 use parsers::Parse;
 use property_bag::PropertyBag;
-use state::{ComputedValues, Overflow, RsvgState, SpecifiedValue, State};
-
-extern "C" {
-    fn rsvg_lookup_apply_css_style(
-        handle: *const RsvgHandle,
-        target: *const libc::c_char,
-        state: *mut RsvgState,
-    ) -> glib_sys::gboolean;
-}
+use state::{ComputedValues, Overflow, SpecifiedValue, State};
 
 // A *const RsvgNode is just a pointer for the C code's benefit: it
 // points to an  Rc<Node>, which is our refcounted Rust representation
@@ -451,66 +442,53 @@ impl Node {
         //
         // This is basically a semi-compliant CSS2 selection engine
 
-        unsafe {
-            let state_ptr = self.state.as_ptr() as *mut RsvgState;
-
-            // *
-            rsvg_lookup_apply_css_style(handle, "*".to_glib_none().0, state_ptr);
-
-            // tag
-            rsvg_lookup_apply_css_style(handle, self.element_name.to_glib_none().0, state_ptr);
-
-            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!("{}.{}#{}", self.element_name, cls, id);
-                            found = found || from_glib(rsvg_lookup_apply_css_style(
-                                handle,
-                                target.to_glib_none().0,
-                                state_ptr,
-                            ));
-                        }
-
-                        // .class#id
-                        if let Some(id) = self.get_id() {
-                            let target = format!(".{}#{}", cls, id);
-                            found = found || from_glib(rsvg_lookup_apply_css_style(
-                                handle,
-                                target.to_glib_none().0,
-                                state_ptr,
-                            ));
-                        }
-
-                        // tag.class
-                        let target = format!("{}.{}", self.element_name, cls);
-                        found = found || from_glib(rsvg_lookup_apply_css_style(
-                            handle,
-                            target.to_glib_none().0,
-                            state_ptr,
-                        ));
-
-                        if !found {
-                            // didn't find anything more specific, just apply the class style
-                            let target = format!(".{}", cls);
-                            rsvg_lookup_apply_css_style(handle, target.to_glib_none().0, state_ptr);
-                        }
+        let css_styles = handle::get_css_styles(handle);
+        let mut state = self.state.borrow_mut();
+
+        // *
+        css_styles.lookup_apply("*", &mut state);
+
+        // tag
+        css_styles.lookup_apply(&self.element_name, &mut state);
+
+        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!("{}.{}#{}", self.element_name, cls, id);
+                        found = found || css_styles.lookup_apply(&target, &mut state);
+                    }
+
+                    // .class#id
+                    if let Some(id) = self.get_id() {
+                        let target = format!(".{}#{}", cls, id);
+                        found = found || css_styles.lookup_apply(&target, &mut state);
+                    }
+
+                    // tag.class
+                    let target = format!("{}.{}", self.element_name, cls);
+                    found = found || css_styles.lookup_apply(&target, &mut state);
+
+                    if !found {
+                        // didn't find anything more specific, just apply the class style
+                        let target = format!(".{}", cls);
+                        css_styles.lookup_apply(&target, &mut state);
                     }
                 }
             }
+        }
 
-            if let Some(id) = self.get_id() {
-                // id
-                let target = format!("#{}", id);
-                rsvg_lookup_apply_css_style(handle, target.to_glib_none().0, state_ptr);
+        if let Some(id) = self.get_id() {
+            // id
+            let target = format!("#{}", id);
+            css_styles.lookup_apply(&target, &mut state);
 
-                // tag#id
-                let target = format!("{}#{}", self.element_name, id);
-                rsvg_lookup_apply_css_style(handle, target.to_glib_none().0, state_ptr);
-            }
+            // tag#id
+            let target = format!("{}#{}", self.element_name, id);
+            css_styles.lookup_apply(&target, &mut state);
         }
     }
 
diff --git a/rsvg_internals/src/state.rs b/rsvg_internals/src/state.rs
index ef1cf3da..9faa5bc1 100644
--- a/rsvg_internals/src/state.rs
+++ b/rsvg_internals/src/state.rs
@@ -1,7 +1,4 @@
 use cssparser::{self, Parser, Token};
-use glib::translate::*;
-use glib_sys;
-use libc;
 use std::cell::RefCell;
 use std::collections::HashSet;
 use std::str::FromStr;
@@ -16,7 +13,6 @@ use parsers::{Parse, ParseError};
 use property_bag::PropertyBag;
 use property_macros::Property;
 use unitinterval::UnitInterval;
-use util::utf8_cstr;
 
 /// Representation of a single CSS property value.
 ///
@@ -70,9 +66,6 @@ where
     }
 }
 
-// This is only used as *const RsvgState or *mut RsvgState, as an opaque pointer for C
-pub enum RsvgState {}
-
 /// Holds the state of CSS properties
 ///
 /// This is used for various purposes:
@@ -550,7 +543,7 @@ impl State {
         Ok(())
     }
 
-    fn parse_style_pair(
+    pub fn parse_style_pair(
         &mut self,
         attr: Attribute,
         value: &str,
@@ -1386,29 +1379,3 @@ make_property!(
     "default" => Default,
     "preserve" => Preserve,
 );
-
-#[no_mangle]
-pub extern "C" fn rsvg_state_parse_style_pair(
-    state: *mut RsvgState,
-    name: *const libc::c_char,
-    value: *const libc::c_char,
-    important: glib_sys::gboolean,
-) -> glib_sys::gboolean {
-    assert!(!state.is_null());
-    let state = unsafe { &mut *(state as *mut State) };
-
-    assert!(!name.is_null());
-    let name = unsafe { utf8_cstr(name) };
-
-    assert!(!value.is_null());
-    let value = unsafe { utf8_cstr(value) };
-
-    if let Ok(attr) = Attribute::from_str(name) {
-        match state.parse_style_pair(attr, value, from_glib(important)) {
-            Ok(_) => true.to_glib(),
-            Err(_) => false.to_glib(),
-        }
-    } else {
-        false.to_glib()
-    }
-}


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