summaryrefslogtreecommitdiffstats
path: root/src/libcroco
diff options
context:
space:
mode:
authorVishal Agarwal <vishalagarwal.jiit@gmail.com>2017-09-02 16:34:08 +0000
committerTavmjong Bah <tavmjong@free.fr>2017-09-02 16:34:08 +0000
commitf4cd41d6a53518399d442c4dd730faa100dba16e (patch)
tree13592f0b5a77d9eb2d171f535a261612704fd72a /src/libcroco
parentRessurrect Spiro Symplify by LPE because no wrong results and retain removed ... (diff)
downloadinkscape-f4cd41d6a53518399d442c4dd730faa100dba16e.tar.gz
inkscape-f4cd41d6a53518399d442c4dd730faa100dba16e.zip
Implement CSS 3 pseudo selectors. GSoC project 2017.
Diffstat (limited to 'src/libcroco')
-rw-r--r--src/libcroco/cr-parser.c29
-rw-r--r--src/libcroco/cr-pseudo.c16
-rw-r--r--src/libcroco/cr-pseudo.h4
-rw-r--r--src/libcroco/cr-sel-eng.c688
-rw-r--r--src/libcroco/cr-sel-eng.h8
-rw-r--r--src/libcroco/cr-term.c1
-rw-r--r--src/libcroco/cr-term.h5
-rw-r--r--src/libcroco/cr-token.h14
8 files changed, 722 insertions, 43 deletions
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 <glib.h>
#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)
{
@@ -120,6 +161,60 @@ cr_sel_eng_set_node_iface (CRSelEng *const a_this, CRNodeIface const *const a_no
(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;
}
@@ -165,11 +261,84 @@ lang_pseudo_class_handler (CRSelEng *const a_this,
}
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,11 +356,398 @@ 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,
CRXMLNodePtr a_node)
@@ -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
@@ -123,6 +123,11 @@ struct _CRTerm
} 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*/