From f4cd41d6a53518399d442c4dd730faa100dba16e Mon Sep 17 00:00:00 2001 From: Vishal Agarwal Date: Sat, 2 Sep 2017 18:34:08 +0200 Subject: Implement CSS 3 pseudo selectors. GSoC project 2017. --- src/libcroco/cr-parser.c | 29 +- src/libcroco/cr-pseudo.c | 16 +- src/libcroco/cr-pseudo.h | 4 +- src/libcroco/cr-sel-eng.c | 688 ++++++++++++++++++++++++++++++++++++++++++++-- src/libcroco/cr-sel-eng.h | 8 + src/libcroco/cr-term.c | 1 + src/libcroco/cr-term.h | 5 + src/libcroco/cr-token.h | 14 +- 8 files changed, 722 insertions(+), 43 deletions(-) (limited to 'src') diff --git a/src/libcroco/cr-parser.c b/src/libcroco/cr-parser.c index 4e50b5402..454e1c330 100644 --- a/src/libcroco/cr-parser.c +++ b/src/libcroco/cr-parser.c @@ -1499,6 +1499,9 @@ cr_parser_parse_property (CRParser * a_this, *EMS S* | EXS S* | ANGLE S* | TIME S* | FREQ S* | function ] | *STRING S* | IDENT S* | URI S* | RGB S* | UNICODERANGE S* | hexcolor * + * As a special case for 'an+b', parse integer followed immediately by 'n'. + * The 'an' is parsed as a DIMEN. + * *TODO: handle parsing of 'RGB' * *Returns CR_OK upon successfull completion, an error code otherwise. @@ -1595,6 +1598,13 @@ cr_parser_parse_term (CRParser * a_this, CRTerm ** a_term) status = cr_term_set_hash (result, token->u.str); CHECK_PARSING_STATUS (status, TRUE); token->u.str = NULL; + } else if (token && token->type == DIMEN_TK) { + gboolean n = !strcmp(token->dimen->stryng->str, "n"); + status = cr_term_set_number (result, token->u.num); + result->n = n; // For nth-child (an+b) + CHECK_PARSING_STATUS (status, TRUE); + token->u.num = NULL; + status = CR_OK; } else { status = CR_PARSING_ERROR; } @@ -1819,22 +1829,21 @@ cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel) (&pseudo->location, &token->location) ; + /* Save selector name for use by 'type' pseudo selectors */ + if (sel->name) + pseudo->sel_name = cr_string_dup (sel->name); + if (token->type == IDENT_TK) { pseudo->type = IDENT_PSEUDO; pseudo->name = token->u.str; token->u.str = NULL; found_sel = TRUE; } else if (token->type == FUNCTION_TK) { - pseudo->name = token->u.str; - token->u.str = NULL; - cr_parser_try_to_skip_spaces_and_comments - (a_this); - status = cr_parser_parse_ident - (a_this, &pseudo->extra); - + status = cr_tknzr_unget_token (PRIVATE (a_this)->tknzr, token); + token = NULL; + status = cr_parser_parse_function (a_this, &pseudo->name, &pseudo->term); ENSURE_PARSING_COND (status == CR_OK); - READ_NEXT_CHAR (a_this, &cur_char); - ENSURE_PARSING_COND (cur_char == ')'); + pseudo->type = FUNCTION_PSEUDO; found_sel = TRUE; } else { @@ -1863,7 +1872,7 @@ cr_parser_parse_simple_selector (CRParser * a_this, CRSimpleSel ** a_sel) token = NULL; break; } - } + } // for loop if (status == CR_OK && found_sel == TRUE) { cr_parser_try_to_skip_spaces_and_comments (a_this); diff --git a/src/libcroco/cr-pseudo.c b/src/libcroco/cr-pseudo.c index cee3fc869..8715c19f1 100644 --- a/src/libcroco/cr-pseudo.c +++ b/src/libcroco/cr-pseudo.c @@ -86,11 +86,6 @@ cr_pseudo_to_string (CRPseudo const * a_this) name = (guchar *) g_strndup (a_this->name->stryng->str, a_this->name->stryng->len); - if (a_this->extra) { - arg = (guchar *) g_strndup (a_this->extra->stryng->str, - a_this->extra->stryng->len); - } - if (name) { g_string_append_printf (str_buf, "%s(", name); g_free (name); @@ -158,9 +153,14 @@ cr_pseudo_destroy (CRPseudo * a_this) a_this->name = NULL; } - if (a_this->extra) { - cr_string_destroy (a_this->extra); - a_this->extra = NULL; + if (a_this->sel_name) { + cr_string_destroy (a_this->sel_name); + a_this->sel_name = NULL; + } + + if (a_this->term) { + cr_term_destroy (a_this->term); + a_this->term = NULL; } g_free (a_this); diff --git a/src/libcroco/cr-pseudo.h b/src/libcroco/cr-pseudo.h index 8917da45e..b78435014 100644 --- a/src/libcroco/cr-pseudo.h +++ b/src/libcroco/cr-pseudo.h @@ -27,6 +27,7 @@ #include #include "cr-attr-sel.h" #include "cr-parsing-location.h" +#include "cr-term.h" G_BEGIN_DECLS @@ -47,7 +48,8 @@ struct _CRPseudo { enum CRPseudoType type ; CRString *name ; - CRString *extra ; + CRString *sel_name; + CRTerm *term ; CRParsingLocation location ; } ; diff --git a/src/libcroco/cr-sel-eng.c b/src/libcroco/cr-sel-eng.c index 665ba4daf..3c96ac7c7 100644 --- a/src/libcroco/cr-sel-eng.c +++ b/src/libcroco/cr-sel-eng.c @@ -87,26 +87,67 @@ static enum CRStatus put_css_properties_in_props_list (CRPropList ** a_props, a_ruleset); static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this, - CRAdditionalSel * - a_add_sel, + CRAdditionalSel * a_add_sel, CRXMLNodePtr a_node); +static gboolean empty_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean root_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + static gboolean lang_pseudo_class_handler (CRSelEng * a_this, CRAdditionalSel * a_sel, CRXMLNodePtr a_node); +static gboolean only_child_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + static gboolean first_child_pseudo_class_handler (CRSelEng * a_this, CRAdditionalSel * a_sel, CRXMLNodePtr a_node); +static gboolean first_of_type_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean last_child_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean last_of_type_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean nth_child_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean nth_of_type_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean nth_last_child_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + +static gboolean nth_last_of_type_pseudo_class_handler (CRSelEng * a_this, + CRAdditionalSel * a_sel, + CRXMLNodePtr a_node); + static CRXMLNodePtr get_next_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node); -static CRXMLNodePtr get_next_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node); +static CRXMLNodePtr get_first_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node); static CRXMLNodePtr get_prev_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node); static CRXMLNodePtr get_next_parent_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node); +static CRArguments get_arguments_from_function (CRAdditionalSel * a_sel); + void cr_sel_eng_set_node_iface (CRSelEng *const a_this, CRNodeIface const *const a_node_iface) { @@ -119,6 +160,60 @@ cr_sel_eng_set_node_iface (CRSelEng *const a_this, CRNodeIface const *const a_no #define strqcmp(str,lit,lit_len) \ (strlen (str) != (lit_len) || memcmp (str, lit, lit_len)) +static gboolean +root_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "root") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :root only"); + return FALSE; + } + + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + + // libxml apears to set the parent of the root element to an + // element of type 'xml'. + return (parent == NULL || !strcmp(node_iface->getLocalName(parent),"xml") ); +} + +static gboolean +empty_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "empty") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :empty only"); + return FALSE; + } + node_iface = PRIVATE(a_this)->node_iface; + + CRXMLNodePtr cur_node = NULL; + cur_node = node_iface->getFirstChild (a_node); + + return (cur_node == NULL); +} + static gboolean lang_pseudo_class_handler (CRSelEng *const a_this, CRAdditionalSel * a_sel, CRXMLNodePtr a_node) @@ -144,15 +239,16 @@ lang_pseudo_class_handler (CRSelEng *const a_this, return FALSE; } /*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) + if (!a_sel->content.pseudo->term + || a_sel->content.pseudo->term->type != TERM_IDENT + || !a_sel->content.pseudo->term->content.str->stryng + || a_sel->content.pseudo->term->content.str->stryng->len < 2) return FALSE; for (; node; node = get_next_parent_element_node (node_iface, node)) { char *val = node_iface->getProp (node, (const xmlChar *) "lang"); if (!val) val = node_iface->getProp (node, (const xmlChar *) "xml:lang"); if (val) { - if (!strcasecmp(val, a_sel->content.pseudo->extra->stryng->str)) { + if (!strcasecmp(val, a_sel->content.pseudo->term->content.str->stryng->str)) { result = TRUE; break; } @@ -164,12 +260,85 @@ lang_pseudo_class_handler (CRSelEng *const a_this, return result; } +static gboolean +only_child_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "only-child") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :only-child only"); + return FALSE; + } + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + CRXMLNodePtr cur_node = NULL; + + cur_node = get_first_child_element_node (node_iface, parent); + return (cur_node == a_node && + !get_next_element_node(node_iface, cur_node) ); +} + +static gboolean +only_of_type_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "only-of-type") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :only-of-type selector only"); + return FALSE; + } + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + CRXMLNodePtr cur_node = NULL; + int m = 0; + int child = 0; + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + if (!strcmp(node_iface->getLocalName(cur_node), a_sel->content.pseudo->sel_name->stryng->str)) { + ++m; + } + if (cur_node == a_node) { + child = m; + } + cur_node = get_next_element_node (node_iface, cur_node); + } + return (child == m && child == 1); +} + static gboolean first_child_pseudo_class_handler (CRSelEng *const a_this, CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) { CRNodeIface const *node_iface = NULL; - CRXMLNodePtr node = NULL, parent = NULL; + CRXMLNodePtr node = NULL; + CRXMLNodePtr parent = NULL; g_return_val_if_fail (a_this && PRIVATE (a_this) && a_sel && a_sel->content.pseudo @@ -178,8 +347,7 @@ first_child_pseudo_class_handler (CRSelEng *const a_this, && a_sel->content.pseudo->name->stryng && a_node, FALSE); - if (strcmp (a_sel->content.pseudo->name->stryng->str, - "first-child") + 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 FALSE; @@ -188,10 +356,397 @@ first_child_pseudo_class_handler (CRSelEng *const a_this, parent = node_iface->getParentNode (a_node); if (!parent) return FALSE; - node = get_next_child_element_node (node_iface, parent); + node = get_first_child_element_node (node_iface, parent); return (node == a_node); } +static gboolean +first_of_type_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "first-of-type") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :first-of-type only"); + return FALSE; + } + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + // Count which child no. of type + CRXMLNodePtr cur_node = NULL; + int child = 0; + int found = FALSE; + + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + if(!strcmp(node_iface->getLocalName(cur_node), a_sel->content.pseudo->sel_name->stryng->str)) { + child++; + } + if (cur_node == a_node) { + found = TRUE; + break; + } + cur_node = get_next_element_node (node_iface, cur_node); + } + + if (!found) + return FALSE; + + return (child == 1); +} + +static gboolean +last_child_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "last-child") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :last-child only"); + return FALSE; + } + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + CRXMLNodePtr cur_node = NULL; + int m = 0; + int child = 0; + + cur_node = get_first_child_element_node (node_iface, parent); + while (cur_node) { + ++m; + if (cur_node == a_node) { + child = m; + } + cur_node = get_next_element_node (node_iface, cur_node); + + } + return (m == child); +} + +static gboolean +last_of_type_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "last-of-type") + || a_sel->content.pseudo->type != IDENT_PSEUDO) { + cr_utils_trace_info ("This handler is for :last-of-type only"); + return FALSE; + } + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + CRXMLNodePtr cur_node = NULL; + int m = 0; + int child = 0; + + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + if (!strcmp(node_iface->getLocalName(cur_node), a_sel->content.pseudo->sel_name->stryng->str)) { + ++m; + } + if (cur_node == a_node) { + child = m; + } + cur_node = get_next_element_node (node_iface, cur_node); + } + + return (m == child); +} + +// See https://www.w3.org/TR/selectors/#nth-child-pseudo +static gboolean +nth_child_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "nth-child") + || a_sel->content.pseudo->type != FUNCTION_PSEUDO) { + cr_utils_trace_info ("This handler is for :nth-child only"); + return FALSE; + } + + /*pseude code term should exist */ + if (!a_sel->content.pseudo->term) + return FALSE; + + CRArguments arg = get_arguments_from_function (a_sel); + + if (arg.a == 0 && arg.b == 0) + return FALSE; + + int a = arg.a; + int b = arg.b; + + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + /* Count which child this is */ + CRXMLNodePtr cur_node = NULL; + int child = 0; + int found = FALSE; + + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + ++child; + if (cur_node == a_node) { + found = TRUE; + break; + } + cur_node = get_next_element_node (node_iface, cur_node); + } + + if (!found) + return FALSE; + + if (a == 0) + return (b == child); + + return ((child - b)%a == 0 && (child - b)/a > -1); +} + +static gboolean +nth_of_type_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "nth-of-type") + || a_sel->content.pseudo->type != FUNCTION_PSEUDO) { + cr_utils_trace_info ("This handler is for :nth-of-type only"); + return FALSE; + } + + // pseudo code term should exist + if (!a_sel->content.pseudo->term) + return FALSE; + + CRArguments arg = get_arguments_from_function (a_sel); + + if (arg.a == 0 && arg.b == 0) + return FALSE; + + int a = arg.a; + int b = arg.b; + + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) + return FALSE; + + // Count which child no. of required type + CRXMLNodePtr cur_node = NULL; + int child = 0; + int found = FALSE; + + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + // check if type match + if (!strcmp(node_iface->getLocalName(cur_node), a_sel->content.pseudo->sel_name->stryng->str)) + ++child; + if (cur_node == a_node) { + found = TRUE; + break; + } + cur_node = get_next_element_node (node_iface, cur_node); + } + + if (!found) + return FALSE; + + if (a == 0) + return (b == child); + + return ((child - b)%a == 0 && (child - b)/a > -1); +} + +static gboolean +nth_last_child_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "nth-last-child")) { + cr_utils_trace_info ("This handler is for :nth-last-child only"); + return FALSE; + } + + /*pseudo code term should exist */ + if (!a_sel->content.pseudo->term) + return FALSE; + + CRArguments arg = get_arguments_from_function (a_sel); + + if (arg.a == 0 && arg.b == 0) + return FALSE; + + int a = arg.a; + int b = arg.b; + + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + + if (!parent) { + return FALSE; + } + + /* Count which child this is (child) and total number of children (m). */ + + CRXMLNodePtr cur_node = NULL; + int m = 0; + int child = 0; + int found = FALSE; + + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + if (cur_node == a_node) { + found = TRUE; + child = m; + } + cur_node = get_next_element_node (node_iface,cur_node); + ++m; + } + + if (!found) + return FALSE; + + if (a == 0) + return ((m - b) == child); + + return ((m - child - b)%a == 0 && (m - child - b)/a > -1); +} + +static gboolean +nth_last_of_type_pseudo_class_handler (CRSelEng *const a_this, + CRAdditionalSel * a_sel, CRXMLNodePtr const a_node) +{ + CRNodeIface const *node_iface = NULL; + CRXMLNodePtr parent = 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, FALSE); + + if (strcmp (a_sel->content.pseudo->name->stryng->str, "nth-last-of-type")) { + cr_utils_trace_info ("This handler is for :nth-last-of-type only"); + return FALSE; + } + + /*pseude code term should exist */ + if (!a_sel->content.pseudo->term) + return FALSE; + + CRArguments arg = get_arguments_from_function (a_sel); + + if (arg.a == 0 && arg.b == 0) + return FALSE; + + int a = arg.a; + int b = arg.b; + + node_iface = PRIVATE(a_this)->node_iface; + parent = node_iface->getParentNode (a_node); + if (!parent) { + return FALSE; + } + + CRXMLNodePtr cur_node = NULL; + int m = 0; + int child = 0; + int found = FALSE; + + cur_node = get_first_child_element_node (node_iface, parent); + + while (cur_node) { + if (!strcmp(node_iface->getLocalName(cur_node), a_sel->content.pseudo->sel_name->stryng->str)) + ++m; + if (cur_node == a_node) { + found = TRUE; + child = m; + } + cur_node = get_next_element_node (node_iface, cur_node); + } + + if (!found) + return FALSE; + + if (a == 0) + return ((m - b) == child); + + return ((m - child - b +1)%a == 0 && (m - child - b +1)/a > -1); + +} + static gboolean pseudo_class_add_sel_matches_node (CRSelEng * a_this, CRAdditionalSel * a_add_sel, @@ -539,11 +1094,8 @@ get_next_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node) return cur_node; } -/* TODO: Consider renaming this to get_first_child_element_node. - (cf get_first_parent_element_node, which does getParent until element node - rather than getNextSibling). */ static CRXMLNodePtr -get_next_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node) +get_first_child_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_node) { CRXMLNodePtr cur_node = NULL; @@ -583,6 +1135,64 @@ get_next_parent_element_node (CRNodeIface const * a_node_iface, CRXMLNodePtr a_n return cur_node; } +static CRArguments +get_arguments_from_function (CRAdditionalSel * a_sel) +{ + CRArguments arg; + arg.a = 0; + arg.b = 0; + switch (a_sel->content.pseudo->term->type) { + case TERM_NUMBER: + if (a_sel->content.pseudo->term->content.num) { + arg.b = a_sel->content.pseudo->term->content.num->val; + } + if (a_sel->content.pseudo->term->n) { + arg.a = arg.b; + arg.b = 0; + } + break; + + case TERM_IDENT: + if (a_sel->content.pseudo->term->content.str) { + if (!strcmp(a_sel->content.pseudo->term->content.str->stryng->str, "even")) { + arg.a = 2; + arg.b = 0; + } else if (!strcmp(a_sel->content.pseudo->term->content.str->stryng->str, "odd")) { + arg.a = 2; + arg.b = 1; + } else if (!strcmp(a_sel->content.pseudo->term->content.str->stryng->str, "n")) { + /* 'n' without number */ + arg.a = 1; + } else if (!strcmp(a_sel->content.pseudo->term->content.str->stryng->str, "-n")) { + /* '-n' without number */ + arg.a = -1; + } else { + /* Unknown string */ + arg.a = 0; + arg.b = 0; + return (arg); + } + } + break; + + default: + cr_utils_trace_info ("Unknown term in nth style handler"); + arg.a = 0; + arg.b = 0; + return (arg); + } + + if (arg.a != 0 && a_sel->content.pseudo->term->next) { + /* check for b in 'an+b' */ + if (a_sel->content.pseudo->term->next->type == TERM_NUMBER && + a_sel->content.pseudo->term->next->content.num ) { + arg.b = a_sel->content.pseudo->term->next->content.num->val; + } + } + + return (arg); +} + /** *Evaluate a selector (a simple selectors list) and says *if it matches the xml node given in parameter. @@ -1121,13 +1731,57 @@ cr_sel_eng_new (void) } memset (PRIVATE (result), 0, sizeof (CRSelEngPriv)); cr_sel_eng_register_pseudo_class_sel_handler - (result, (guchar *) "first-child", + (result, (guchar *) "root", IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ - first_child_pseudo_class_handler); + root_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "empty", + IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + empty_pseudo_class_handler); cr_sel_eng_register_pseudo_class_sel_handler (result, (guchar *) "lang", FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ lang_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "only-child", + IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + only_child_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "only-of-type", + IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + only_of_type_pseudo_class_handler); + 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 *) "first-of-type", + IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + first_of_type_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "last-child", + IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + last_child_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "last-of-type", + IDENT_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + last_of_type_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "nth-child", + FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + nth_child_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "nth-of-type", + FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + nth_of_type_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "nth-last-child", + FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + nth_last_child_pseudo_class_handler); + cr_sel_eng_register_pseudo_class_sel_handler + (result, (guchar *) "nth-last-of-type", + FUNCTION_PSEUDO, /*(CRPseudoClassSelectorHandler)*/ + nth_last_of_type_pseudo_class_handler); return result; } diff --git a/src/libcroco/cr-sel-eng.h b/src/libcroco/cr-sel-eng.h index 564debc8d..206990eb6 100644 --- a/src/libcroco/cr-sel-eng.h +++ b/src/libcroco/cr-sel-eng.h @@ -44,6 +44,14 @@ G_BEGIN_DECLS typedef struct _CRSelEng CRSelEng ; typedef struct _CRSelEngPriv CRSelEngPriv ; +typedef struct _CRArguments CRArguments ; + +//stores arguments of function of type an+b +struct _CRArguments +{ + int a; + int b; +} ; /** *The Selection engine class. diff --git a/src/libcroco/cr-term.c b/src/libcroco/cr-term.c index 1c50aed2a..aec524b0b 100644 --- a/src/libcroco/cr-term.c +++ b/src/libcroco/cr-term.c @@ -74,6 +74,7 @@ cr_term_clear (CRTerm * a_this) } a_this->type = TERM_NO_TYPE; + a_this->n = FALSE; } /** diff --git a/src/libcroco/cr-term.h b/src/libcroco/cr-term.h index e85867afb..39b9c6a8b 100644 --- a/src/libcroco/cr-term.h +++ b/src/libcroco/cr-term.h @@ -122,6 +122,11 @@ struct _CRTerm CRTerm *func_param ; } ext_content ; + /** + *A flag to indicate if there is an n as in the nth child expression 'an+b'. + */ + gboolean n; + /** *A spare pointer, just in case. *Can be used by the application. diff --git a/src/libcroco/cr-token.h b/src/libcroco/cr-token.h index f1257b7a8..35e914d2d 100644 --- a/src/libcroco/cr-token.h +++ b/src/libcroco/cr-token.h @@ -35,37 +35,37 @@ G_BEGIN_DECLS enum CRTokenType { - NO_TK, + NO_TK, // 0 S_TK, CDO_TK, CDC_TK, INCLUDES_TK, - DASHMATCH_TK, + DASHMATCH_TK, // 5 COMMENT_TK, STRING_TK, IDENT_TK, HASH_TK, - IMPORT_SYM_TK, + IMPORT_SYM_TK, // 10 PAGE_SYM_TK, MEDIA_SYM_TK, FONT_FACE_SYM_TK, CHARSET_SYM_TK, - ATKEYWORD_TK, + ATKEYWORD_TK, // 15 IMPORTANT_SYM_TK, EMS_TK, EXS_TK, LENGTH_TK, - ANGLE_TK, + ANGLE_TK, // 20 TIME_TK, FREQ_TK, DIMEN_TK, PERCENTAGE_TK, - NUMBER_TK, + NUMBER_TK, // 25 RGB_TK, URI_TK, FUNCTION_TK, UNICODERANGE_TK, - SEMICOLON_TK, + SEMICOLON_TK, // 30 CBO_TK, /*opening curly bracket*/ CBC_TK, /*closing curly bracket*/ PO_TK, /*opening parenthesis*/ -- cgit v1.2.3