From 9fa9b9d104c730ef6d84b19245b61ebc9554b432 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Mon, 31 Jan 2011 00:18:15 +0000 Subject: CSS3 Selectors svn path=/trunk/libcss/; revision=11557 --- include/libcss/select.h | 23 +- src/parse/language.c | 476 +++++++++++++++++++++++++++++++++++------- src/parse/propstrings.c | 18 ++ src/parse/propstrings.h | 5 +- src/select/select.c | 297 +++++++++++++++++++++++++- src/select/select.h | 16 ++ src/stylesheet.c | 38 ++-- src/stylesheet.h | 71 +++++-- src/utils/utils.c | 25 ++- src/utils/utils.h | 2 + test/data/parse/INDEX | 1 + test/data/parse/nth.dat | 246 ++++++++++++++++++++++ test/data/parse/selectors.dat | 224 ++++++++++++++++++++ test/dump.h | 73 ++++++- test/parse-auto.c | 81 +++++-- test/select-auto.c | 284 ++++++++++++++++++++++++- 16 files changed, 1728 insertions(+), 152 deletions(-) create mode 100644 test/data/parse/nth.dat diff --git a/include/libcss/select.h b/include/libcss/select.h index 9e77d68..b912fa3 100644 --- a/include/libcss/select.h +++ b/include/libcss/select.h @@ -63,6 +63,8 @@ typedef struct css_select_handler { lwc_string *name, void **parent); css_error (*named_sibling_node)(void *pw, void *node, lwc_string *name, void **sibling); + css_error (*named_generic_sibling_node)(void *pw, void *node, + lwc_string *name, void **sibling); css_error (*parent_node)(void *pw, void *node, void **parent); css_error (*sibling_node)(void *pw, void *node, void **sibling); @@ -84,13 +86,32 @@ typedef struct css_select_handler { css_error (*node_has_attribute_includes)(void *pw, void *node, lwc_string *name, lwc_string *value, bool *match); + css_error (*node_has_attribute_prefix)(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match); + css_error (*node_has_attribute_suffix)(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match); + css_error (*node_has_attribute_substring)(void *pw, void *node, + lwc_string *name, lwc_string *value, + bool *match); + + css_error (*node_is_root)(void *pw, void *node, bool *match); + css_error (*node_count_siblings)(void *pw, void *node, + bool same_name, bool after, int32_t *count); + css_error (*node_is_empty)(void *pw, void *node, bool *match); - css_error (*node_is_first_child)(void *pw, void *node, bool *match); css_error (*node_is_link)(void *pw, void *node, bool *match); css_error (*node_is_visited)(void *pw, void *node, bool *match); css_error (*node_is_hover)(void *pw, void *node, bool *match); css_error (*node_is_active)(void *pw, void *node, bool *match); css_error (*node_is_focus)(void *pw, void *node, bool *match); + + css_error (*node_is_enabled)(void *pw, void *node, bool *match); + css_error (*node_is_disabled)(void *pw, void *node, bool *match); + css_error (*node_is_checked)(void *pw, void *node, bool *match); + + css_error (*node_is_target)(void *pw, void *node, bool *match); css_error (*node_is_lang)(void *pw, void *node, lwc_string *lang, bool *match); diff --git a/src/parse/language.c b/src/parse/language.c index c003ed6..1f1aeb5 100644 --- a/src/parse/language.c +++ b/src/parse/language.c @@ -63,10 +63,16 @@ static css_error parseClass(css_language *c, static css_error parseAttrib(css_language *c, const parserutils_vector *vector, int *ctx, css_selector_detail *specific); +static css_error parseNth(css_language *c, + const parserutils_vector *vector, int *ctx, + css_selector_detail_value *value); static css_error parsePseudo(css_language *c, const parserutils_vector *vector, int *ctx, - css_selector_detail *specific); + bool in_not, css_selector_detail *specific); static css_error parseSpecific(css_language *c, + const parserutils_vector *vector, int *ctx, + bool in_not, css_selector_detail *specific); +static css_error parseAppendSpecific(css_language *c, const parserutils_vector *vector, int *ctx, css_selector **parent); static css_error parseSelectorSpecifics(css_language *c, @@ -808,6 +814,7 @@ css_error parseMediaList(css_language *c, css_error parseClass(css_language *c, const parserutils_vector *vector, int *ctx, css_selector_detail *specific) { + css_selector_detail_value detail_value; const css_token *token; /* class -> '.' IDENT */ @@ -819,18 +826,28 @@ css_error parseClass(css_language *c, const parserutils_vector *vector, if (token == NULL || token->type != CSS_TOKEN_IDENT) return CSS_INVALID; + detail_value.string = NULL; + return css__stylesheet_selector_detail_init(c->sheet, - CSS_SELECTOR_CLASS, token->idata, NULL, specific); + CSS_SELECTOR_CLASS, token->idata, detail_value, + CSS_SELECTOR_DETAIL_VALUE_STRING, false, specific); } css_error parseAttrib(css_language *c, const parserutils_vector *vector, int *ctx, css_selector_detail *specific) { + css_selector_detail_value detail_value; const css_token *token, *name, *value = NULL; css_selector_type type = CSS_SELECTOR_ATTRIBUTE; /* attrib -> '[' ws IDENT ws [ - * [ '=' | INCLUDES | DASHMATCH ] ws + * [ '=' | + * INCLUDES | + * DASHMATCH | + * PREFIXMATCH | + * SUFFIXMATCH | + * SUBSTRINGMATCH + * ] ws * [ IDENT | STRING ] ws ]? ']' */ token = parserutils_vector_iterate(vector, ctx); @@ -858,6 +875,12 @@ css_error parseAttrib(css_language *c, const parserutils_vector *vector, type = CSS_SELECTOR_ATTRIBUTE_INCLUDES; else if (token->type == CSS_TOKEN_DASHMATCH) type = CSS_SELECTOR_ATTRIBUTE_DASHMATCH; + else if (token->type == CSS_TOKEN_PREFIXMATCH) + type = CSS_SELECTOR_ATTRIBUTE_PREFIX; + else if (token->type == CSS_TOKEN_SUFFIXMATCH) + type = CSS_SELECTOR_ATTRIBUTE_SUFFIX; + else if (token->type == CSS_TOKEN_SUBSTRINGMATCH) + type = CSS_SELECTOR_ATTRIBUTE_SUBSTRING; else return CSS_INVALID; @@ -877,107 +900,387 @@ css_error parseAttrib(css_language *c, const parserutils_vector *vector, return CSS_INVALID; } + detail_value.string = value != NULL ? value->idata : NULL; + return css__stylesheet_selector_detail_init(c->sheet, type, - name->idata, value != NULL ? value->idata : NULL, - specific); + name->idata, detail_value, + CSS_SELECTOR_DETAIL_VALUE_STRING, false, specific); +} + +css_error parseNth(css_language *c, + const parserutils_vector *vector, int *ctx, + css_selector_detail_value *value) +{ + const css_token *token; + bool match; + + /* nth -> [ DIMENSION | IDENT ] ws [ [ CHAR ws ]? NUMBER ws ]? + * (e.g. DIMENSION: 2n-1, 2n- 1, 2n -1, 2n - 1) + * (e.g. IDENT: -n-1, -n- 1, -n -1, -n - 1) + * -> NUMBER ws + * -> IDENT(odd) ws + * -> IDENT(even) ws + */ + + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || (token->type != CSS_TOKEN_IDENT && + token->type != CSS_TOKEN_NUMBER && + token->type != CSS_TOKEN_DIMENSION)) + return CSS_INVALID; + + if (token->type == CSS_TOKEN_IDENT && + lwc_string_caseless_isequal(token->idata, + c->strings[ODD], &match) == lwc_error_ok && + match) { + /* Odd */ + value->nth.a = 2; + value->nth.b = 1; + } else if (token->type == CSS_TOKEN_IDENT && + lwc_string_caseless_isequal(token->idata, + c->strings[EVEN], &match) == lwc_error_ok && + match) { + /* Even */ + value->nth.a = 2; + value->nth.b = 0; + } else if (token->type == CSS_TOKEN_NUMBER) { + size_t consumed = 0; + css_fixed val = 0; + + val = css__number_from_lwc_string(token->idata, + true, &consumed); + if (consumed != lwc_string_length(token->idata)) + return CSS_INVALID; + + value->nth.a = 0; + value->nth.b = FIXTOINT(val); + } else { + /* [ DIMENSION | IDENT ] ws [ [ CHAR ws ]? NUMBER ws ]? + * + * (e.g. DIMENSION: 2n-1, 2n- 1, 2n -1, 2n - 1) + * (e.g. IDENT: n, -n-1, -n- 1, -n -1, -n - 1) + */ + size_t consumed = 0, len; + const char *data; + css_fixed a = 0, b = 0; + int sign = 1; + bool had_sign = false, had_b = false; + + len = lwc_string_length(token->idata); + data = lwc_string_data(token->idata); + + /* Compute a */ + if (token->type == CSS_TOKEN_IDENT) { + if (len < 2) { + if (data[0] != 'n' && data[0] != 'N') + return CSS_INVALID; + + /* n */ + a = INTTOFIX(1); + + data += 1; + len -= 1; + } else { + if (data[0] != '-' || + (data[1] != 'n' && data[1] != 'N')) + return CSS_INVALID; + + /* -n */ + a = INTTOFIX(-1); + + data += 2; + len -= 2; + } + + if (len > 0) { + if (data[0] != '-') + return CSS_INVALID; + + /* -n- */ + sign = -1; + had_sign = true; + + if (len > 1) { + /* Reject additional sign */ + if (data[1] == '-' || data[1] == '+') + return CSS_INVALID; + + /* -n-b */ + b = css__number_from_string( + (const uint8_t *) data + 1, + len - 1, + true, + &consumed); + if (consumed != len - 1) + return CSS_INVALID; + + had_b = true; + } + } + } else { + /* 2n */ + a = css__number_from_lwc_string(token->idata, + true, &consumed); + if (consumed == 0 || (data[consumed] != 'n' && + data[consumed] != 'N')) + return CSS_INVALID; + + if (len - (++consumed) > 0) { + if (data[consumed] != '-') + return CSS_INVALID; + + /* 2n- */ + sign = -1; + had_sign = true; + + if (len - (++consumed) > 0) { + /* Reject additional sign */ + if (data[consumed] == '-' || + data[consumed] == '+') + return CSS_INVALID; + + /* 2n-b */ + size_t bstart = consumed; + + b = css__number_from_string( + (const uint8_t *) data + bstart, + len - bstart, + true, + &consumed); + if (consumed != len - bstart) + return CSS_INVALID; + + had_b = true; + } + } + } + + if (had_b == false) { + consumeWhitespace(vector, ctx); + + /* Look for optional b : [ [ CHAR ws ]? NUMBER ws ]? */ + token = parserutils_vector_peek(vector, *ctx); + + if (had_sign == false && token != NULL && + (tokenIsChar(token, '-') || + tokenIsChar(token, '+'))) { + parserutils_vector_iterate(vector, ctx); + + had_sign = true; + + if (tokenIsChar(token, '-')) + sign = -1; + + consumeWhitespace(vector, ctx); + + token = parserutils_vector_peek(vector, *ctx); + } + + /* Expect NUMBER */ + if (token != NULL && token->type == CSS_TOKEN_NUMBER) { + parserutils_vector_iterate(vector, ctx); + + /* If we've already seen a sign, ensure one + * does not occur at the start of this token + */ + if (had_sign && lwc_string_length( + token->idata) > 0) { + data = lwc_string_data(token->idata); + + if (data[0] == '-' || data[0] == '+') + return CSS_INVALID; + } + + b = css__number_from_lwc_string(token->idata, + true, &consumed); + if (consumed != lwc_string_length(token->idata)) + return CSS_INVALID; + } + } + + value->nth.a = FIXTOINT(a); + value->nth.b = FIXTOINT(b) * sign; + } + + consumeWhitespace(vector, ctx); + + return CSS_OK; } css_error parsePseudo(css_language *c, const parserutils_vector *vector, - int *ctx, css_selector_detail *specific) + int *ctx, bool in_not, css_selector_detail *specific) { - const css_token *token, *name, *value = NULL; - bool match = false; - css_selector_type type; + static const struct + { + int index; + css_selector_type type; + } pseudo_lut[] = { + { FIRST_CHILD, CSS_SELECTOR_PSEUDO_CLASS }, + { LINK, CSS_SELECTOR_PSEUDO_CLASS }, + { VISITED, CSS_SELECTOR_PSEUDO_CLASS }, + { HOVER, CSS_SELECTOR_PSEUDO_CLASS }, + { ACTIVE, CSS_SELECTOR_PSEUDO_CLASS }, + { FOCUS, CSS_SELECTOR_PSEUDO_CLASS }, + { LANG, CSS_SELECTOR_PSEUDO_CLASS }, + { LEFT, CSS_SELECTOR_PSEUDO_CLASS }, + { RIGHT, CSS_SELECTOR_PSEUDO_CLASS }, + { FIRST, CSS_SELECTOR_PSEUDO_CLASS }, + { ROOT, CSS_SELECTOR_PSEUDO_CLASS }, + { NTH_CHILD, CSS_SELECTOR_PSEUDO_CLASS }, + { NTH_LAST_CHILD, CSS_SELECTOR_PSEUDO_CLASS }, + { NTH_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS }, + { NTH_LAST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS }, + { LAST_CHILD, CSS_SELECTOR_PSEUDO_CLASS }, + { FIRST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS }, + { LAST_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS }, + { ONLY_CHILD, CSS_SELECTOR_PSEUDO_CLASS }, + { ONLY_OF_TYPE, CSS_SELECTOR_PSEUDO_CLASS }, + { EMPTY, CSS_SELECTOR_PSEUDO_CLASS }, + { TARGET, CSS_SELECTOR_PSEUDO_CLASS }, + { ENABLED, CSS_SELECTOR_PSEUDO_CLASS }, + { DISABLED, CSS_SELECTOR_PSEUDO_CLASS }, + { CHECKED, CSS_SELECTOR_PSEUDO_CLASS }, + { NOT, CSS_SELECTOR_PSEUDO_CLASS }, + + { FIRST_LINE, CSS_SELECTOR_PSEUDO_ELEMENT }, + { FIRST_LETTER, CSS_SELECTOR_PSEUDO_ELEMENT }, + { BEFORE, CSS_SELECTOR_PSEUDO_ELEMENT }, + { AFTER, CSS_SELECTOR_PSEUDO_ELEMENT } + }; + css_selector_detail_value detail_value; + css_selector_detail_value_type value_type = + CSS_SELECTOR_DETAIL_VALUE_STRING; + lwc_string *name; + const css_token *token; + bool match = false, require_element = false, negate = false; + uint32_t lut_idx; + css_selector_type type = CSS_SELECTOR_PSEUDO_CLASS;/* GCC's braindead */ + css_error error; - /* pseudo -> ':' [ IDENT | FUNCTION ws IDENT? ws ')' ] */ + /* pseudo -> ':' ':'? [ IDENT | FUNCTION ws any1 ws ')' ] */ + + detail_value.string = NULL; token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ':') == false) return CSS_INVALID; + /* Optional second colon before pseudo element names */ token = parserutils_vector_iterate(vector, ctx); + if (token != NULL && tokenIsChar(token, ':')) { + /* If present, we require a pseudo element */ + require_element = true; + + /* Consume subsequent token */ + token = parserutils_vector_iterate(vector, ctx); + } + + /* Expect IDENT or FUNCTION */ if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_FUNCTION)) return CSS_INVALID; - name = token; + name = token->idata; + + /* Search lut for selector type */ + for (lut_idx = 0; lut_idx < N_ELEMENTS(pseudo_lut); lut_idx++) { + if ((lwc_string_caseless_isequal(name, + c->strings[pseudo_lut[lut_idx].index], + &match) == lwc_error_ok) && match) { + type = pseudo_lut[lut_idx].type; + break; + } + } + + /* Not found: invalid */ + if (lut_idx == N_ELEMENTS(pseudo_lut)) + return CSS_INVALID; + + /* Required a pseudo element, but didn't find one: invalid */ + if (require_element && type != CSS_SELECTOR_PSEUDO_ELEMENT) + return CSS_INVALID; + + /* :not() and pseudo elements are not permitted in :not() */ + if (in_not && (type == CSS_SELECTOR_PSEUDO_ELEMENT || + pseudo_lut[lut_idx].index == NOT)) + return CSS_INVALID; if (token->type == CSS_TOKEN_FUNCTION) { + int fun_type = pseudo_lut[lut_idx].index; + consumeWhitespace(vector, ctx); - token = parserutils_vector_iterate(vector, ctx); + if (fun_type == LANG) { + /* IDENT */ + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || token->type != CSS_TOKEN_IDENT) + return CSS_INVALID; - if (token != NULL && token->type == CSS_TOKEN_IDENT) { - value = token; + detail_value.string = token->idata; + value_type = CSS_SELECTOR_DETAIL_VALUE_STRING; consumeWhitespace(vector, ctx); + } else if (fun_type == NTH_CHILD || + fun_type == NTH_LAST_CHILD || + fun_type == NTH_OF_TYPE || + fun_type == NTH_LAST_OF_TYPE) { + /* an + b */ + error = parseNth(c, vector, ctx, &detail_value); + if (error != CSS_OK) + return error; - token = parserutils_vector_iterate(vector, ctx); + value_type = CSS_SELECTOR_DETAIL_VALUE_NTH; + } else if (fun_type == NOT) { + /* element_name | specific */ + token = parserutils_vector_peek(vector, *ctx); + if (token == NULL) + return CSS_INVALID; + + if (token->type == CSS_TOKEN_IDENT || + tokenIsChar(token, '*')) { + /* Have element name */ + name = token->idata; + + type = CSS_SELECTOR_ELEMENT; + + detail_value.string = NULL; + value_type = CSS_SELECTOR_DETAIL_VALUE_STRING; + + parserutils_vector_iterate(vector, ctx); + } else { + /* specific */ + css_selector_detail det; + + error = parseSpecific(c, vector, ctx, true, + &det); + if (error != CSS_OK) + return error; + + name = det.name; + type = det.type; + detail_value = det.value; + value_type = det.value_type; + } + + negate = true; + + consumeWhitespace(vector, ctx); } + token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ')') == false) return CSS_INVALID; } - if ((lwc_string_caseless_isequal( - name->idata, c->strings[FIRST_CHILD], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[LINK], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[VISITED], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[HOVER], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[ACTIVE], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[FOCUS], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[LANG], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[LEFT], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[RIGHT], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[FIRST], - &match) == lwc_error_ok && match)) - type = CSS_SELECTOR_PSEUDO_CLASS; - else if ((lwc_string_caseless_isequal( - name->idata, c->strings[FIRST_LINE], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[FIRST_LETTER], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[BEFORE], - &match) == lwc_error_ok && match) || - (lwc_string_caseless_isequal( - name->idata, c->strings[AFTER], - &match) == lwc_error_ok && match)) - type = CSS_SELECTOR_PSEUDO_ELEMENT; - else - return CSS_INVALID; - return css__stylesheet_selector_detail_init(c->sheet, - type, name->idata, value != NULL ? value->idata : NULL, - specific); + type, name, detail_value, value_type, negate, specific); } -css_error parseSpecific(css_language *c, +css_error parseSpecific(css_language *c, const parserutils_vector *vector, int *ctx, - css_selector **parent) + bool in_not, css_selector_detail *specific) { css_error error; const css_token *token; - css_selector_detail specific; /* specific -> [ HASH | class | attrib | pseudo ] */ @@ -986,28 +1289,48 @@ css_error parseSpecific(css_language *c, return CSS_INVALID; if (token->type == CSS_TOKEN_HASH) { + css_selector_detail_value detail_value; + + detail_value.string = NULL; + error = css__stylesheet_selector_detail_init(c->sheet, - CSS_SELECTOR_ID, token->idata, NULL, &specific); + CSS_SELECTOR_ID, token->idata, detail_value, + CSS_SELECTOR_DETAIL_VALUE_STRING, false, + specific); if (error != CSS_OK) return error; parserutils_vector_iterate(vector, ctx); } else if (tokenIsChar(token, '.')) { - error = parseClass(c, vector, ctx, &specific); + error = parseClass(c, vector, ctx, specific); if (error != CSS_OK) return error; } else if (tokenIsChar(token, '[')) { - error = parseAttrib(c, vector, ctx, &specific); + error = parseAttrib(c, vector, ctx, specific); if (error != CSS_OK) return error; } else if (tokenIsChar(token, ':')) { - error = parsePseudo(c, vector, ctx, &specific); + error = parsePseudo(c, vector, ctx, in_not, specific); if (error != CSS_OK) return error; } else { return CSS_INVALID; } + return CSS_OK; +} + +css_error parseAppendSpecific(css_language *c, + const parserutils_vector *vector, int *ctx, + css_selector **parent) +{ + css_error error; + css_selector_detail specific; + + error = parseSpecific(c, vector, ctx, false, &specific); + if (error != CSS_OK) + return error; + return css__stylesheet_selector_append_specific(c->sheet, parent, &specific); } @@ -1024,8 +1347,9 @@ css_error parseSelectorSpecifics(css_language *c, token->type != CSS_TOKEN_S && tokenIsChar(token, '+') == false && tokenIsChar(token, '>') == false && + tokenIsChar(token, '~') == false && tokenIsChar(token, ',') == false) { - error = parseSpecific(c, vector, ctx, parent); + error = parseAppendSpecific(c, vector, ctx, parent); if (error != CSS_OK) return error; } @@ -1066,7 +1390,7 @@ css_error parseSimpleSelector(css_language *c, return error; /* Ensure we have at least one specific selector */ - error = parseSpecific(c, vector, ctx, &selector); + error = parseAppendSpecific(c, vector, ctx, &selector); if (error != CSS_OK) { css__stylesheet_selector_destroy(c->sheet, selector); return error; @@ -1090,7 +1414,7 @@ css_error parseCombinator(css_language *c, const parserutils_vector *vector, const css_token *token; css_combinator comb = CSS_COMBINATOR_NONE; - /* combinator -> ws '+' ws | ws '>' ws | ws1 */ + /* combinator -> ws '+' ws | ws '>' ws | ws '~' ws | ws1 */ UNUSED(c); @@ -1099,6 +1423,8 @@ css_error parseCombinator(css_language *c, const parserutils_vector *vector, comb = CSS_COMBINATOR_SIBLING; else if (tokenIsChar(token, '>')) comb = CSS_COMBINATOR_PARENT; + else if (tokenIsChar(token, '~')) + comb = CSS_COMBINATOR_GENERIC_SIBLING; else if (token->type == CSS_TOKEN_S) comb = CSS_COMBINATOR_ANCESTOR; else @@ -1106,7 +1432,7 @@ css_error parseCombinator(css_language *c, const parserutils_vector *vector, parserutils_vector_iterate(vector, ctx); - /* If we've seen a '+' or '>', we're done. */ + /* If we've seen a '+', '>', or '~', we're done. */ if (comb != CSS_COMBINATOR_ANCESTOR) break; } diff --git a/src/parse/propstrings.c b/src/parse/propstrings.c index 487ad98..37ced63 100644 --- a/src/parse/propstrings.c +++ b/src/parse/propstrings.c @@ -36,6 +36,22 @@ const stringmap_entry stringmap[LAST_KNOWN] = { { "focus", SLEN("focus") }, { "lang", SLEN("lang") }, { "first", SLEN("first") }, + { "root", SLEN("root") }, + { "nth-child", SLEN("nth-child") }, + { "nth-last-child", SLEN("nth-last-child") }, + { "nth-of-type", SLEN("nth-of-type") }, + { "nth-last-of-type", SLEN("nth-last-of-type") }, + { "last-child", SLEN("last-child") }, + { "first-of-type", SLEN("first-of-type") }, + { "last-of-type", SLEN("last-of-type") }, + { "only-child", SLEN("only-child") }, + { "only-of-type", SLEN("only-of-type") }, + { "empty", SLEN("empty") }, + { "target", SLEN("target") }, + { "enabled", SLEN("enabled") }, + { "disabled", SLEN("disabled") }, + { "checked", SLEN("checked") }, + { "not", SLEN("not") }, { "first-line", SLEN("first-line") }, { "first-letter", SLEN("first-letter") }, @@ -338,6 +354,8 @@ const stringmap_entry stringmap[LAST_KNOWN] = { { "-libcss-center", SLEN("-libcss-center") }, { "-libcss-right", SLEN("-libcss-right") }, { "currentColor", SLEN("currentColor") }, + { "odd", SLEN("odd") }, + { "even", SLEN("even") }, { "aliceblue", SLEN("aliceblue") }, { "antiquewhite", SLEN("antiquewhite") }, diff --git a/src/parse/propstrings.h b/src/parse/propstrings.h index f81fe30..fc91806 100644 --- a/src/parse/propstrings.h +++ b/src/parse/propstrings.h @@ -24,6 +24,9 @@ enum { /* Pseudo classes */ FIRST_CHILD, LINK, VISITED, HOVER, ACTIVE, FOCUS, LANG, /* LEFT, RIGHT, -- already in properties */ FIRST, + ROOT, NTH_CHILD, NTH_LAST_CHILD, NTH_OF_TYPE, NTH_LAST_OF_TYPE, + LAST_CHILD, FIRST_OF_TYPE, LAST_OF_TYPE, ONLY_CHILD, + ONLY_OF_TYPE, EMPTY, TARGET, ENABLED, DISABLED, CHECKED, NOT, /* Pseudo elements */ FIRST_LINE, FIRST_LETTER, BEFORE, AFTER, @@ -85,7 +88,7 @@ enum { W_RESIZE, LIBCSS_TEXT, WAIT, HELP, PROGRESS, SERIF, SANS_SERIF, CURSIVE, FANTASY, MONOSPACE, MALE, FEMALE, CHILD, MIX, UNDERLINE, OVERLINE, LINE_THROUGH, BLINK, RGB, RGBA, HSL, HSLA, LIBCSS_LEFT, LIBCSS_CENTER, - LIBCSS_RIGHT, CURRENTCOLOR, + LIBCSS_RIGHT, CURRENTCOLOR, ODD, EVEN, /* Named colours */ FIRST_COLOUR, diff --git a/src/select/select.c b/src/select/select.c index ee34df2..9f46075 100644 --- a/src/select/select.c +++ b/src/select/select.c @@ -455,6 +455,38 @@ cleanup: lwc_string_unref(state.active); if (state.focus != NULL) lwc_string_unref(state.focus); + if (state.nth_child != NULL) + lwc_string_unref(state.nth_child); + if (state.nth_last_child != NULL) + lwc_string_unref(state.nth_last_child); + if (state.nth_of_type != NULL) + lwc_string_unref(state.nth_of_type); + if (state.nth_last_of_type != NULL) + lwc_string_unref(state.nth_last_of_type); + if (state.last_child != NULL) + lwc_string_unref(state.last_child); + if (state.first_of_type != NULL) + lwc_string_unref(state.first_of_type); + if (state.last_of_type != NULL) + lwc_string_unref(state.last_of_type); + if (state.only_child != NULL) + lwc_string_unref(state.only_child); + if (state.only_of_type != NULL) + lwc_string_unref(state.only_of_type); + if (state.root != NULL) + lwc_string_unref(state.root); + if (state.empty != NULL) + lwc_string_unref(state.empty); + if (state.target != NULL) + lwc_string_unref(state.target); + if (state.lang != NULL) + lwc_string_unref(state.lang); + if (state.enabled != NULL) + lwc_string_unref(state.enabled); + if (state.disabled != NULL) + lwc_string_unref(state.disabled); + if (state.checked != NULL) + lwc_string_unref(state.checked); if (state.first_line != NULL) lwc_string_unref(state.first_line); if (state.first_letter != NULL) @@ -696,6 +728,102 @@ css_error intern_strings_for_sheet(css_select_ctx *ctx, if (error != lwc_error_ok) return css_error_from_lwc_error(error); + error = lwc_intern_string( + "nth-child", SLEN("nth-child"), + &state->nth_child); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "nth-last-child", SLEN("nth-last-child"), + &state->nth_last_child); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "nth-of-type", SLEN("nth-of-type"), + &state->nth_of_type); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "nth-last-of-type", SLEN("nth-last-of-type"), + &state->nth_last_of_type); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "last-child", SLEN("last-child"), + &state->last_child); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "first-of-type", SLEN("first-of-type"), + &state->first_of_type); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "last-of-type", SLEN("last-of-type"), + &state->last_of_type); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "only-child", SLEN("only-child"), + &state->only_child); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "only-of-type", SLEN("only-of-type"), + &state->only_of_type); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "root", SLEN("root"), + &state->root); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "empty", SLEN("empty"), + &state->empty); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "target", SLEN("target"), + &state->target); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "lang", SLEN("lang"), + &state->lang); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "enabled", SLEN("enabled"), + &state->enabled); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "disabled", SLEN("disabled"), + &state->disabled); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + + error = lwc_intern_string( + "checked", SLEN("checked"), + &state->checked); + if (error != lwc_error_ok) + return css_error_from_lwc_error(error); + /* Pseudo elements */ error = lwc_intern_string( "first-line", SLEN("first-line"), @@ -1075,6 +1203,11 @@ css_error match_named_combinator(css_select_ctx *ctx, css_combinator type, if (error != CSS_OK) return error; break; + case CSS_COMBINATOR_GENERIC_SIBLING: + error = state->handler->named_generic_sibling_node( + state->pw, n, selector->data.name, &n); + if (error != CSS_OK) + return error; case CSS_COMBINATOR_NONE: break; } @@ -1124,6 +1257,7 @@ css_error match_universal_combinator(css_select_ctx *ctx, css_combinator type, return error; break; case CSS_COMBINATOR_SIBLING: + case CSS_COMBINATOR_GENERIC_SIBLING: error = state->handler->sibling_node(state->pw, n, &n); if (error != CSS_OK) return error; @@ -1196,6 +1330,23 @@ css_error match_details(css_select_ctx *ctx, void *node, return CSS_OK; } +static inline bool match_nth(int32_t a, int32_t b, int32_t count) +{ + if (a == 0) { + return count == b; + } else { + const int32_t delta = count - b; + + /* (count - b) / a is positive or (count - b) is 0 */ + if (((delta > 0) == (a > 0)) || delta == 0) { + /* (count - b) / a is integer */ + return (delta % a == 0); + } + + return false; + } +} + css_error match_detail(css_select_ctx *ctx, void *node, const css_selector_detail *detail, css_select_state *state, bool *match, css_pseudo_element *pseudo_element) @@ -1224,7 +1375,108 @@ css_error match_detail(css_select_ctx *ctx, void *node, break; case CSS_SELECTOR_PSEUDO_CLASS: if (detail->name == state->first_child) { - error = state->handler->node_is_first_child(state->pw, + int32_t num_before = 0; + + error = state->handler->node_count_siblings(state->pw, + node, false, false, &num_before); + if (error == CSS_OK) + *match = (num_before == 0); + } else if (detail->name == state->nth_child) { + int32_t num_before = 0; + + error = state->handler->node_count_siblings(state->pw, + node, false, false, &num_before); + if (error == CSS_OK) { + int32_t a = detail->value.nth.a; + int32_t b = detail->value.nth.b; + + *match = match_nth(a, b, num_before + 1); + } + } else if (detail->name == state->nth_last_child) { + int32_t num_after = 0; + + error = state->handler->node_count_siblings(state->pw, + node, false, true, &num_after); + if (error == CSS_OK) { + int32_t a = detail->value.nth.a; + int32_t b = detail->value.nth.b; + + *match = match_nth(a, b, num_after + 1); + } + } else if (detail->name == state->nth_of_type) { + int32_t num_before = 0; + + error = state->handler->node_count_siblings(state->pw, + node, true, false, &num_before); + if (error == CSS_OK) { + int32_t a = detail->value.nth.a; + int32_t b = detail->value.nth.b; + + *match = match_nth(a, b, num_before + 1); + } + } else if (detail->name == state->nth_last_of_type) { + int32_t num_after = 0; + + error = state->handler->node_count_siblings(state->pw, + node, true, true, &num_after); + if (error == CSS_OK) { + int32_t a = detail->value.nth.a; + int32_t b = detail->value.nth.b; + + *match = match_nth(a, b, num_after + 1); + } + } else if (detail->name == state->last_child) { + int32_t num_after = 0; + + error = state->handler->node_count_siblings(state->pw, + node, false, true, &num_after); + if (error == CSS_OK) + *match = (num_after == 0); + } else if (detail->name == state->first_of_type) { + int32_t num_before = 0; + + error = state->handler->node_count_siblings(state->pw, + node, true, false, &num_before); + if (error == CSS_OK) + *match = (num_before == 0); + } else if (detail->name == state->last_of_type) { + int32_t num_after = 0; + + error = state->handler->node_count_siblings(state->pw, + node, true, true, &num_after); + if (error == CSS_OK) + *match = (num_after == 0); + } else if (detail->name == state->only_child) { + int32_t num_before = 0, num_after = 0; + + error = state->handler->node_count_siblings(state->pw, + node, false, false, &num_before); + if (error == CSS_OK) { + error = state->handler->node_count_siblings( + state->pw, node, false, true, + &num_after); + if (error == CSS_OK) + *match = (num_before == 0) && + (num_after == 0); + } + } else if (detail->name == state->only_of_type) { + int32_t num_before = 0, num_after = 0; + + error = state->handler->node_count_siblings(state->pw, + node, true, false, &num_before); + if (error == CSS_OK) { + error = state->handler->node_count_siblings( + state->pw, node, true, true, + &num_after); + if (error == CSS_OK) + *match = (num_before == 0) && + (num_after == 0); + } + } else if (detail->name == state->root) { + error = state->handler->node_is_root(state->pw, + node, match); + } else if (detail->name == state->empty) { + error = state->handler->node_is_empty(state->pw, node, match); } else if (detail->name == state->link) { error = state->handler->node_is_link(state->pw, @@ -1241,6 +1493,21 @@ css_error match_detail(css_select_ctx *ctx, void *node, } else if (detail->name == state->focus) { error = state->handler->node_is_focus(state->pw, node, match); + } else if (detail->name == state->target) { + error = state->handler->node_is_target(state->pw, + node, match); + } else if (detail->name == state->lang) { + error = state->handler->node_is_lang(state->pw, + node, detail->value.string, match); + } else if (detail->name == state->enabled) { + error = state->handler->node_is_enabled(state->pw, + node, match); + } else if (detail->name == state->disabled) { + error = state->handler->node_is_disabled(state->pw, + node, match); + } else if (detail->name == state->checked) { + error = state->handler->node_is_checked(state->pw, + node, match); } else *match = false; break; @@ -1264,18 +1531,40 @@ css_error match_detail(css_select_ctx *ctx, void *node, break; case CSS_SELECTOR_ATTRIBUTE_EQUAL: error = state->handler->node_has_attribute_equal(state->pw, - node, detail->name, detail->value, match); + node, detail->name, detail->value.string, + match); break; case CSS_SELECTOR_ATTRIBUTE_DASHMATCH: error = state->handler->node_has_attribute_dashmatch(state->pw, - node, detail->name, detail->value, match); + node, detail->name, detail->value.string, + match); break; case CSS_SELECTOR_ATTRIBUTE_INCLUDES: error = state->handler->node_has_attribute_includes(state->pw, - node, detail->name, detail->value, match); + node, detail->name, detail->value.string, + match); + break; + case CSS_SELECTOR_ATTRIBUTE_PREFIX: + error = state->handler->node_has_attribute_prefix(state->pw, + node, detail->name, detail->value.string, + match); + break; + case CSS_SELECTOR_ATTRIBUTE_SUFFIX: + error = state->handler->node_has_attribute_suffix(state->pw, + node, detail->name, detail->value.string, + match); + break; + case CSS_SELECTOR_ATTRIBUTE_SUBSTRING: + error = state->handler->node_has_attribute_substring(state->pw, + node, detail->name, detail->value.string, + match); break; } + /* Invert match, if the detail requests it */ + if (error == CSS_OK && detail->negate != 0) + *match = !*match; + return error; } diff --git a/src/select/select.h b/src/select/select.h index c0ba7cf..9c956de 100644 --- a/src/select/select.h +++ b/src/select/select.h @@ -50,6 +50,22 @@ typedef struct css_select_state { lwc_string *hover; lwc_string *active; lwc_string *focus; + lwc_string *nth_child; + lwc_string *nth_last_child; + lwc_string *nth_of_type; + lwc_string *nth_last_of_type; + lwc_string *last_child; + lwc_string *first_of_type; + lwc_string *last_of_type; + lwc_string *only_child; + lwc_string *only_of_type; + lwc_string *root; + lwc_string *empty; + lwc_string *target; + lwc_string *lang; + lwc_string *enabled; + lwc_string *disabled; + lwc_string *checked; lwc_string *first_line; lwc_string *first_letter; lwc_string *before; diff --git a/src/stylesheet.c b/src/stylesheet.c index b983190..42b5820 100644 --- a/src/stylesheet.c +++ b/src/stylesheet.c @@ -800,7 +800,8 @@ css_error css__stylesheet_selector_create(css_stylesheet *sheet, sel->data.type = CSS_SELECTOR_ELEMENT; sel->data.name = lwc_string_ref(name); - sel->data.value = NULL; + sel->data.value.string = NULL; + sel->data.value_type = CSS_SELECTOR_DETAIL_VALUE_STRING; if (sheet->inline_style) { sel->specificity = CSS_SPECIFICITY_A; @@ -846,8 +847,10 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet, for (detail = &c->data; detail;) { lwc_string_unref(detail->name); - if (detail->value != NULL) { - lwc_string_unref(detail->value); + if (detail->value_type == + CSS_SELECTOR_DETAIL_VALUE_STRING && + detail->value.string != NULL) { + lwc_string_unref(detail->value.string); } if (detail->next) @@ -862,8 +865,9 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet, for (detail = &selector->data; detail;) { lwc_string_unref(detail->name); - if (detail->value != NULL) { - lwc_string_unref(detail->value); + if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING && + detail->value.string != NULL) { + lwc_string_unref(detail->value.string); } if (detail->next) @@ -882,18 +886,21 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet, /** * Initialise a selector detail * - * \param sheet The stylesheet context - * \param type The type of selector to create - * \param name Name of selector - * \param value Value of selector, or NULL - * \param detail Pointer to detail object to initialise + * \param sheet The stylesheet context + * \param type The type of selector to create + * \param name Name of selector + * \param value Value of selector + * \param value_type Type of \a value + * \param negate Whether the detail match should be negated + * \param detail Pointer to detail object to initialise * \return CSS_OK on success, * CSS_BADPARM on bad parameters */ css_error css__stylesheet_selector_detail_init(css_stylesheet *sheet, css_selector_type type, lwc_string *name, - lwc_string *value, - css_selector_detail *detail) + css_selector_detail_value value, + css_selector_detail_value_type value_type, + bool negate, css_selector_detail *detail) { if (sheet == NULL || name == NULL || detail == NULL) return CSS_BADPARM; @@ -903,6 +910,8 @@ css_error css__stylesheet_selector_detail_init(css_stylesheet *sheet, detail->type = type; detail->name = name; detail->value = value; + detail->value_type = value_type; + detail->negate = negate; return CSS_OK; } @@ -948,8 +957,9 @@ css_error css__stylesheet_selector_append_specific(css_stylesheet *sheet, /* Ref the strings */ lwc_string_ref(detail->name); - if (detail->value != NULL) - lwc_string_ref(detail->value); + if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING && + detail->value.string != NULL) + lwc_string_ref(detail->value.string); (*parent) = temp; diff --git a/src/stylesheet.h b/src/stylesheet.h index 6296f9a..5d4eeb9 100644 --- a/src/stylesheet.h +++ b/src/stylesheet.h @@ -41,24 +41,43 @@ typedef enum css_selector_type { CSS_SELECTOR_ATTRIBUTE, CSS_SELECTOR_ATTRIBUTE_EQUAL, CSS_SELECTOR_ATTRIBUTE_DASHMATCH, - CSS_SELECTOR_ATTRIBUTE_INCLUDES + CSS_SELECTOR_ATTRIBUTE_INCLUDES, + CSS_SELECTOR_ATTRIBUTE_PREFIX, + CSS_SELECTOR_ATTRIBUTE_SUFFIX, + CSS_SELECTOR_ATTRIBUTE_SUBSTRING } css_selector_type; typedef enum css_combinator { CSS_COMBINATOR_NONE, CSS_COMBINATOR_ANCESTOR, CSS_COMBINATOR_PARENT, - CSS_COMBINATOR_SIBLING + CSS_COMBINATOR_SIBLING, + CSS_COMBINATOR_GENERIC_SIBLING } css_combinator; +typedef enum css_selector_detail_value_type { + CSS_SELECTOR_DETAIL_VALUE_STRING, + CSS_SELECTOR_DETAIL_VALUE_NTH +} css_selector_detail_value_type; + +typedef union css_selector_detail_value { + lwc_string *string; /**< Interned string, or NULL */ + struct { + int32_t a; + int32_t b; + } nth; /**< Data for x = an + b */ +} css_selector_detail_value; + typedef struct css_selector_detail { - lwc_string *name; /**< Interned name */ - lwc_string *value; /**< Interned value, or NULL */ + lwc_string *name; /**< Interned name */ + css_selector_detail_value value; /**< Detail value */ - unsigned int type : 4, /**< Type of selector */ - comb : 2, /**< Type of combinator */ - next : 1; /**< Another selector detail + unsigned int type : 4, /**< Type of selector */ + comb : 3, /**< Type of combinator */ + next : 1, /**< Another selector detail * follows */ + value_type : 1, /**< Type of value field */ + negate : 1; /**< Detail match is inverted */ } css_selector_detail; struct css_selector { @@ -186,31 +205,36 @@ struct css_stylesheet { css_style *cached_style; /**< Cache for style parsing */ lwc_string **string_vector; /**< Bytecode string vector */ - uint32_t string_vector_l; /**< The string vector allocated length in entries */ - uint32_t string_vector_c; /**< The number of string vector entries used */ + uint32_t string_vector_l; /**< The string vector allocated + * length in entries */ + uint32_t string_vector_c; /**< The number of string + * vector entries used */ }; -css_error css__stylesheet_style_create(css_stylesheet *sheet, css_style **style); +css_error css__stylesheet_style_create(css_stylesheet *sheet, + css_style **style); css_error css__stylesheet_style_append(css_style *style, css_code_t code); -css_error css__stylesheet_style_vappend(css_style *style, uint32_t style_count, ...); +css_error css__stylesheet_style_vappend(css_style *style, uint32_t style_count, + ...); css_error css__stylesheet_style_destroy(css_style *style); css_error css__stylesheet_merge_style(css_style *target, css_style *style); /** Helper function to avoid distinct buildOPV call */ -static inline css_error css__stylesheet_style_appendOPV(css_style *style, opcode_t opcode, uint8_t flags, uint16_t value) +static inline css_error css__stylesheet_style_appendOPV(css_style *style, + opcode_t opcode, uint8_t flags, uint16_t value) { - return css__stylesheet_style_append(style, buildOPV(opcode, flags, value)); + return css__stylesheet_style_append(style, + buildOPV(opcode, flags, value)); } /** Helper function to set inherit flag */ -static inline css_error css_stylesheet_style_inherit(css_style *style, opcode_t opcode) +static inline css_error css_stylesheet_style_inherit(css_style *style, + opcode_t opcode) { - return css__stylesheet_style_append(style, buildOPV(opcode, FLAG_INHERIT, 0)); + return css__stylesheet_style_append(style, + buildOPV(opcode, FLAG_INHERIT, 0)); } - - - css_error css__stylesheet_selector_create(css_stylesheet *sheet, lwc_string *name, css_selector **selector); css_error css__stylesheet_selector_destroy(css_stylesheet *sheet, @@ -218,8 +242,9 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet, css_error css__stylesheet_selector_detail_init(css_stylesheet *sheet, css_selector_type type, lwc_string *name, - lwc_string *value, - css_selector_detail *detail); + css_selector_detail_value value, + css_selector_detail_value_type value_type, + bool negate, css_selector_detail *detail); css_error css__stylesheet_selector_append_specific(css_stylesheet *sheet, css_selector **parent, const css_selector_detail *specific); @@ -253,9 +278,11 @@ css_error css__stylesheet_add_rule(css_stylesheet *sheet, css_rule *rule, css_rule *parent); css_error css__stylesheet_remove_rule(css_stylesheet *sheet, css_rule *rule); -css_error css__stylesheet_string_get(css_stylesheet *sheet, uint32_t string_number, lwc_string **string); +css_error css__stylesheet_string_get(css_stylesheet *sheet, + uint32_t string_number, lwc_string **string); -css_error css__stylesheet_string_add(css_stylesheet *sheet, lwc_string *string, uint32_t *string_number); +css_error css__stylesheet_string_add(css_stylesheet *sheet, + lwc_string *string, uint32_t *string_number); #endif diff --git a/src/utils/utils.c b/src/utils/utils.c index 94b15e1..1745ea0 100644 --- a/src/utils/utils.c +++ b/src/utils/utils.c @@ -10,20 +10,29 @@ css_fixed css__number_from_lwc_string(lwc_string *string, bool int_only, size_t *consumed) { - size_t len; - const uint8_t *ptr; + if (string == NULL || lwc_string_length(string) == 0 || + consumed == NULL) + return 0; + + return css__number_from_string( + (uint8_t *)lwc_string_data(string), + lwc_string_length(string), + int_only, + consumed); +} + +css_fixed css__number_from_string(const uint8_t *data, size_t len, + bool int_only, size_t *consumed) +{ + const uint8_t *ptr = data; int sign = 1; int32_t intpart = 0; int32_t fracpart = 0; int32_t pwr = 1; - if (string == NULL || lwc_string_length(string) == 0 || - consumed == NULL) + if (data == NULL || len == 0 || consumed == NULL) return 0; - len = lwc_string_length(string); - ptr = (uint8_t *)lwc_string_data(string); - /* number = [+-]? ([0-9]+ | [0-9]* '.' [0-9]+) */ /* Extract sign, if any */ @@ -92,7 +101,7 @@ css_fixed css__number_from_lwc_string(lwc_string *string, } } - *consumed = (char *)ptr - lwc_string_data(string); + *consumed = ptr - data; if (sign > 0) { /* If the result is larger than we can represent, diff --git a/src/utils/utils.h b/src/utils/utils.h index 4859dea..1f7ed8c 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -36,6 +36,8 @@ css_fixed css__number_from_lwc_string(lwc_string *string, bool int_only, size_t *consumed); +css_fixed css__number_from_string(const uint8_t *data, size_t len, + bool int_only, size_t *consumed); static inline bool isDigit(uint8_t c) { diff --git a/test/data/parse/INDEX b/test/data/parse/INDEX index 6019062..ced33a3 100644 --- a/test/data/parse/INDEX +++ b/test/data/parse/INDEX @@ -7,4 +7,5 @@ selectors.dat Selectors atrules.dat @-rules colours.dat Colour values colours-hsl.dat HSL Colour values +nth.dat :nth-* expressions properties.dat Properties diff --git a/test/data/parse/nth.dat b/test/data/parse/nth.dat new file mode 100644 index 0000000..fa5744c --- /dev/null +++ b/test/data/parse/nth.dat @@ -0,0 +1,246 @@ +# Test data for nth-* expressions + +# Odd and Even + +#data +E:nth-child(odd) {} +#errors +#expected +| 1 E:nth-child(2n+1) +#reset + +#data +E:nth-child(even) {} +#errors +#expected +| 1 E:nth-child(2n+0) +#reset + +# Basic numbers + +#data +E:nth-child(1) {} +#errors +#expected +| 1 E:nth-child(0n+1) +#reset + +#data +E:nth-child(-1) {} +#errors +#expected +| 1 E:nth-child(0n+-1) +#reset + +# IDENT ws [ NUMBER ws ]? + +#data +E:nth-child(n) {} +#errors +#expected +| 1 E:nth-child(1n+0) +#reset + +#data +E:nth-child(-n) {} +#errors +#expected +| 1 E:nth-child(-1n+0) +#reset + +#data +E:nth-child(-n- 1) {} +#errors +#expected +| 1 E:nth-child(-1n+-1) +#reset + +#data +E:nth-child(-n-1) {} +#errors +#expected +| 1 E:nth-child(-1n+-1) +#reset + +# DIMENSION ws [ NUMBER ws ]? + +#data +E:nth-child(2n) {} +#errors +#expected +| 1 E:nth-child(2n+0) +#reset + +#data +E:nth-child(-2n) {} +#errors +#expected +| 1 E:nth-child(-2n+0) +#reset + +#data +E:nth-child(2n- 1) {} +#errors +#expected +| 1 E:nth-child(2n+-1) +#reset + +#data +E:nth-child(-2n- 1) {} +#errors +#expected +| 1 E:nth-child(-2n+-1) +#reset + +#data +E:nth-child(2n-1) {} +#errors +#expected +| 1 E:nth-child(2n+-1) +#reset + +#data +E:nth-child(-2n-1) {} +#errors +#expected +| 1 E:nth-child(-2n+-1) +#reset + +# IDENT ws CHAR ws NUMBER ws + +#data +E:nth-child(n - 1) {} +#errors +#expected +| 1 E:nth-child(1n+-1) +#reset + +#data +E:nth-child(n+1) {} +#errors +#expected +| 1 E:nth-child(1n+1) +#reset + +#data +E:nth-child(n + 1) {} +#errors +#expected +| 1 E:nth-child(1n+1) +#reset + +# DIMENSION ws CHAR ws NUMBER ws + +#data +E:nth-child(2n - 1) {} +#errors +#expected +| 1 E:nth-child(2n+-1) +#reset + +#data +E:nth-child(2n+1) {} +#errors +#expected +| 1 E:nth-child(2n+1) +#reset + +#data +E:nth-child(2n + 1) {} +#errors +#expected +| 1 E:nth-child(2n+1) +#reset + +# Illegal inputs + +#data +E:nth-child(n--1) {} +#errors +#expected +#reset + +#data +E:nth-child(n-+1) {} +#errors +#expected +#reset + +#data +E:nth-child(n- -1) {} +#errors +#expected +#reset + +#data +E:nth-child(n- +1) {} +#errors +#expected +#reset + +#data +E:nth-child(n + -1) {} +#errors +#expected +#reset + +#data +E:nth-child(n - +1) {} +#errors +#expected +#reset + +#data +E:nth-child(2n--1) {} +#errors +#expected +#reset + +#data +E:nth-child(2n-+1) {} +#errors +#expected +#reset + +#data +E:nth-child(2n- -1) {} +#errors +#expected +#reset + +#data +E:nth-child(2n- +1) {} +#errors +#expected +#reset + +#data +E:nth-child(2n + -1) {} +#errors +#expected +#reset + +#data +E:nth-child(2n - +1) {} +#errors +#expected +#reset + +#data +E:nth-child(3 n) {} +#errors +#expected +#reset + +#data +E:nth-child(+2 n) {} +#errors +#expected +#reset + +#data +E:nth-child(+ 2) {} +#errors +#expected +#reset + diff --git a/test/data/parse/selectors.dat b/test/data/parse/selectors.dat index e58d8ac..b77b18a 100644 --- a/test/data/parse/selectors.dat +++ b/test/data/parse/selectors.dat @@ -98,6 +98,111 @@ E:first {} | 1 E:first #reset +#data +E:root {} +#errors +#expected +| 1 E:root +#reset + +#data +E:nth-child(2n+1) {} +#errors +#expected +| 1 E:nth-child(2n+1) +#reset + +#data +E:nth-last-child(2n+1) {} +#errors +#expected +| 1 E:nth-last-child(2n+1) +#reset + +#data +E:nth-of-type(2n+1) {} +#errors +#expected +| 1 E:nth-of-type(2n+1) +#reset + +#data +E:nth-last-of-type(2n+1) {} +#errors +#expected +| 1 E:nth-last-of-type(2n+1) +#reset + +#data +E:last-child {} +#errors +#expected +| 1 E:last-child +#reset + +#data +E:first-of-type {} +#errors +#expected +| 1 E:first-of-type +#reset + +#data +E:last-of-type {} +#errors +#expected +| 1 E:last-of-type +#reset + +#data +E:only-child {} +#errors +#expected +| 1 E:only-child +#reset + +#data +E:only-of-type {} +#errors +#expected +| 1 E:only-of-type +#reset + +#data +E:empty {} +#errors +#expected +| 1 E:empty +#reset + +#data +E:target {} +#errors +#expected +| 1 E:target +#reset + +#data +E:enabled {} +#errors +#expected +| 1 E:enabled +#reset + +#data +E:disabled {} +#errors +#expected +| 1 E:disabled +#reset + +#data +E:checked {} +#errors +#expected +| 1 E:checked +#reset + #data E:first-line {} #errors @@ -126,6 +231,34 @@ E:after {} | 1 E:after #reset +#data +E::first-line {} +#errors +#expected +| 1 E:first-line +#reset + +#data +E::first-letter {} +#errors +#expected +| 1 E:first-letter +#reset + +#data +E::before {} +#errors +#expected +| 1 E:before +#reset + +#data +E::after {} +#errors +#expected +| 1 E:after +#reset + #data E + F {} #errors @@ -133,6 +266,13 @@ E + F {} | 1 E + F #reset +#data +E ~ F {} +#errors +#expected +| 1 E ~ F +#reset + #data E[foo] {} #errors @@ -161,6 +301,27 @@ E[lang|="en"] {} | 1 E[lang|="en"] #reset +#data +E[foo^="warning"] {} +#errors +#expected +| 1 E[foo^="warning"] +#reset + +#data +E[foo$="warning"] {} +#errors +#expected +| 1 E[foo$="warning"] +#reset + +#data +E[foo*="warning"] {} +#errors +#expected +| 1 E[foo*="warning"] +#reset + #data DIV.warning {} #errors @@ -368,3 +529,66 @@ E#myid,bar {} | 1 #myid, bar #reset +# Not pseudo class + +#data +E:not(bar) {} +#errors +#expected +| 1 E:not(bar) +#reset + +#data +E:not(*) {} +#errors +#expected +| 1 E:not(*) +#reset + +#data +E:not(#foo) {} +#errors +#expected +| 1 E:not(#foo) +#reset + +#data +E:not(.bar) {} +#errors +#expected +| 1 E:not(.bar) +#reset + +#data +E:not([bar]) {} +#errors +#expected +| 1 E:not([bar]) +#reset + +#data +E:not(:first-child) {} +#errors +#expected +| 1 E:not(:first-child) +#reset + +#data +E:not(:nth-child(2n+1)) {} +#errors +#expected +| 1 E:not(:nth-child(2n+1)) +#reset + +#data +E:not(:first-line) {} +#errors +#expected +#reset + +#data +E:not(:not(bar)) {} +#errors +#expected +#reset + diff --git a/test/dump.h b/test/dump.h index 602530a..85f65b8 100644 --- a/test/dump.h +++ b/test/dump.h @@ -178,6 +178,10 @@ void dump_selector_list(css_selector *list, char **ptr) memcpy(*ptr, " + ", 3); *ptr += 3; break; + case CSS_COMBINATOR_GENERIC_SIBLING: + memcpy(*ptr, " + ", 3); + *ptr += 3; + break; } dump_selector(list, ptr); @@ -200,6 +204,9 @@ void dump_selector(css_selector *selector, char **ptr) void dump_selector_detail(css_selector_detail *detail, char **ptr) { + if (detail->negate) + *ptr += sprintf(*ptr, ":not("); + switch (detail->type) { case CSS_SELECTOR_ELEMENT: if (lwc_string_length(detail->name) == 1 && @@ -226,12 +233,18 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) **ptr = ':'; *ptr += 1; dump_string(detail->name, ptr); - if (detail->value != NULL) { - **ptr = '('; - *ptr += 1; - dump_string(detail->value, ptr); - **ptr = ')'; - *ptr += 1; + if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING) { + if (detail->value.string != NULL) { + **ptr = '('; + *ptr += 1; + dump_string(detail->value.string, ptr); + **ptr = ')'; + *ptr += 1; + } + } else { + *ptr += sprintf(*ptr, "(%dn+%d)", + detail->value.nth.a, + detail->value.nth.b); } break; case CSS_SELECTOR_ATTRIBUTE: @@ -248,7 +261,7 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) (*ptr)[0] = '='; (*ptr)[1] = '"'; *ptr += 2; - dump_string(detail->value, ptr); + dump_string(detail->value.string, ptr); (*ptr)[0] = '"'; (*ptr)[1] = ']'; *ptr += 2; @@ -261,7 +274,7 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) (*ptr)[1] = '='; (*ptr)[2] = '"'; *ptr += 3; - dump_string(detail->value, ptr); + dump_string(detail->value.string, ptr); (*ptr)[0] = '"'; (*ptr)[1] = ']'; *ptr += 2; @@ -274,12 +287,54 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) (*ptr)[1] = '='; (*ptr)[2] = '"'; *ptr += 3; - dump_string(detail->value, ptr); + dump_string(detail->value.string, ptr); + (*ptr)[0] = '"'; + (*ptr)[1] = ']'; + *ptr += 2; + break; + case CSS_SELECTOR_ATTRIBUTE_PREFIX: + **ptr = '['; + *ptr += 1; + dump_string(detail->name, ptr); + (*ptr)[0] = '^'; + (*ptr)[1] = '='; + (*ptr)[2] = '"'; + *ptr += 3; + dump_string(detail->value.string, ptr); + (*ptr)[0] = '"'; + (*ptr)[1] = ']'; + *ptr += 2; + break; + case CSS_SELECTOR_ATTRIBUTE_SUFFIX: + **ptr = '['; + *ptr += 1; + dump_string(detail->name, ptr); + (*ptr)[0] = '$'; + (*ptr)[1] = '='; + (*ptr)[2] = '"'; + *ptr += 3; + dump_string(detail->value.string, ptr); + (*ptr)[0] = '"'; + (*ptr)[1] = ']'; + *ptr += 2; + break; + case CSS_SELECTOR_ATTRIBUTE_SUBSTRING: + **ptr = '['; + *ptr += 1; + dump_string(detail->name, ptr); + (*ptr)[0] = '*'; + (*ptr)[1] = '='; + (*ptr)[2] = '"'; + *ptr += 3; + dump_string(detail->value.string, ptr); (*ptr)[0] = '"'; (*ptr)[1] = ']'; *ptr += 2; break; } + + if (detail->negate) + *ptr += sprintf(*ptr, ")"); } /** diff --git a/test/parse-auto.c b/test/parse-auto.c index e268c3b..3c3e011 100644 --- a/test/parse-auto.c +++ b/test/parse-auto.c @@ -619,12 +619,15 @@ void dump_selector_list(css_selector *list, char **ptr) memcpy(*ptr, " + ", 3); *ptr += 3; break; + case CSS_COMBINATOR_GENERIC_SIBLING: + memcpy(*ptr, " ~ ", 3); + *ptr += 3; + break; } dump_selector(list, ptr); } - void dump_selector(css_selector *selector, char **ptr) { css_selector_detail *d = &selector->data; @@ -641,14 +644,17 @@ void dump_selector(css_selector *selector, char **ptr) void dump_selector_detail(css_selector_detail *detail, char **ptr) { + if (detail->negate) + *ptr += sprintf(*ptr, ":not("); + switch (detail->type) { case CSS_SELECTOR_ELEMENT: - if (lwc_string_length(detail->name) == 1 && - lwc_string_data(detail->name)[0] == '*' && + if (lwc_string_length(detail->name) == 1 && + lwc_string_data(detail->name)[0] == '*' && detail->next == 0) { dump_string(detail->name, ptr); } else if (lwc_string_length(detail->name) != 1 || - lwc_string_data(detail->name)[0] != '*') { + lwc_string_data(detail->name)[0] != '*') { dump_string(detail->name, ptr); } break; @@ -667,12 +673,18 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) **ptr = ':'; *ptr += 1; dump_string(detail->name, ptr); - if (detail->value != NULL) { - **ptr = '('; - *ptr += 1; - dump_string(detail->value, ptr); - **ptr = ')'; - *ptr += 1; + if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING) { + if (detail->value.string != NULL) { + **ptr = '('; + *ptr += 1; + dump_string(detail->value.string, ptr); + **ptr = ')'; + *ptr += 1; + } + } else { + *ptr += sprintf(*ptr, "(%dn+%d)", + detail->value.nth.a, + detail->value.nth.b); } break; case CSS_SELECTOR_ATTRIBUTE: @@ -689,7 +701,7 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) (*ptr)[0] = '='; (*ptr)[1] = '"'; *ptr += 2; - dump_string(detail->value, ptr); + dump_string(detail->value.string, ptr); (*ptr)[0] = '"'; (*ptr)[1] = ']'; *ptr += 2; @@ -702,7 +714,7 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) (*ptr)[1] = '='; (*ptr)[2] = '"'; *ptr += 3; - dump_string(detail->value, ptr); + dump_string(detail->value.string, ptr); (*ptr)[0] = '"'; (*ptr)[1] = ']'; *ptr += 2; @@ -715,12 +727,54 @@ void dump_selector_detail(css_selector_detail *detail, char **ptr) (*ptr)[1] = '='; (*ptr)[2] = '"'; *ptr += 3; - dump_string(detail->value, ptr); + dump_string(detail->value.string, ptr); + (*ptr)[0] = '"'; + (*ptr)[1] = ']'; + *ptr += 2; + break; + case CSS_SELECTOR_ATTRIBUTE_PREFIX: + **ptr = '['; + *ptr += 1; + dump_string(detail->name, ptr); + (*ptr)[0] = '^'; + (*ptr)[1] = '='; + (*ptr)[2] = '"'; + *ptr += 3; + dump_string(detail->value.string, ptr); + (*ptr)[0] = '"'; + (*ptr)[1] = ']'; + *ptr += 2; + break; + case CSS_SELECTOR_ATTRIBUTE_SUFFIX: + **ptr = '['; + *ptr += 1; + dump_string(detail->name, ptr); + (*ptr)[0] = '$'; + (*ptr)[1] = '='; + (*ptr)[2] = '"'; + *ptr += 3; + dump_string(detail->value.string, ptr); + (*ptr)[0] = '"'; + (*ptr)[1] = ']'; + *ptr += 2; + break; + case CSS_SELECTOR_ATTRIBUTE_SUBSTRING: + **ptr = '['; + *ptr += 1; + dump_string(detail->name, ptr); + (*ptr)[0] = '*'; + (*ptr)[1] = '='; + (*ptr)[2] = '"'; + *ptr += 3; + dump_string(detail->value.string, ptr); (*ptr)[0] = '"'; (*ptr)[1] = ']'; *ptr += 2; break; } + + if (detail->negate) + *ptr += sprintf(*ptr, ")"); } void dump_string(lwc_string *string, char **ptr) @@ -728,4 +782,3 @@ void dump_string(lwc_string *string, char **ptr) *ptr += sprintf(*ptr, "%.*s", (int) lwc_string_length(string), lwc_string_data(string)); } - diff --git a/test/select-auto.c b/test/select-auto.c index 12fdb5f..3f43898 100644 --- a/test/select-auto.c +++ b/test/select-auto.c @@ -90,6 +90,9 @@ static css_error named_parent_node(void *pw, void *node, static css_error named_sibling_node(void *pw, void *node, lwc_string *name, void **sibling); +static css_error named_generic_sibling_node(void *pw, void *node, + lwc_string *name, + void **sibling); static css_error parent_node(void *pw, void *node, void **parent); static css_error sibling_node(void *pw, void *node, void **sibling); static css_error node_has_name(void *pw, void *node, @@ -116,12 +119,31 @@ static css_error node_has_attribute_includes(void *pw, void *node, lwc_string *name, lwc_string *value, bool *match); -static css_error node_is_first_child(void *pw, void *node, bool *match); +static css_error node_has_attribute_prefix(void *pw, void *node, + lwc_string *name, + lwc_string *value, + bool *match); +static css_error node_has_attribute_suffix(void *pw, void *node, + lwc_string *name, + lwc_string *value, + bool *match); +static css_error node_has_attribute_substring(void *pw, void *node, + lwc_string *name, + lwc_string *value, + bool *match); +static css_error node_is_root(void *pw, void *node, bool *match); +static css_error node_count_siblings(void *pw, void *node, + bool same_name, bool after, int32_t *count); +static css_error node_is_empty(void *pw, void *node, bool *match); static css_error node_is_link(void *pw, void *node, bool *match); static css_error node_is_visited(void *pw, void *node, bool *match); static css_error node_is_hover(void *pw, void *node, bool *match); static css_error node_is_active(void *pw, void *node, bool *match); static css_error node_is_focus(void *pw, void *node, bool *match); +static css_error node_is_enabled(void *pw, void *node, bool *match); +static css_error node_is_disabled(void *pw, void *node, bool *match); +static css_error node_is_checked(void *pw, void *node, bool *match); +static css_error node_is_target(void *pw, void *node, bool *match); static css_error node_is_lang(void *pw, void *node, lwc_string *lang, bool *match); static css_error node_presentational_hint(void *pw, void *node, @@ -138,6 +160,7 @@ static css_select_handler select_handler = { named_ancestor_node, named_parent_node, named_sibling_node, + named_generic_sibling_node, parent_node, sibling_node, node_has_name, @@ -147,12 +170,21 @@ static css_select_handler select_handler = { node_has_attribute_equal, node_has_attribute_dashmatch, node_has_attribute_includes, - node_is_first_child, + node_has_attribute_prefix, + node_has_attribute_suffix, + node_has_attribute_substring, + node_is_root, + node_count_siblings, + node_is_empty, node_is_link, node_is_visited, node_is_hover, node_is_active, node_is_focus, + node_is_enabled, + node_is_disabled, + node_is_checked, + node_is_target, node_is_lang, node_presentational_hint, ua_default_for_property, @@ -882,6 +914,26 @@ css_error named_sibling_node(void *pw, void *n, return CSS_OK; } +css_error named_generic_sibling_node(void *pw, void *n, + lwc_string *name, + void **sibling) +{ + node *node = n; + UNUSED(pw); + + for (node = node->prev; node != NULL; node = node->prev) { + bool match; + assert(lwc_string_caseless_isequal( + name, node->name, &match) == lwc_error_ok); + if (match == true) + break; + } + + *sibling = (void *) node; + + return CSS_OK; +} + css_error parent_node(void *pw, void *n, void **parent) { node *node = n; @@ -1109,13 +1161,189 @@ css_error node_has_attribute_dashmatch(void *pw, void *n, return CSS_OK; } -css_error node_is_first_child(void *pw, void *n, bool *match) +css_error node_has_attribute_prefix(void *pw, void *n, + lwc_string *name, + lwc_string *value, + bool *match) +{ + node *node = n; + uint32_t i; + UNUSED(pw); + + *match = false; + + for (i = 0; i < node->n_attrs; i++) { + assert(lwc_string_caseless_isequal( + node->attrs[i].name, name, match) == + lwc_error_ok); + if (*match == true) + break; + } + + if (*match == true) { + size_t len = lwc_string_length(node->attrs[i].value); + const char *data = lwc_string_data(node->attrs[i].value); + + size_t vlen = lwc_string_length(value); + const char *vdata = lwc_string_data(value); + + if (len < vlen) + *match = false; + else + *match = (strncasecmp(data, vdata, vlen) == 0); + } + + return CSS_OK; +} + +css_error node_has_attribute_suffix(void *pw, void *n, + lwc_string *name, + lwc_string *value, + bool *match) +{ + node *node = n; + uint32_t i; + UNUSED(pw); + + *match = false; + + for (i = 0; i < node->n_attrs; i++) { + assert(lwc_string_caseless_isequal( + node->attrs[i].name, name, match) == + lwc_error_ok); + if (*match == true) + break; + } + + if (*match == true) { + size_t len = lwc_string_length(node->attrs[i].value); + const char *data = lwc_string_data(node->attrs[i].value); + + size_t vlen = lwc_string_length(value); + const char *vdata = lwc_string_data(value); + + size_t suffix_start = len - vlen; + + if (len < vlen) + *match = false; + else { + *match = (strncasecmp(data + suffix_start, + vdata, vlen) == 0); + } + } + + return CSS_OK; +} + +css_error node_has_attribute_substring(void *pw, void *n, + lwc_string *name, + lwc_string *value, + bool *match) +{ + node *node = n; + uint32_t i; + UNUSED(pw); + + *match = false; + + for (i = 0; i < node->n_attrs; i++) { + assert(lwc_string_caseless_isequal( + node->attrs[i].name, name, match) == + lwc_error_ok); + if (*match == true) + break; + } + + if (*match == true) { + size_t len = lwc_string_length(node->attrs[i].value); + const char *data = lwc_string_data(node->attrs[i].value); + + size_t vlen = lwc_string_length(value); + const char *vdata = lwc_string_data(value); + + const char *last_start = data + len - vlen; + + if (len < vlen) + *match = false; + else { + while (data <= last_start) { + if (strncasecmp(data, vdata, vlen) == 0) { + *match = true; + break; + } + + data++; + } + + if (data > last_start) + *match = false; + } + } + + return CSS_OK; +} + +css_error node_is_root(void *pw, void *n, bool *match) { node *node = n; + UNUSED(pw); + + *match = (node->parent == NULL); + + return CSS_OK; +} + +css_error node_count_siblings(void *pw, void *n, + bool same_name, bool after, int32_t *count) +{ + int32_t cnt = 0; + bool match = false; + node *node = n; + UNUSED(pw); + + if (after) { + while (node->next != NULL) { + if (same_name) { + assert(lwc_string_caseless_isequal( + node->name, node->next->name, &match) == + lwc_error_ok); + + if (match) + cnt++; + } else { + cnt++; + } + + node = node->next; + } + } else { + while (node->prev != NULL) { + if (same_name) { + assert(lwc_string_caseless_isequal( + node->name, node->prev->name, &match) == + lwc_error_ok); + + if (match) + cnt++; + } else { + cnt++; + } + + node = node->prev; + } + } + + *count = cnt; + + return CSS_OK; +} +css_error node_is_empty(void *pw, void *n, bool *match) +{ + node *node = n; UNUSED(pw); - *match = (node->parent != NULL && node->parent->children == node); + *match = (node->children == NULL); return CSS_OK; } @@ -1180,6 +1408,54 @@ css_error node_is_focus(void *pw, void *n, bool *match) return CSS_OK; } +css_error node_is_enabled(void *pw, void *n, bool *match) +{ + node *node = n; + + UNUSED(pw); + UNUSED(node); + + *match = false; + + return CSS_OK; +} + +css_error node_is_disabled(void *pw, void *n, bool *match) +{ + node *node = n; + + UNUSED(pw); + UNUSED(node); + + *match = false; + + return CSS_OK; +} + +css_error node_is_checked(void *pw, void *n, bool *match) +{ + node *node = n; + + UNUSED(pw); + UNUSED(node); + + *match = false; + + return CSS_OK; +} + +css_error node_is_target(void *pw, void *n, bool *match) +{ + node *node = n; + + UNUSED(pw); + UNUSED(node); + + *match = false; + + return CSS_OK; +} + css_error node_is_lang(void *pw, void *n, lwc_string *lang, bool *match) -- cgit v1.2.3