summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2011-01-31 00:18:15 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2011-01-31 00:18:15 +0000
commit9fa9b9d104c730ef6d84b19245b61ebc9554b432 (patch)
treefb4f28285283241da6490b482dc61aac731f48a8 /src
parent6ba000db056d7e9b70a7e154a003644046bf7e98 (diff)
downloadlibcss-9fa9b9d104c730ef6d84b19245b61ebc9554b432.tar.gz
libcss-9fa9b9d104c730ef6d84b19245b61ebc9554b432.tar.bz2
CSS3 Selectors
svn path=/trunk/libcss/; revision=11557
Diffstat (limited to 'src')
-rw-r--r--src/parse/language.c476
-rw-r--r--src/parse/propstrings.c18
-rw-r--r--src/parse/propstrings.h5
-rw-r--r--src/select/select.c297
-rw-r--r--src/select/select.h16
-rw-r--r--src/stylesheet.c38
-rw-r--r--src/stylesheet.h71
-rw-r--r--src/utils/utils.c25
-rw-r--r--src/utils/utils.h2
9 files changed, 824 insertions, 124 deletions
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,11 +63,17 @@ 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,
const parserutils_vector *vector, int *ctx,
@@ -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)
{