summaryrefslogtreecommitdiff
path: root/gnulib-local/lib/libcroco/cr-sel-eng.c
diff options
context:
space:
mode:
Diffstat (limited to 'gnulib-local/lib/libcroco/cr-sel-eng.c')
-rw-r--r--gnulib-local/lib/libcroco/cr-sel-eng.c1572
1 files changed, 1572 insertions, 0 deletions
diff --git a/gnulib-local/lib/libcroco/cr-sel-eng.c b/gnulib-local/lib/libcroco/cr-sel-eng.c
new file mode 100644
index 0000000..2b07edf
--- /dev/null
+++ b/gnulib-local/lib/libcroco/cr-sel-eng.c
@@ -0,0 +1,1572 @@
+/* -*- Mode: C; indent-tabs-mode:nil; c-basic-offset: 8-*- */
+
+/*
+ * This file is part of The Croco Library
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of version 2.1 of the GNU Lesser General Public
+ * License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser
+ * General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ * USA
+ *
+ * See COPYRIGHTS file for copyright informations.
+ */
+
+#include <config.h>
+#include <string.h>
+#include "cr-sel-eng.h"
+
+/**
+ *@CRSelEng:
+ *
+ *The definition of the #CRSelEng class.
+ *The #CRSelEng is actually the "Selection Engine"
+ *class. This is highly experimental for at the moment and
+ *its api is very likely to change in a near future.
+ */
+
+#define PRIVATE(a_this) (a_this)->priv
+
+struct CRPseudoClassSelHandlerEntry {
+ guchar *name;
+ enum CRPseudoType type;
+ CRPseudoClassSelectorHandler handler;
+};
+
+struct _CRSelEngPriv {
+ /*not used yet */
+ gboolean case_sensitive;
+
+ CRStyleSheet *sheet;
+ /**
+ *where to store the next statement
+ *to be visited so that we can remember
+ *it from one method call to another.
+ */
+ CRStatement *cur_stmt;
+ GList *pcs_handlers;
+ gint pcs_handlers_size;
+} ;
+
+static gboolean class_add_sel_matches_node (CRAdditionalSel * a_add_sel,
+ xmlNode * a_node);
+
+static gboolean id_add_sel_matches_node (CRAdditionalSel * a_add_sel,
+ xmlNode * a_node);
+
+static gboolean attr_add_sel_matches_node (CRAdditionalSel * a_add_sel,
+ xmlNode * a_node);
+
+static enum CRStatus sel_matches_node_real (CRSelEng * a_this,
+ CRSimpleSel * a_sel,
+ xmlNode * a_node,
+ gboolean * a_result,
+ gboolean a_eval_sel_list_from_end,
+ gboolean a_recurse);
+
+static enum CRStatus cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
+ CRStyleSheet *
+ a_stylesheet,
+ xmlNode * a_node,
+ CRStatement **
+ a_rulesets,
+ gulong * a_len);
+
+static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props,
+ CRStatement *
+ a_ruleset);
+
+static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this,
+ CRAdditionalSel *
+ a_add_sel,
+ xmlNode * a_node);
+
+static gboolean lang_pseudo_class_handler (CRSelEng * a_this,
+ CRAdditionalSel * a_sel,
+ xmlNode * a_node);
+
+static gboolean first_child_pseudo_class_handler (CRSelEng * a_this,
+ CRAdditionalSel * a_sel,
+ xmlNode * a_node);
+
+static xmlNode *get_next_element_node (xmlNode * a_node);
+
+static xmlNode *get_next_child_element_node (xmlNode * a_node);
+
+static xmlNode *get_prev_element_node (xmlNode * a_node);
+
+static xmlNode *get_next_parent_element_node (xmlNode * a_node);
+
+static gboolean
+lang_pseudo_class_handler (CRSelEng * a_this,
+ CRAdditionalSel * a_sel, xmlNode * a_node)
+{
+ xmlNode *node = a_node;
+ xmlChar *val = NULL;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_sel && a_sel->content.pseudo
+ && a_sel->content.pseudo
+ && a_sel->content.pseudo->name
+ && a_sel->content.pseudo->name->stryng
+ && a_node, CR_BAD_PARAM_ERROR);
+
+ if (strncmp (a_sel->content.pseudo->name->stryng->str,
+ "lang", 4)
+ || !a_sel->content.pseudo->type == FUNCTION_PSEUDO) {
+ cr_utils_trace_info ("This handler is for :lang only");
+ return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
+ }
+ /*lang code should exist and be at least of length 2 */
+ if (!a_sel->content.pseudo->extra
+ || !a_sel->content.pseudo->extra->stryng
+ || a_sel->content.pseudo->extra->stryng->len < 2)
+ return FALSE;
+ for (; node; node = get_next_parent_element_node (node)) {
+ val = xmlGetProp (node, "lang");
+ if (val
+ && !strncmp (val,
+ a_sel->content.pseudo->extra->stryng->str,
+ a_sel->content.pseudo->extra->stryng->len)) {
+ result = TRUE;
+ }
+ if (val) {
+ xmlFree (val);
+ val = NULL;
+ }
+ }
+
+ return result;
+}
+
+static gboolean
+first_child_pseudo_class_handler (CRSelEng * a_this,
+ CRAdditionalSel * a_sel, xmlNode * a_node)
+{
+ xmlNode *node = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_sel && a_sel->content.pseudo
+ && a_sel->content.pseudo
+ && a_sel->content.pseudo->name
+ && a_sel->content.pseudo->name->stryng
+ && a_node, CR_BAD_PARAM_ERROR);
+
+ if (strcmp (a_sel->content.pseudo->name->stryng->str,
+ "first-child")
+ || !a_sel->content.pseudo->type == IDENT_PSEUDO) {
+ cr_utils_trace_info ("This handler is for :first-child only");
+ return CR_BAD_PSEUDO_CLASS_SEL_HANDLER_ERROR;
+ }
+ if (!a_node->parent)
+ return FALSE;
+ node = get_next_child_element_node (a_node->parent);
+ if (node == a_node)
+ return TRUE;
+ return FALSE;
+}
+
+static gboolean
+pseudo_class_add_sel_matches_node (CRSelEng * a_this,
+ CRAdditionalSel * a_add_sel,
+ xmlNode * a_node)
+{
+ enum CRStatus status = CR_OK;
+ CRPseudoClassSelectorHandler handler = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_add_sel
+ && a_add_sel->content.pseudo
+ && a_add_sel->content.pseudo->name
+ && a_add_sel->content.pseudo->name->stryng
+ && a_add_sel->content.pseudo->name->stryng->str
+ && a_node, CR_BAD_PARAM_ERROR);
+
+ status = cr_sel_eng_get_pseudo_class_selector_handler
+ (a_this, a_add_sel->content.pseudo->name->stryng->str,
+ a_add_sel->content.pseudo->type, &handler);
+ if (status != CR_OK || !handler)
+ return FALSE;
+
+ return handler (a_this, a_add_sel, a_node);
+}
+
+/**
+ *@param a_add_sel the class additional selector to consider.
+ *@param a_node the xml node to consider.
+ *@return TRUE if the class additional selector matches
+ *the xml node given in argument, FALSE otherwise.
+ */
+static gboolean
+class_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
+{
+ gboolean result = FALSE;
+ xmlChar *klass = NULL,
+ *cur = NULL;
+
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == CLASS_ADD_SELECTOR
+ && a_add_sel->content.class_name
+ && a_add_sel->content.class_name->stryng
+ && a_add_sel->content.class_name->stryng->str
+ && a_node, FALSE);
+
+ if (xmlHasProp (a_node, "class")) {
+ klass = xmlGetProp (a_node, "class");
+ for (cur = klass; cur && *cur; cur++) {
+ while (cur && *cur
+ && cr_utils_is_white_space (*cur)
+ == TRUE)
+ cur++;
+
+ if (!strncmp (cur,
+ a_add_sel->content.class_name->stryng->str,
+ a_add_sel->content.class_name->stryng->len)) {
+ cur += a_add_sel->content.class_name->stryng->len;
+ if ((cur && !*cur)
+ || cr_utils_is_white_space (*cur) == TRUE)
+ result = TRUE;
+ }
+ if (cur && !*cur)
+ break ;
+ }
+ }
+ if (klass) {
+ xmlFree (klass);
+ klass = NULL;
+ }
+ return result;
+
+}
+
+/**
+ *@return TRUE if the additional attribute selector matches
+ *the current xml node given in argument, FALSE otherwise.
+ *@param a_add_sel the additional attribute selector to consider.
+ *@param a_node the xml node to consider.
+ */
+static gboolean
+id_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
+{
+ gboolean result = FALSE;
+ xmlChar *id = NULL;
+
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == ID_ADD_SELECTOR
+ && a_add_sel->content.id_name
+ && a_add_sel->content.id_name->stryng
+ && a_add_sel->content.id_name->stryng->str
+ && a_node, FALSE);
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == ID_ADD_SELECTOR
+ && a_node, FALSE);
+
+ if (xmlHasProp (a_node, "id")) {
+ id = xmlGetProp (a_node, "id");
+ if (!strncmp (id, a_add_sel->content.id_name->stryng->str,
+ a_add_sel->content.id_name->stryng->len)) {
+ result = TRUE;
+ }
+ }
+ if (id) {
+ xmlFree (id);
+ id = NULL;
+ }
+ return result;
+}
+
+/**
+ *Returns TRUE if the instance of #CRAdditional selector matches
+ *the node given in parameter, FALSE otherwise.
+ *@param a_add_sel the additional selector to evaluate.
+ *@param a_node the xml node against whitch the selector is to
+ *be evaluated
+ *return TRUE if the additional selector matches the current xml node
+ *FALSE otherwise.
+ */
+static gboolean
+attr_add_sel_matches_node (CRAdditionalSel * a_add_sel, xmlNode * a_node)
+{
+ CRAttrSel *cur_sel = NULL;
+
+ g_return_val_if_fail (a_add_sel
+ && a_add_sel->type == ATTRIBUTE_ADD_SELECTOR
+ && a_node, FALSE);
+
+ for (cur_sel = a_add_sel->content.attr_sel;
+ cur_sel; cur_sel = cur_sel->next) {
+ switch (cur_sel->match_way) {
+ case SET:
+ if (!cur_sel->name
+ || !cur_sel->name->stryng
+ || !cur_sel->name->stryng->str)
+ return FALSE;
+
+ if (!xmlHasProp (a_node,
+ cur_sel->name->stryng->str))
+ return FALSE;
+ break;
+
+ case EQUALS:
+ {
+ xmlChar *value = NULL;
+
+ if (!cur_sel->name
+ || !cur_sel->name->stryng
+ || !cur_sel->name->stryng->str
+ || !cur_sel->value
+ || !cur_sel->value->stryng
+ || !cur_sel->value->stryng->str)
+ return FALSE;
+
+ if (!xmlHasProp
+ (a_node,
+ cur_sel->name->stryng->str))
+ return FALSE;
+
+ value = xmlGetProp
+ (a_node,
+ cur_sel->name->stryng->str);
+
+ if (value
+ && strcmp
+ (value,
+ cur_sel->value->stryng->str)) {
+ xmlFree (value);
+ return FALSE;
+ }
+ xmlFree (value);
+ }
+ break;
+
+ case INCLUDES:
+ {
+ xmlChar *value = NULL,
+ *ptr1 = NULL,
+ *ptr2 = NULL,
+ *cur = NULL;
+ gboolean found = FALSE;
+
+ if (!xmlHasProp
+ (a_node,
+ cur_sel->name->stryng->str))
+ return FALSE;
+ value = xmlGetProp
+ (a_node,
+ cur_sel->name->stryng->str);
+
+ if (!value)
+ return FALSE;
+
+ /*
+ *here, make sure value is a space
+ *separated list of "words", where one
+ *value is exactly cur_sel->value->str
+ */
+ for (cur = value; *cur; cur++) {
+ /*
+ *set ptr1 to the first non white space
+ *char addr.
+ */
+ while (cr_utils_is_white_space
+ (*cur) == TRUE && *cur)
+ cur++;
+ if (!*cur)
+ break;
+ ptr1 = cur;
+
+ /*
+ *set ptr2 to the end the word.
+ */
+ while (cr_utils_is_white_space
+ (*cur) == FALSE && *cur)
+ cur++;
+ cur--;
+ ptr2 = cur;
+
+ if (!strncmp
+ (ptr1,
+ cur_sel->value->stryng->str,
+ ptr2 - ptr1 + 1)) {
+ found = TRUE;
+ break;
+ }
+ ptr1 = ptr2 = NULL;
+ }
+
+ if (found == FALSE) {
+ xmlFree (value);
+ return FALSE;
+ }
+ xmlFree (value);
+ }
+ break;
+
+ case DASHMATCH:
+ {
+ xmlChar *value = NULL,
+ *ptr1 = NULL,
+ *ptr2 = NULL,
+ *cur = NULL;
+ gboolean found = FALSE;
+
+ if (!xmlHasProp
+ (a_node,
+ cur_sel->name->stryng->str))
+ return FALSE;
+ value = xmlGetProp
+ (a_node,
+ cur_sel->name->stryng->str);
+
+ /*
+ *here, make sure value is an hyphen
+ *separated list of "words", each of which
+ *starting with "cur_sel->value->str"
+ */
+ for (cur = value; *cur; cur++) {
+ if (*cur == '-')
+ cur++;
+ ptr1 = cur;
+
+ while (*cur != '-' && *cur)
+ cur++;
+ cur--;
+ ptr2 = cur;
+
+ if (g_strstr_len
+ (ptr1, ptr2 - ptr1 + 1,
+ cur_sel->value->stryng->str)
+ == (gchar *) ptr1) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found == FALSE) {
+ xmlFree (value);
+ return FALSE;
+ }
+ xmlFree (value);
+ }
+ break;
+ default:
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/**
+ *Evaluates if a given additional selector matches an xml node.
+ *@param a_add_sel the additional selector to consider.
+ *@param a_node the xml node to consider.
+ *@return TRUE is a_add_sel matches a_node, FALSE otherwise.
+ */
+static gboolean
+additional_selector_matches_node (CRSelEng * a_this,
+ CRAdditionalSel * a_add_sel,
+ xmlNode * a_node)
+{
+ CRAdditionalSel *cur_add_sel = NULL, *tail = NULL ;
+ gboolean evaluated = FALSE ;
+
+ for (tail = a_add_sel ;
+ tail && tail->next;
+ tail = tail->next) ;
+
+ g_return_val_if_fail (tail, FALSE) ;
+
+ for (cur_add_sel = tail ;
+ cur_add_sel ;
+ cur_add_sel = cur_add_sel->prev) {
+
+ evaluated = TRUE ;
+ if (cur_add_sel->type == NO_ADD_SELECTOR) {
+ return FALSE;
+ }
+
+ if (cur_add_sel->type == CLASS_ADD_SELECTOR
+ && cur_add_sel->content.class_name
+ && cur_add_sel->content.class_name->stryng
+ && cur_add_sel->content.class_name->stryng->str) {
+ if (class_add_sel_matches_node (cur_add_sel,
+ a_node) == FALSE) {
+ return FALSE;
+ }
+ continue ;
+ } else if (cur_add_sel->type == ID_ADD_SELECTOR
+ && cur_add_sel->content.id_name
+ && cur_add_sel->content.id_name->stryng
+ && cur_add_sel->content.id_name->stryng->str) {
+ if (id_add_sel_matches_node (cur_add_sel, a_node) == FALSE) {
+ return FALSE;
+ }
+ continue ;
+ } else if (cur_add_sel->type == ATTRIBUTE_ADD_SELECTOR
+ && cur_add_sel->content.attr_sel) {
+ /*
+ *here, call a function that does the match
+ *against an attribute additionnal selector
+ *and an xml node.
+ */
+ if (attr_add_sel_matches_node (cur_add_sel, a_node)
+ == FALSE) {
+ return FALSE;
+ }
+ continue ;
+ } else if (cur_add_sel->type == PSEUDO_CLASS_ADD_SELECTOR
+ && cur_add_sel->content.pseudo) {
+ if (pseudo_class_add_sel_matches_node
+ (a_this, cur_add_sel, a_node) == TRUE) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+ }
+ if (evaluated == TRUE)
+ return TRUE;
+ return FALSE ;
+}
+
+static xmlNode *
+get_next_element_node (xmlNode * a_node)
+{
+ xmlNode *cur_node = NULL;
+
+ g_return_val_if_fail (a_node, NULL);
+
+ cur_node = a_node->next;
+ while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
+ cur_node = cur_node->next;
+ }
+ return cur_node;
+}
+
+static xmlNode *
+get_next_child_element_node (xmlNode * a_node)
+{
+ xmlNode *cur_node = NULL;
+
+ g_return_val_if_fail (a_node, NULL);
+
+ cur_node = a_node->children;
+ if (!cur_node)
+ return cur_node;
+ if (a_node->children->type == XML_ELEMENT_NODE)
+ return a_node->children;
+ return get_next_element_node (a_node->children);
+}
+
+static xmlNode *
+get_prev_element_node (xmlNode * a_node)
+{
+ xmlNode *cur_node = NULL;
+
+ g_return_val_if_fail (a_node, NULL);
+
+ cur_node = a_node->prev;
+ while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
+ cur_node = cur_node->prev;
+ }
+ return cur_node;
+}
+
+static xmlNode *
+get_next_parent_element_node (xmlNode * a_node)
+{
+ xmlNode *cur_node = NULL;
+
+ g_return_val_if_fail (a_node, NULL);
+
+ cur_node = a_node->parent;
+ while (cur_node && cur_node->type != XML_ELEMENT_NODE) {
+ cur_node = cur_node->parent;
+ }
+ return cur_node;
+}
+
+/**
+ *Evaluate a selector (a simple selectors list) and says
+ *if it matches the xml node given in parameter.
+ *The algorithm used here is the following:
+ *Walk the combinator separated list of simple selectors backward, starting
+ *from the end of the list. For each simple selector, looks if
+ *if matches the current node.
+ *
+ *@param a_this the selection engine.
+ *@param a_sel the simple selection list.
+ *@param a_node the xml node.
+ *@param a_result out parameter. Set to true if the
+ *selector matches the xml node, FALSE otherwise.
+ *@param a_recurse if set to TRUE, the function will walk to
+ *the next simple selector (after the evaluation of the current one)
+ *and recursively evaluate it. Must be usually set to TRUE unless you
+ *know what you are doing.
+ */
+static enum CRStatus
+sel_matches_node_real (CRSelEng * a_this, CRSimpleSel * a_sel,
+ xmlNode * a_node, gboolean * a_result,
+ gboolean a_eval_sel_list_from_end,
+ gboolean a_recurse)
+{
+ CRSimpleSel *cur_sel = NULL;
+ xmlNode *cur_node = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_this && a_node
+ && a_result, CR_BAD_PARAM_ERROR);
+
+ *a_result = FALSE;
+
+ if (a_node->type != XML_ELEMENT_NODE)
+ return CR_OK;
+
+ if (a_eval_sel_list_from_end == TRUE) {
+ /*go and get the last simple selector of the list */
+ for (cur_sel = a_sel;
+ cur_sel && cur_sel->next; cur_sel = cur_sel->next) ;
+ } else {
+ cur_sel = a_sel;
+ }
+
+ for (cur_node = a_node; cur_sel; cur_sel = cur_sel->prev) {
+ if (((cur_sel->type_mask & TYPE_SELECTOR)
+ && (cur_sel->name
+ && cur_sel->name->stryng
+ && cur_sel->name->stryng->str)
+ && (!strcmp (cur_sel->name->stryng->str,
+ cur_node->name)))
+ || (cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
+ /*
+ *this simple selector
+ *matches the current xml node
+ *Let's see if the preceding
+ *simple selectors also match
+ *their xml node counterpart.
+ */
+ if (cur_sel->add_sel) {
+ if (additional_selector_matches_node (a_this, cur_sel->add_sel,
+ cur_node) == TRUE) {
+ goto walk_a_step_in_expr;
+ } else {
+ goto done;
+ }
+ } else {
+ goto walk_a_step_in_expr;
+ }
+ }
+ if (!(cur_sel->type_mask & TYPE_SELECTOR)
+ && !(cur_sel->type_mask & UNIVERSAL_SELECTOR)) {
+ if (!cur_sel->add_sel) {
+ goto done;
+ }
+ if (additional_selector_matches_node
+ (a_this, cur_sel->add_sel, cur_node)
+ == TRUE) {
+ goto walk_a_step_in_expr;
+ } else {
+ goto done;
+ }
+ } else {
+ goto done ;
+ }
+
+ walk_a_step_in_expr:
+ if (a_recurse == FALSE) {
+ *a_result = TRUE;
+ goto done;
+ }
+
+ /*
+ *here, depending on the combinator of cur_sel
+ *choose the axis of the xml tree traversal
+ *and walk one step in the xml tree.
+ */
+ if (!cur_sel->prev)
+ break;
+
+ switch (cur_sel->combinator) {
+ case NO_COMBINATOR:
+ break;
+
+ case COMB_WS: /*descendant selector */
+ {
+ xmlNode *n = NULL;
+ enum CRStatus status = CR_OK;
+ gboolean matches = FALSE;
+
+ /*
+ *walk the xml tree upward looking for a parent
+ *node that matches the preceding selector.
+ */
+ for (n = cur_node->parent; n; n = n->parent) {
+ status = sel_matches_node_real
+ (a_this, cur_sel->prev,
+ n, &matches, FALSE, TRUE);
+
+ if (status != CR_OK)
+ goto done;
+
+ if (matches == TRUE) {
+ cur_node = n ;
+ break;
+ }
+ }
+
+ if (!n) {
+ /*
+ *didn't find any ancestor that matches
+ *the previous simple selector.
+ */
+ goto done;
+ }
+ /*
+ *in this case, the preceding simple sel
+ *will have been interpreted twice, which
+ *is a cpu and mem waste ... I need to find
+ *another way to do this. Anyway, this is
+ *my first attempt to write this function and
+ *I am a bit clueless.
+ */
+ break;
+ }
+
+ case COMB_PLUS:
+ cur_node = get_prev_element_node (cur_node);
+ if (!cur_node)
+ goto done;
+ break;
+
+ case COMB_GT:
+ cur_node = get_next_parent_element_node (cur_node);
+ if (!cur_node)
+ goto done;
+ break;
+
+ default:
+ goto done;
+ }
+ continue;
+ }
+
+ /*
+ *if we reached this point, it means the selector matches
+ *the xml node.
+ */
+ *a_result = TRUE;
+
+ done:
+ return CR_OK;
+}
+
+
+/**
+ *Returns array of the ruleset statements that matches the
+ *given xml node.
+ *The engine keeps in memory the last statement he
+ *visited during the match. So, the next call
+ *to this function will eventually return a rulesets list starting
+ *from the last ruleset statement visited during the previous call.
+ *The enable users to get matching rulesets in an incremental way.
+ *Note that for each statement returned,
+ *the engine calculates the specificity of the selector
+ *that matched the xml node and stores it in the "specifity" field
+ *of the statement structure.
+ *
+ *@param a_sel_eng the current selection engine
+ *@param a_node the xml node for which the request
+ *is being made.
+ *@param a_sel_list the list of selectors to perform the search in.
+ *@param a_rulesets in/out parameter. A pointer to the
+ *returned array of rulesets statements that match the xml node
+ *given in parameter. The caller allocates the array before calling this
+ *function.
+ *@param a_len in/out parameter the length (in sizeof (#CRStatement*))
+ *of the returned array.
+ *(the length of a_rulesets, more precisely).
+ *The caller must set it to the length of a_ruleset prior to calling this
+ *function. In return, the function sets it to the length
+ *(in sizeof (#CRStatement)) of the actually returned CRStatement array.
+ *@return CR_OUTPUT_TOO_SHORT_ERROR if found more rulesets than the size
+ *of the a_rulesets array. In this case, the first *a_len rulesets found
+ *are put in a_rulesets, and a further call will return the following
+ *ruleset(s) following the same principle.
+ *@return CR_OK if all the rulesets found have been returned. In this
+ *case, *a_len is set to the actual number of ruleset found.
+ *@return CR_BAD_PARAM_ERROR in case any of the given parameter are
+ *bad (e.g null pointer).
+ *@return CR_ERROR if any other error occured.
+ */
+static enum CRStatus
+cr_sel_eng_get_matched_rulesets_real (CRSelEng * a_this,
+ CRStyleSheet * a_stylesheet,
+ xmlNode * a_node,
+ CRStatement ** a_rulesets,
+ gulong * a_len)
+{
+ CRStatement *cur_stmt = NULL;
+ CRSelector *sel_list = NULL,
+ *cur_sel = NULL;
+ gboolean matches = FALSE;
+ enum CRStatus status = CR_OK;
+ gulong i = 0;
+
+ g_return_val_if_fail (a_this
+ && a_stylesheet
+ && a_node && a_rulesets, CR_BAD_PARAM_ERROR);
+
+ if (!a_stylesheet->statements) {
+ *a_rulesets = NULL;
+ *a_len = 0;
+ return CR_OK;
+ }
+
+ /*
+ *if this stylesheet is "new one"
+ *let's remember it for subsequent calls.
+ */
+ if (PRIVATE (a_this)->sheet != a_stylesheet) {
+ PRIVATE (a_this)->sheet = a_stylesheet;
+ PRIVATE (a_this)->cur_stmt = a_stylesheet->statements;
+ }
+
+ /*
+ *walk through the list of statements and,
+ *get the selectors list inside the statements that
+ *contain some, and try to match our xml node in these
+ *selectors lists.
+ */
+ for (cur_stmt = PRIVATE (a_this)->cur_stmt, i = 0;
+ (PRIVATE (a_this)->cur_stmt = cur_stmt);
+ cur_stmt = cur_stmt->next) {
+ /*
+ *initialyze the selector list in which we will
+ *really perform the search.
+ */
+ sel_list = NULL;
+
+ /*
+ *get the the damn selector list in
+ *which we have to look
+ */
+ switch (cur_stmt->type) {
+ case RULESET_STMT:
+ if (cur_stmt->kind.ruleset
+ && cur_stmt->kind.ruleset->sel_list) {
+ sel_list = cur_stmt->kind.ruleset->sel_list;
+ }
+ break;
+
+ case AT_MEDIA_RULE_STMT:
+ if (cur_stmt->kind.media_rule
+ && cur_stmt->kind.media_rule->rulesets
+ && cur_stmt->kind.media_rule->rulesets->
+ kind.ruleset
+ && cur_stmt->kind.media_rule->rulesets->
+ kind.ruleset->sel_list) {
+ sel_list =
+ cur_stmt->kind.media_rule->
+ rulesets->kind.ruleset->sel_list;
+ }
+ break;
+
+ case AT_IMPORT_RULE_STMT:
+ /*
+ *some recursivity may be needed here.
+ *I don't like this :(
+ */
+ break;
+ default:
+ break;
+ }
+
+ if (!sel_list)
+ continue;
+
+ /*
+ *now, we have a comma separated selector list to look in.
+ *let's walk it and try to match the xml_node
+ *on each item of the list.
+ */
+ for (cur_sel = sel_list; cur_sel; cur_sel = cur_sel->next) {
+ if (!cur_sel->simple_sel)
+ continue;
+
+ status = cr_sel_eng_matches_node
+ (a_this, cur_sel->simple_sel,
+ a_node, &matches);
+
+ if (status == CR_OK && matches == TRUE) {
+ /*
+ *bingo!!! we found one ruleset that
+ *matches that fucking node.
+ *lets put it in the out array.
+ */
+
+ if (i < *a_len) {
+ a_rulesets[i] = cur_stmt;
+ i++;
+
+ /*
+ *For the cascade computing algorithm
+ *(which is gonna take place later)
+ *we must compute the specificity
+ *(css2 spec chap 6.4.1) of the selector
+ *that matched the current xml node
+ *and store it in the css2 statement
+ *(statement == ruleset here).
+ */
+ status = cr_simple_sel_compute_specificity (cur_sel->simple_sel);
+
+ g_return_val_if_fail (status == CR_OK,
+ CR_ERROR);
+ cur_stmt->specificity =
+ cur_sel->simple_sel->
+ specificity;
+ } else
+ {
+ *a_len = i;
+ return CR_OUTPUT_TOO_SHORT_ERROR;
+ }
+ }
+ }
+ }
+
+ /*
+ *if we reached this point, it means
+ *we reached the end of stylesheet.
+ *no need to store any info about the stylesheet
+ *anymore.
+ */
+ g_return_val_if_fail (!PRIVATE (a_this)->cur_stmt, CR_ERROR);
+ PRIVATE (a_this)->sheet = NULL;
+ *a_len = i;
+ return CR_OK;
+}
+
+static enum CRStatus
+put_css_properties_in_props_list (CRPropList ** a_props, CRStatement * a_stmt)
+{
+ CRPropList *props = NULL,
+ *pair = NULL,
+ *tmp_props = NULL;
+ CRDeclaration *cur_decl = NULL;
+
+ g_return_val_if_fail (a_props && a_stmt
+ && a_stmt->type == RULESET_STMT
+ && a_stmt->kind.ruleset, CR_BAD_PARAM_ERROR);
+
+ props = *a_props;
+
+ for (cur_decl = a_stmt->kind.ruleset->decl_list;
+ cur_decl; cur_decl = cur_decl->next) {
+ CRDeclaration *decl;
+
+ decl = NULL;
+ pair = NULL;
+
+ if (!cur_decl->property
+ || !cur_decl->property->stryng
+ || !cur_decl->property->stryng->str)
+ continue;
+ /*
+ *First, test if the property is not
+ *already present in our properties list
+ *If yes, apply the cascading rules to
+ *compute the precedence. If not, insert
+ *the property into the list
+ */
+ cr_prop_list_lookup_prop (props,
+ cur_decl->property,
+ &pair);
+
+ if (!pair) {
+ tmp_props = cr_prop_list_append2
+ (props, cur_decl->property, cur_decl);
+ if (tmp_props) {
+ props = tmp_props;
+ tmp_props = NULL;
+ }
+ continue;
+ }
+
+ /*
+ *A property with the same name already exists.
+ *We must apply here
+ *some cascading rules
+ *to compute the precedence.
+ */
+ cr_prop_list_get_decl (pair, &decl);
+ g_return_val_if_fail (decl, CR_ERROR);
+
+ /*
+ *first, look at the origin.
+ *6.4.1 says:
+ *"for normal declarations,
+ *author style sheets override user
+ *style sheets which override
+ *the default style sheet."
+ */
+ if (decl->parent_statement
+ && decl->parent_statement->parent_sheet
+ && (decl->parent_statement->parent_sheet->origin
+ < a_stmt->parent_sheet->origin)) {
+ /*
+ *if the already selected declaration
+ *is marked as being !important the current
+ *declaration must not overide it
+ *(unless the already selected declaration
+ *has an UA origin)
+ */
+ if (decl->important == TRUE
+ && decl->parent_statement->parent_sheet->origin
+ != ORIGIN_UA) {
+ continue;
+ }
+ tmp_props = cr_prop_list_unlink (props, pair);
+ if (props) {
+ cr_prop_list_destroy (pair);
+ }
+ props = tmp_props;
+ tmp_props = NULL;
+ props = cr_prop_list_append2
+ (props, cur_decl->property, cur_decl);
+
+ continue;
+ } else if (decl->parent_statement
+ && decl->parent_statement->parent_sheet
+ && (decl->parent_statement->
+ parent_sheet->origin
+ > a_stmt->parent_sheet->origin)) {
+ cr_utils_trace_info
+ ("We should not reach this line\n");
+ continue;
+ }
+
+ /*
+ *A property with the same
+ *name and the same origin already exists.
+ *shit. This is lasting longer than expected ...
+ *Luckily, the spec says in 6.4.1:
+ *"more specific selectors will override
+ *more general ones"
+ *and
+ *"if two rules have the same weight,
+ *origin and specificity,
+ *the later specified wins"
+ */
+ if (a_stmt->specificity
+ >= decl->parent_statement->specificity) {
+ if (decl->important == TRUE)
+ continue;
+ props = cr_prop_list_unlink (props, pair);
+ if (pair) {
+ cr_prop_list_destroy (pair);
+ pair = NULL;
+ }
+ props = cr_prop_list_append2 (props,
+ cur_decl->property,
+ cur_decl);
+ }
+ }
+ /*TODO: this may leak. Check this out */
+ *a_props = props;
+
+ return CR_OK;
+}
+
+static void
+set_style_from_props (CRStyle * a_style, CRPropList * a_props)
+{
+ CRPropList *cur = NULL;
+ CRDeclaration *decl = NULL;
+
+ for (cur = a_props; cur; cur = cr_prop_list_get_next (cur)) {
+ cr_prop_list_get_decl (cur, &decl);
+ cr_style_set_style_from_decl (a_style, decl);
+ decl = NULL;
+ }
+}
+
+/****************************************
+ *PUBLIC METHODS
+ ****************************************/
+
+/**
+ * cr_sel_eng_new:
+ *Creates a new instance of #CRSelEng.
+ *
+ *Returns the newly built instance of #CRSelEng of
+ *NULL if an error occurs.
+ */
+CRSelEng *
+cr_sel_eng_new (void)
+{
+ CRSelEng *result = NULL;
+
+ result = g_try_malloc (sizeof (CRSelEng));
+ if (!result) {
+ cr_utils_trace_info ("Out of memory");
+ return NULL;
+ }
+ memset (result, 0, sizeof (CRSelEng));
+
+ PRIVATE (result) = g_try_malloc (sizeof (CRSelEngPriv));
+ if (!PRIVATE (result)) {
+ cr_utils_trace_info ("Out of memory");
+ g_free (result);
+ return NULL;
+ }
+ memset (PRIVATE (result), 0, sizeof (CRSelEngPriv));
+ cr_sel_eng_register_pseudo_class_sel_handler
+ (result, (guchar *) "first-child",
+ IDENT_PSEUDO, (CRPseudoClassSelectorHandler)
+ first_child_pseudo_class_handler);
+ cr_sel_eng_register_pseudo_class_sel_handler
+ (result, (guchar *) "lang",
+ FUNCTION_PSEUDO, (CRPseudoClassSelectorHandler)
+ lang_pseudo_class_handler);
+
+ return result;
+}
+
+/**
+ * cr_sel_eng_register_pseudo_class_sel_handler:
+ *@a_this: the current instance of #CRSelEng
+ *@a_pseudo_class_sel_name: the name of the pseudo class selector.
+ *@a_pseudo_class_type: the type of the pseudo class selector.
+ *@a_handler: the actual handler or callback to be called during
+ *the selector evaluation process.
+ *
+ *Adds a new handler entry in the handlers entry table.
+ *
+ *Returns CR_OK, upon successful completion, an error code otherwise.
+ */
+enum CRStatus
+cr_sel_eng_register_pseudo_class_sel_handler (CRSelEng * a_this,
+ guchar * a_name,
+ enum CRPseudoType a_type,
+ CRPseudoClassSelectorHandler
+ a_handler)
+{
+ struct CRPseudoClassSelHandlerEntry *handler_entry = NULL;
+ GList *list = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_handler && a_name, CR_BAD_PARAM_ERROR);
+
+ handler_entry = g_try_malloc
+ (sizeof (struct CRPseudoClassSelHandlerEntry));
+ if (!handler_entry) {
+ return CR_OUT_OF_MEMORY_ERROR;
+ }
+ memset (handler_entry, 0,
+ sizeof (struct CRPseudoClassSelHandlerEntry));
+ handler_entry->name = g_strdup (a_name);
+ handler_entry->type = a_type;
+ handler_entry->handler = a_handler;
+ list = g_list_append (PRIVATE (a_this)->pcs_handlers, handler_entry);
+ if (!list) {
+ return CR_OUT_OF_MEMORY_ERROR;
+ }
+ PRIVATE (a_this)->pcs_handlers = list;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_sel_eng_unregister_pseudo_class_sel_handler (CRSelEng * a_this,
+ guchar * a_name,
+ enum CRPseudoType a_type)
+{
+ GList *elem = NULL,
+ *deleted_elem = NULL;
+ gboolean found = FALSE;
+ struct CRPseudoClassSelHandlerEntry *entry = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ for (elem = PRIVATE (a_this)->pcs_handlers;
+ elem; elem = g_list_next (elem)) {
+ entry = elem->data;
+ if (!strcmp (entry->name, a_name)
+ && entry->type == a_type) {
+ found = TRUE;
+ break;
+ }
+ }
+ if (found == FALSE)
+ return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
+ PRIVATE (a_this)->pcs_handlers = g_list_delete_link
+ (PRIVATE (a_this)->pcs_handlers, elem);
+ entry = elem->data;
+ if (entry->name)
+ g_free (entry->name);
+ g_free (elem);
+ g_list_free (deleted_elem);
+
+ return CR_OK;
+}
+
+/**
+ * cr_sel_eng_unregister_all_pseudo_class_sel_handlers:
+ *@a_this: the current instance of #CRSelEng .
+ *
+ *Unregisters all the pseudo class sel handlers
+ *and frees all the associated allocated datastructures.
+ *
+ *Returns CR_OK upon succesful completion, an error code
+ *otherwise.
+ */
+enum CRStatus
+cr_sel_eng_unregister_all_pseudo_class_sel_handlers (CRSelEng * a_this)
+{
+ GList *elem = NULL;
+ struct CRPseudoClassSelHandlerEntry *entry = NULL;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this), CR_BAD_PARAM_ERROR);
+
+ if (!PRIVATE (a_this)->pcs_handlers)
+ return CR_OK;
+ for (elem = PRIVATE (a_this)->pcs_handlers;
+ elem; elem = g_list_next (elem)) {
+ entry = elem->data;
+ if (!entry)
+ continue;
+ if (entry->name) {
+ g_free (entry->name);
+ entry->name = NULL;
+ }
+ g_free (entry);
+ elem->data = NULL;
+ }
+ g_list_free (PRIVATE (a_this)->pcs_handlers);
+ PRIVATE (a_this)->pcs_handlers = NULL;
+ return CR_OK;
+}
+
+enum CRStatus
+cr_sel_eng_get_pseudo_class_selector_handler (CRSelEng * a_this,
+ guchar * a_name,
+ enum CRPseudoType a_type,
+ CRPseudoClassSelectorHandler *
+ a_handler)
+{
+ GList *elem = NULL;
+ struct CRPseudoClassSelHandlerEntry *entry = NULL;
+ gboolean found = FALSE;
+
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_name, CR_BAD_PARAM_ERROR);
+
+ for (elem = PRIVATE (a_this)->pcs_handlers;
+ elem; elem = g_list_next (elem)) {
+ entry = elem->data;
+ if (!strcmp (a_name, entry->name)
+ && entry->type == a_type) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found == FALSE)
+ return CR_PSEUDO_CLASS_SEL_HANDLER_NOT_FOUND_ERROR;
+ *a_handler = entry->handler;
+ return CR_OK;
+}
+
+/**
+ * cr_sel_eng_matches_node:
+ *@a_this: the selection engine.
+ *@a_sel: the simple selector against which the xml node
+ *is going to be matched.
+ *@a_node: the node against which the selector is going to be matched.
+ *@a_result: out parameter. The result of the match. Is set to
+ *TRUE if the selector matches the node, FALSE otherwise. This value
+ *is considered if and only if this functions returns CR_OK.
+ *
+ *Evaluates a chained list of simple selectors (known as a css2 selector).
+ *Says wheter if this selector matches the xml node given in parameter or
+ *not.
+ *
+ *Returns the CR_OK if the selection ran correctly, an error code otherwise.
+ */
+enum CRStatus
+cr_sel_eng_matches_node (CRSelEng * a_this, CRSimpleSel * a_sel,
+ xmlNode * a_node, gboolean * a_result)
+{
+ g_return_val_if_fail (a_this && PRIVATE (a_this)
+ && a_this && a_node
+ && a_result, CR_BAD_PARAM_ERROR);
+
+ if (a_node->type != XML_ELEMENT_NODE) {
+ *a_result = FALSE;
+ return CR_OK;
+ }
+
+ return sel_matches_node_real (a_this, a_sel,
+ a_node, a_result,
+ TRUE, TRUE);
+}
+
+/**
+ * cr_sel_eng_get_matched_rulesets:
+ *@a_this: the current instance of the selection engine.
+ *@a_sheet: the stylesheet that holds the selectors.
+ *@a_node: the xml node to consider during the walk thru
+ *the stylesheet.
+ *@a_rulesets: out parameter. A pointer to an array of
+ *rulesets statement pointers. *a_rulesets is allocated by
+ *this function and must be freed by the caller. However, the caller
+ *must not alter the rulesets statements pointer because they
+ *point to statements that are still in the css stylesheet.
+ *@a_len: the length of *a_ruleset.
+ *
+ *Returns an array of pointers to selectors that matches
+ *the xml node given in parameter.
+ *
+ *Returns CR_OK upon sucessfull completion, an error code otherwise.
+ */
+enum CRStatus
+cr_sel_eng_get_matched_rulesets (CRSelEng * a_this,
+ CRStyleSheet * a_sheet,
+ xmlNode * a_node,
+ CRStatement *** a_rulesets, gulong * a_len)
+{
+ CRStatement **stmts_tab = NULL;
+ enum CRStatus status = CR_OK;
+ gulong tab_size = 0,
+ tab_len = 0,
+ index = 0;
+ gushort stmts_chunck_size = 8;
+
+ g_return_val_if_fail (a_this
+ && a_sheet
+ && a_node
+ && a_rulesets && *a_rulesets == NULL
+ && a_len, CR_BAD_PARAM_ERROR);
+
+ stmts_tab = g_try_malloc (stmts_chunck_size * sizeof (CRStatement *));
+
+ if (!stmts_tab) {
+ cr_utils_trace_info ("Out of memory");
+ status = CR_ERROR;
+ goto error;
+ }
+ memset (stmts_tab, 0, stmts_chunck_size * sizeof (CRStatement *));
+
+ tab_size = stmts_chunck_size;
+ tab_len = tab_size;
+
+ while ((status = cr_sel_eng_get_matched_rulesets_real
+ (a_this, a_sheet, a_node, stmts_tab + index, &tab_len))
+ == CR_OUTPUT_TOO_SHORT_ERROR) {
+ stmts_tab = g_try_realloc (stmts_tab,
+ (tab_size + stmts_chunck_size)
+ * sizeof (CRStatement *));
+ if (!stmts_tab) {
+ cr_utils_trace_info ("Out of memory");
+ status = CR_ERROR;
+ goto error;
+ }
+ tab_size += stmts_chunck_size;
+ index += tab_len;
+ tab_len = tab_size - index;
+ }
+
+ tab_len = tab_size - stmts_chunck_size + tab_len;
+ *a_rulesets = stmts_tab;
+ *a_len = tab_len;
+
+ return CR_OK;
+
+ error:
+
+ if (stmts_tab) {
+ g_free (stmts_tab);
+ stmts_tab = NULL;
+
+ }
+
+ *a_len = 0;
+ return status;
+}
+
+
+enum CRStatus
+cr_sel_eng_get_matched_properties_from_cascade (CRSelEng * a_this,
+ CRCascade * a_cascade,
+ xmlNode * a_node,
+ CRPropList ** a_props)
+{
+ CRStatement **stmts_tab = NULL;
+ enum CRStatus status = CR_OK;
+ gulong tab_size = 0,
+ tab_len = 0,
+ i = 0,
+ index = 0;
+ enum CRStyleOrigin origin = 0;
+ gushort stmts_chunck_size = 8;
+ CRStyleSheet *sheet = NULL;
+
+ g_return_val_if_fail (a_this
+ && a_cascade
+ && a_node && a_props, CR_BAD_PARAM_ERROR);
+
+ for (origin = ORIGIN_UA; origin < NB_ORIGINS; origin++) {
+ sheet = cr_cascade_get_sheet (a_cascade, origin);
+ if (!sheet)
+ continue;
+ if (tab_size - index < 1) {
+ stmts_tab = g_try_realloc
+ (stmts_tab, (tab_size + stmts_chunck_size)
+ * sizeof (CRStatement *));
+ if (!stmts_tab) {
+ cr_utils_trace_info ("Out of memory");
+ status = CR_ERROR;
+ goto cleanup;
+ }
+ tab_size += stmts_chunck_size;
+ /*
+ *compute the max size left for
+ *cr_sel_eng_get_matched_rulesets_real()'s output tab
+ */
+ tab_len = tab_size - index;
+ }
+ while ((status = cr_sel_eng_get_matched_rulesets_real
+ (a_this, sheet, a_node, stmts_tab + index, &tab_len))
+ == CR_OUTPUT_TOO_SHORT_ERROR) {
+ stmts_tab = g_try_realloc
+ (stmts_tab, (tab_size + stmts_chunck_size)
+ * sizeof (CRStatement *));
+ if (!stmts_tab) {
+ cr_utils_trace_info ("Out of memory");
+ status = CR_ERROR;
+ goto cleanup;
+ }
+ tab_size += stmts_chunck_size;
+ index += tab_len;
+ /*
+ *compute the max size left for
+ *cr_sel_eng_get_matched_rulesets_real()'s output tab
+ */
+ tab_len = tab_size - index;
+ }
+ if (status != CR_OK) {
+ cr_utils_trace_info ("Error while running "
+ "selector engine");
+ goto cleanup;
+ }
+ index += tab_len;
+ tab_len = tab_size - index;
+ }
+
+ /*
+ *TODO, walk down the stmts_tab and build the
+ *property_name/declaration hashtable.
+ *Make sure one can walk from the declaration to
+ *the stylesheet.
+ */
+ for (i = 0; i < index; i++) {
+ CRStatement *stmt = stmts_tab[i];
+
+ if (!stmt)
+ continue;
+ switch (stmt->type) {
+ case RULESET_STMT:
+ if (!stmt->parent_sheet)
+ continue;
+ status = put_css_properties_in_props_list
+ (a_props, stmt);
+ break;
+ default:
+ break;
+ }
+
+ }
+ status = CR_OK ;
+ cleanup:
+ if (stmts_tab) {
+ g_free (stmts_tab);
+ stmts_tab = NULL;
+ }
+
+ return status;
+}
+
+enum CRStatus
+cr_sel_eng_get_matched_style (CRSelEng * a_this,
+ CRCascade * a_cascade,
+ xmlNode * a_node,
+ CRStyle * a_parent_style,
+ CRStyle ** a_style,
+ gboolean a_set_props_to_initial_values)
+{
+ enum CRStatus status = CR_OK;
+
+ CRPropList *props = NULL;
+
+ g_return_val_if_fail (a_this && a_cascade
+ && a_node && a_style, CR_BAD_PARAM_ERROR);
+
+ status = cr_sel_eng_get_matched_properties_from_cascade
+ (a_this, a_cascade, a_node, &props);
+
+ g_return_val_if_fail (status == CR_OK, status);
+ if (props) {
+ if (!*a_style) {
+ *a_style = cr_style_new (a_set_props_to_initial_values) ;
+ g_return_val_if_fail (*a_style, CR_ERROR);
+ } else {
+ if (a_set_props_to_initial_values == TRUE) {
+ cr_style_set_props_to_initial_values (*a_style) ;
+ } else {
+ cr_style_set_props_to_default_values (*a_style);
+ }
+ }
+ (*a_style)->parent_style = a_parent_style;
+
+ set_style_from_props (*a_style, props);
+ if (props) {
+ cr_prop_list_destroy (props);
+ props = NULL;
+ }
+ }
+ return CR_OK;
+}
+
+/**
+ * cr_sel_eng_destroy:
+ *@a_this: the current instance of the selection engine.
+ *
+ *The destructor of #CRSelEng
+ */
+void
+cr_sel_eng_destroy (CRSelEng * a_this)
+{
+ g_return_if_fail (a_this);
+
+ if (!PRIVATE (a_this))
+ goto end ;
+ if (PRIVATE (a_this)->pcs_handlers) {
+ cr_sel_eng_unregister_all_pseudo_class_sel_handlers
+ (a_this) ;
+ PRIVATE (a_this)->pcs_handlers = NULL ;
+ }
+ g_free (PRIVATE (a_this));
+ PRIVATE (a_this) = NULL;
+ end:
+ if (a_this) {
+ g_free (a_this);
+ }
+}