summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/parse/language.c334
-rw-r--r--src/parse/language.h17
-rw-r--r--src/parse/propstrings.c1
-rw-r--r--src/parse/propstrings.h2
-rw-r--r--src/select/hash.c31
-rw-r--r--src/select/hash.h2
-rw-r--r--src/select/select.c104
-rw-r--r--src/stylesheet.c41
-rw-r--r--src/stylesheet.h6
9 files changed, 417 insertions, 121 deletions
diff --git a/src/parse/language.c b/src/parse/language.c
index ab363bd..5e550b1 100644
--- a/src/parse/language.c
+++ b/src/parse/language.c
@@ -55,6 +55,10 @@ static css_error handleDeclaration(css_language *c,
static css_error parseMediaList(css_language *c,
const parserutils_vector *vector, int *ctx,
uint64_t *media);
+static css_error addNamespace(css_language *c,
+ lwc_string *prefix, lwc_string *uri);
+static css_error lookupNamespace(css_language *c,
+ lwc_string *prefix, lwc_string **uri);
/* Selector list parsing */
static css_error parseClass(css_language *c,
@@ -78,6 +82,9 @@ static css_error parseAppendSpecific(css_language *c,
static css_error parseSelectorSpecifics(css_language *c,
const parserutils_vector *vector, int *ctx,
css_selector **parent);
+static css_error parseTypeSelector(css_language *c,
+ const parserutils_vector *vector, int *ctx,
+ css_qname *qname);
static css_error parseSimpleSelector(css_language *c,
const parserutils_vector *vector, int *ctx,
css_selector **result);
@@ -155,7 +162,10 @@ css_error css__language_create(css_stylesheet *sheet, css_parser *parser,
}
c->sheet = sheet;
- c->state = BEFORE_CHARSET;
+ c->state = CHARSET_PERMITTED;
+ c->default_namespace = NULL;
+ c->namespaces = NULL;
+ c->num_namespaces = 0;
c->alloc = alloc;
c->pw = pw;
@@ -172,11 +182,23 @@ css_error css__language_create(css_stylesheet *sheet, css_parser *parser,
*/
css_error css__language_destroy(css_language *language)
{
- int i;
+ uint32_t i;
if (language == NULL)
return CSS_BADPARM;
+ if (language->default_namespace != NULL)
+ lwc_string_unref(language->default_namespace);
+
+ if (language->namespaces != NULL) {
+ for (i = 0; i < language->num_namespaces; i++) {
+ lwc_string_unref(language->namespaces[i].prefix);
+ lwc_string_unref(language->namespaces[i].uri);
+ }
+
+ language->alloc(language->namespaces, 0, language->pw);
+ }
+
parserutils_stack_destroy(language->context);
for (i = 0; i < LAST_KNOWN; ++i) {
@@ -315,8 +337,8 @@ css_error handleStartRuleset(css_language *c, const parserutils_vector *vector)
return error;
}
- /* Flag that we've had a valid rule, so @import/@charset have
- * no effect. */
+ /* Flag that we've had a valid rule, so @import/@namespace/@charset
+ * have no effect. */
c->state = HAD_RULE;
/* Rule is now owned by the sheet, so no need to destroy it */
@@ -370,7 +392,7 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[CHARSET],
&match) == lwc_error_ok && match) {
- if (c->state == BEFORE_CHARSET) {
+ if (c->state == CHARSET_PERMITTED) {
const css_token *charset;
/* any0 = STRING */
@@ -407,14 +429,14 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
/* Rule is now owned by the sheet,
* so no need to destroy it */
- c->state = BEFORE_RULES;
+ c->state = IMPORT_PERMITTED;
} else {
return CSS_INVALID;
}
} else if (lwc_string_caseless_isequal(atkeyword->idata,
c->strings[LIBCSS_IMPORT], &match) == lwc_error_ok &&
match) {
- if (c->state != HAD_RULE) {
+ if (c->state <= IMPORT_PERMITTED) {
lwc_string *url;
uint64_t media = 0;
@@ -482,7 +504,43 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
/* Rule is now owned by the sheet,
* so no need to destroy it */
- c->state = BEFORE_RULES;
+ c->state = IMPORT_PERMITTED;
+ } else {
+ return CSS_INVALID;
+ }
+ } else if (lwc_string_caseless_isequal(atkeyword->idata,
+ c->strings[NAMESPACE], &match) == lwc_error_ok &&
+ match) {
+ if (c->state <= NAMESPACE_PERMITTED) {
+ lwc_string *prefix = NULL;
+
+ /* any0 = (IDENT ws)? (STRING | URI) ws */
+
+ token = parserutils_vector_iterate(vector, &ctx);
+ if (token == NULL)
+ return CSS_INVALID;
+
+ if (token->type == CSS_TOKEN_IDENT) {
+ prefix = token->idata;
+
+ consumeWhitespace(vector, &ctx);
+
+ token = parserutils_vector_iterate(vector,
+ &ctx);
+ }
+
+ if (token == NULL || (token->type != CSS_TOKEN_STRING &&
+ token->type != CSS_TOKEN_URI)) {
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, &ctx);
+
+ error = addNamespace(c, prefix, token->idata);
+ if (error != CSS_OK)
+ return error;
+
+ c->state = NAMESPACE_PERMITTED;
} else {
return CSS_INVALID;
}
@@ -728,6 +786,7 @@ css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
/******************************************************************************
* At-rule parsing functions *
******************************************************************************/
+
css_error parseMediaList(css_language *c,
const parserutils_vector *vector, int *ctx,
uint64_t *media)
@@ -807,6 +866,101 @@ css_error parseMediaList(css_language *c,
return CSS_OK;
}
+/**
+ * Add a namespace mapping
+ *
+ * \param c Parsing context to add to
+ * \param prefix Namespace prefix, or NULL for default namespace
+ * \param uri Namespace URI
+ * \return CSS_OK on success, CSS_NOMEM on memory exhaustion.
+ */
+css_error addNamespace(css_language *c, lwc_string *prefix, lwc_string *uri)
+{
+ if (prefix == NULL) {
+ /* Replace default namespace */
+ if (c->default_namespace != NULL)
+ lwc_string_unref(c->default_namespace);
+
+ /* Special case: if new namespace uri is "", use NULL */
+ if (lwc_string_length(uri) == 0)
+ c->default_namespace = NULL;
+ else
+ c->default_namespace = lwc_string_ref(uri);
+ } else {
+ /* Replace, or add mapping */
+ bool match;
+ uint32_t idx;
+
+ for (idx = 0; idx < c->num_namespaces; idx++) {
+ if (lwc_string_isequal(c->namespaces[idx].prefix,
+ prefix, &match) == lwc_error_ok &&
+ match)
+ break;
+ }
+
+ if (idx == c->num_namespaces) {
+ /* Not found, create a new mapping */
+ css_namespace *ns = c->alloc(c->namespaces,
+ sizeof(css_namespace) *
+ (c->num_namespaces + 1),
+ c->pw);
+
+ if (ns == NULL)
+ return CSS_NOMEM;
+
+ ns[idx].prefix = lwc_string_ref(prefix);
+ ns[idx].uri = NULL;
+
+ c->namespaces = ns;
+ c->num_namespaces++;
+ }
+
+ /* Replace namespace URI */
+ if (c->namespaces[idx].uri != NULL)
+ lwc_string_unref(c->namespaces[idx].uri);
+
+ /* Special case: if new namespace uri is "", use NULL */
+ if (lwc_string_length(uri) == 0)
+ c->namespaces[idx].uri = NULL;
+ else
+ c->namespaces[idx].uri = lwc_string_ref(uri);
+ }
+
+ return CSS_OK;
+}
+
+/**
+ * Look up a namespace prefix
+ *
+ * \param c Language parser context
+ * \param prefix Namespace prefix to find, or NULL for none
+ * \param uri Pointer to location to receive namespace URI
+ * \return CSS_OK on success, CSS_INVALID if prefix is not found
+ */
+css_error lookupNamespace(css_language *c, lwc_string *prefix, lwc_string **uri)
+{
+ uint32_t idx;
+ bool match;
+
+ if (prefix == NULL) {
+ *uri = NULL;
+ } else {
+ for (idx = 0; idx < c->num_namespaces; idx++) {
+ if (lwc_string_isequal(c->namespaces[idx].prefix,
+ prefix, &match) == lwc_error_ok &&
+ match)
+ break;
+ }
+
+ if (idx == c->num_namespaces)
+ return CSS_INVALID;
+
+ *uri = c->namespaces[idx].uri;
+ }
+
+ return CSS_OK;
+}
+
/******************************************************************************
* Selector list parsing functions *
******************************************************************************/
@@ -814,6 +968,7 @@ css_error parseMediaList(css_language *c,
css_error parseClass(css_language *c, const parserutils_vector *vector,
int *ctx, css_selector_detail *specific)
{
+ css_qname qname;
css_selector_detail_value detail_value;
const css_token *token;
@@ -828,19 +983,25 @@ css_error parseClass(css_language *c, const parserutils_vector *vector,
detail_value.string = NULL;
+ qname.ns = NULL;
+ qname.name = token->idata;
+
return css__stylesheet_selector_detail_init(c->sheet,
- CSS_SELECTOR_CLASS, token->idata, detail_value,
+ CSS_SELECTOR_CLASS, &qname, 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_qname qname;
css_selector_detail_value detail_value;
- const css_token *token, *name, *value = NULL;
+ const css_token *token, *value = NULL;
css_selector_type type = CSS_SELECTOR_ATTRIBUTE;
+ css_error error;
+ lwc_string *prefix = NULL;
- /* attrib -> '[' ws IDENT ws [
+ /* attrib -> '[' ws namespace_prefix? IDENT ws [
* [ '=' |
* INCLUDES |
* DASHMATCH |
@@ -849,6 +1010,7 @@ css_error parseAttrib(css_language *c, const parserutils_vector *vector,
* SUBSTRINGMATCH
* ] ws
* [ IDENT | STRING ] ws ]? ']'
+ * namespace_prefix -> [ IDENT | '*' ]? '|'
*/
token = parserutils_vector_iterate(vector, ctx);
if (token == NULL || tokenIsChar(token, '[') == false)
@@ -857,10 +1019,34 @@ css_error parseAttrib(css_language *c, const parserutils_vector *vector,
consumeWhitespace(vector, ctx);
token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
+ tokenIsChar(token, '*') == false &&
+ tokenIsChar(token, '|') == false))
+ return CSS_INVALID;
+
+ if (tokenIsChar(token, '|')) {
+ token = parserutils_vector_iterate(vector, ctx);
+ } else {
+ const css_token *temp;
+
+ temp = parserutils_vector_peek(vector, *ctx);
+ if (temp != NULL && tokenIsChar(temp, '|')) {
+ prefix = token->idata;
+
+ parserutils_vector_iterate(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ }
+ }
+
if (token == NULL || token->type != CSS_TOKEN_IDENT)
return CSS_INVALID;
- name = token;
+ error = lookupNamespace(c, prefix, &qname.ns);
+ if (error != CSS_OK)
+ return error;
+
+ qname.name = token->idata;
consumeWhitespace(vector, ctx);
@@ -903,7 +1089,7 @@ css_error parseAttrib(css_language *c, const parserutils_vector *vector,
detail_value.string = value != NULL ? value->idata : NULL;
return css__stylesheet_selector_detail_init(c->sheet, type,
- name->idata, detail_value,
+ &qname, detail_value,
CSS_SELECTOR_DETAIL_VALUE_STRING, false, specific);
}
@@ -1151,7 +1337,7 @@ css_error parsePseudo(css_language *c, const parserutils_vector *vector,
css_selector_detail_value detail_value;
css_selector_detail_value_type value_type =
CSS_SELECTOR_DETAIL_VALUE_STRING;
- lwc_string *name;
+ css_qname qname;
const css_token *token;
bool match = false, require_element = false, negate = false;
uint32_t lut_idx;
@@ -1181,11 +1367,12 @@ css_error parsePseudo(css_language *c, const parserutils_vector *vector,
token->type != CSS_TOKEN_FUNCTION))
return CSS_INVALID;
- name = token->idata;
+ qname.ns = NULL;
+ qname.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,
+ if ((lwc_string_caseless_isequal(qname.name,
c->strings[pseudo_lut[lut_idx].index],
&match) == lwc_error_ok) && match) {
type = pseudo_lut[lut_idx].type;
@@ -1232,22 +1419,24 @@ css_error parsePseudo(css_language *c, const parserutils_vector *vector,
value_type = CSS_SELECTOR_DETAIL_VALUE_NTH;
} else if (fun_type == NOT) {
- /* element_name | specific */
+ /* type_selector | 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;
+ tokenIsChar(token, '*') ||
+ tokenIsChar(token, '|')) {
+ /* Have type selector */
+ error = parseTypeSelector(c, vector, ctx,
+ &qname);
+ if (error != CSS_OK)
+ return error;
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;
@@ -1257,7 +1446,7 @@ css_error parsePseudo(css_language *c, const parserutils_vector *vector,
if (error != CSS_OK)
return error;
- name = det.name;
+ qname = det.qname;
type = det.type;
detail_value = det.value;
value_type = det.value_type;
@@ -1274,7 +1463,8 @@ css_error parsePseudo(css_language *c, const parserutils_vector *vector,
}
return css__stylesheet_selector_detail_init(c->sheet,
- type, name, detail_value, value_type, negate, specific);
+ type, &qname, detail_value, value_type,
+ negate, specific);
}
css_error parseSpecific(css_language *c,
@@ -1291,12 +1481,16 @@ css_error parseSpecific(css_language *c,
return CSS_INVALID;
if (token->type == CSS_TOKEN_HASH) {
+ css_qname qname;
css_selector_detail_value detail_value;
detail_value.string = NULL;
+ qname.ns = NULL;
+ qname.name = token->idata;
+
error = css__stylesheet_selector_detail_init(c->sheet,
- CSS_SELECTOR_ID, token->idata, detail_value,
+ CSS_SELECTOR_ID, &qname, detail_value,
CSS_SELECTOR_DETAIL_VALUE_STRING, false,
specific);
if (error != CSS_OK)
@@ -1359,35 +1553,105 @@ css_error parseSelectorSpecifics(css_language *c,
return CSS_OK;
}
+css_error parseTypeSelector(css_language *c, const parserutils_vector *vector,
+ int *ctx, css_qname *qname)
+{
+ const css_token *token;
+ css_error error;
+ lwc_string *prefix = NULL;
+
+ /* type_selector -> namespace_prefix? element_name
+ * namespace_prefix -> [ IDENT | '*' ]? '|'
+ * element_name -> IDENT | '*'
+ */
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (token == NULL)
+ return CSS_INVALID;
+
+ if (tokenIsChar(token, '|') == false) {
+ prefix = token->idata;
+
+ parserutils_vector_iterate(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ }
+
+ if (token != NULL && tokenIsChar(token, '|')) {
+ /* Have namespace prefix */
+ parserutils_vector_iterate(vector, ctx);
+
+ /* Expect element_name */
+ token = parserutils_vector_iterate(vector, ctx);
+
+ if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
+ tokenIsChar(token, '*') == false)) {
+ return CSS_INVALID;
+ }
+
+ error = lookupNamespace(c, prefix, &qname->ns);
+ if (error != CSS_OK)
+ return error;
+
+ qname->name = token->idata;
+ } else {
+ /* No namespace prefix */
+ if (c->default_namespace == NULL) {
+ qname->ns = c->strings[UNIVERSAL];
+ } else {
+ qname->ns = c->default_namespace;
+ }
+
+ qname->name = prefix;
+ }
+
+ return CSS_OK;
+}
+
css_error parseSimpleSelector(css_language *c,
const parserutils_vector *vector, int *ctx,
css_selector **result)
{
+ int orig_ctx = *ctx;
css_error error;
const css_token *token;
css_selector *selector;
+ css_qname qname;
- /* simple_selector -> element_name specifics
- * -> specific specifics
- * element_name -> IDENT | '*'
+ /* simple_selector -> type_selector specifics
+ * -> specific specifics
*/
token = parserutils_vector_peek(vector, *ctx);
if (token == NULL)
return CSS_INVALID;
- if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*')) {
- /* Have element name */
- error = css__stylesheet_selector_create(c->sheet,
- token->idata, &selector);
- if (error != CSS_OK)
+ if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*') ||
+ tokenIsChar(token, '|')) {
+ /* Have type selector */
+ error = parseTypeSelector(c, vector, ctx, &qname);
+ if (error != CSS_OK) {
+ *ctx = orig_ctx;
return error;
+ }
- parserutils_vector_iterate(vector, ctx);
+ error = css__stylesheet_selector_create(c->sheet,
+ &qname, &selector);
+ if (error != CSS_OK) {
+ *ctx = orig_ctx;
+ return error;
+ }
} else {
/* Universal selector */
+ if (c->default_namespace == NULL)
+ qname.ns = c->strings[UNIVERSAL];
+ else
+ qname.ns = c->default_namespace;
+
+ qname.name = c->strings[UNIVERSAL];
+
error = css__stylesheet_selector_create(c->sheet,
- c->strings[UNIVERSAL], &selector);
+ &qname, &selector);
if (error != CSS_OK)
return error;
diff --git a/src/parse/language.h b/src/parse/language.h
index 39b7222..0f1985c 100644
--- a/src/parse/language.h
+++ b/src/parse/language.h
@@ -19,6 +19,14 @@
#include "parse/propstrings.h"
/**
+ * CSS namespace mapping
+ */
+typedef struct css_namespace {
+ lwc_string *prefix; /**< Namespace prefix */
+ lwc_string *uri; /**< Namespace URI */
+} css_namespace;
+
+/**
* Context for a CSS language parser
*/
typedef struct css_language {
@@ -28,8 +36,9 @@ typedef struct css_language {
parserutils_stack *context; /**< Context stack */
enum {
- BEFORE_CHARSET,
- BEFORE_RULES,
+ CHARSET_PERMITTED,
+ IMPORT_PERMITTED,
+ NAMESPACE_PERMITTED,
HAD_RULE
} state; /**< State flag, for at-rule handling */
@@ -37,6 +46,10 @@ typedef struct css_language {
/** Interned strings */
lwc_string *strings[LAST_KNOWN];
+ lwc_string *default_namespace; /**< Default namespace URI */
+ css_namespace *namespaces; /**< Array of namespace mappings */
+ uint32_t num_namespaces; /**< Number of namespace mappings */
+
css_allocator_fn alloc; /**< Memory (de)allocation function */
void *pw; /**< Client's private data */
} css_language;
diff --git a/src/parse/propstrings.c b/src/parse/propstrings.c
index 37ced63..7e15e31 100644
--- a/src/parse/propstrings.c
+++ b/src/parse/propstrings.c
@@ -14,6 +14,7 @@ const stringmap_entry stringmap[LAST_KNOWN] = {
{ "charset", SLEN("charset") },
{ "import", SLEN("import") },
{ "media", SLEN("media") },
+ { "namespace", SLEN("namespace") },
{ "page", SLEN("page") },
{ "aural", SLEN("aural") },
diff --git a/src/parse/propstrings.h b/src/parse/propstrings.h
index fc91806..25fe113 100644
--- a/src/parse/propstrings.h
+++ b/src/parse/propstrings.h
@@ -15,7 +15,7 @@ enum {
UNIVERSAL,
/* At-rules */
- CHARSET, LIBCSS_IMPORT, MEDIA, PAGE,
+ CHARSET, LIBCSS_IMPORT, MEDIA, NAMESPACE, PAGE,
/* Media types */
AURAL, BRAILLE, EMBOSSED, HANDHELD, PRINT, PROJECTION,
diff --git a/src/select/hash.c b/src/select/hash.c
index 43a696f..e6c6cf8 100644
--- a/src/select/hash.c
+++ b/src/select/hash.c
@@ -202,11 +202,11 @@ css_error css__selector_hash_insert(css_selector_hash *hash,
return CSS_BADPARM;
/* Work out which hash to insert into */
- if (lwc_string_length(selector->data.name) != 1 ||
- lwc_string_data(selector->data.name)[0] != '*') {
+ if (lwc_string_length(selector->data.qname.name) != 1 ||
+ lwc_string_data(selector->data.qname.name)[0] != '*') {
/* Named element */
mask = hash->elements.n_slots - 1;
- index = _hash_name(selector->data.name) & mask;
+ index = _hash_name(selector->data.qname.name) & mask;
error = _insert_into_chain(hash, &hash->elements.slots[index],
selector);
@@ -250,11 +250,11 @@ css_error css__selector_hash_remove(css_selector_hash *hash,
return CSS_BADPARM;
/* Work out which hash to insert into */
- if (lwc_string_length(selector->data.name) != 1 ||
- lwc_string_data(selector->data.name)[0] != '*') {
+ if (lwc_string_length(selector->data.qname.name) != 1 ||
+ lwc_string_data(selector->data.qname.name)[0] != '*') {
/* Named element */
mask = hash->elements.n_slots - 1;
- index = _hash_name(selector->data.name) & mask;
+ index = _hash_name(selector->data.qname.name) & mask;
error = _remove_from_chain(hash, &hash->elements.slots[index],
selector);
@@ -284,7 +284,7 @@ css_error css__selector_hash_remove(css_selector_hash *hash,
* Find the first selector that matches name
*
* \param hash Hash to search
- * \param name Name to match
+ * \param qname Qualified name to match
* \param iterator Pointer to location to receive iterator function
* \param matched Pointer to location to receive selector
* \return CSS_OK on success, appropriate error otherwise
@@ -292,19 +292,19 @@ css_error css__selector_hash_remove(css_selector_hash *hash,
* If nothing matches, CSS_OK will be returned and **matched == NULL
*/
css_error css__selector_hash_find(css_selector_hash *hash,
- lwc_string *name,
+ css_qname *qname,
css_selector_hash_iterator *iterator,
const css_selector ***matched)
{
uint32_t index, mask;
hash_entry *head;
- if (hash == NULL || name == NULL || iterator == NULL || matched == NULL)
+ if (hash == NULL || qname == NULL || iterator == NULL || matched == NULL)
return CSS_BADPARM;
/* Find index */
mask = hash->elements.n_slots - 1;
- index = _hash_name(name) & mask;
+ index = _hash_name(qname->name) & mask;
head = &hash->elements.slots[index];
@@ -315,7 +315,8 @@ css_error css__selector_hash_find(css_selector_hash *hash,
bool match = false;
lerror = lwc_string_caseless_isequal(
- name, head->sel->data.name, &match);
+ qname->name, head->sel->data.qname.name,
+ &match);
if (lerror != lwc_error_ok)
return css_error_from_lwc_error(lerror);
@@ -538,7 +539,7 @@ lwc_string *_class_name(const css_selector *selector)
do {
/* Ignore :not(.class) */
if (detail->type == CSS_SELECTOR_CLASS && detail->negate == 0) {
- name = detail->name;
+ name = detail->qname.name;
break;
}
@@ -565,7 +566,7 @@ lwc_string *_id_name(const css_selector *selector)
do {
/* Ignore :not(#id) */
if (detail->type == CSS_SELECTOR_ID && detail->negate == 0) {
- name = detail->name;
+ name = detail->qname.name;
break;
}
@@ -701,7 +702,7 @@ css_error _iterate_elements(css_selector_hash *hash,
if (hash == NULL || current == NULL || next == NULL)
return CSS_BADPARM;
- name = head->sel->data.name;
+ name = head->sel->data.qname.name;
/* Look for the next selector that matches the key */
for (head = head->next; head != NULL; head = head->next) {
@@ -709,7 +710,7 @@ css_error _iterate_elements(css_selector_hash *hash,
bool match = false;
lerror = lwc_string_caseless_isequal(
- name, head->sel->data.name, &match);
+ name, head->sel->data.qname.name, &match);
if (lerror != lwc_error_ok)
return css_error_from_lwc_error(lerror);
diff --git a/src/select/hash.h b/src/select/hash.h
index c8a3d36..6b55950 100644
--- a/src/select/hash.h
+++ b/src/select/hash.h
@@ -32,7 +32,7 @@ css_error css__selector_hash_remove(css_selector_hash *hash,
const struct css_selector *selector);
css_error css__selector_hash_find(css_selector_hash *hash,
- lwc_string *name,
+ css_qname *qname,
css_selector_hash_iterator *iterator,
const struct css_selector ***matched);
css_error css__selector_hash_find_by_class(css_selector_hash *hash,
diff --git a/src/select/select.c b/src/select/select.c
index ee1ad47..e2b55ee 100644
--- a/src/select/select.c
+++ b/src/select/select.c
@@ -929,7 +929,7 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet, css_select_state *state)
{
static const css_selector *empty_selector = NULL;
- lwc_string *element = NULL;
+ css_qname element = { NULL, NULL };
lwc_string *id = NULL;
lwc_string **classes = NULL;
uint32_t n_classes = 0, i = 0;
@@ -965,7 +965,7 @@ css_error match_selectors_in_sheet(css_select_ctx *ctx,
goto cleanup;
/* Find hash chain that applies to current node */
- error = css__selector_hash_find(sheet->selectors, element,
+ error = css__selector_hash_find(sheet->selectors, &element,
&node_iterator, &node_selectors);
if (error != CSS_OK)
goto cleanup;
@@ -1081,7 +1081,10 @@ cleanup:
if (id != NULL)
lwc_string_unref(id);
- lwc_string_unref(element);
+ if (element.ns != NULL)
+ lwc_string_unref(element.ns);
+ lwc_string_unref(element.name);
+
return error;
}
@@ -1122,7 +1125,8 @@ css_error match_selector_chain(css_select_ctx *ctx,
/* Consider any combinator on this selector */
if (s->data.comb != CSS_COMBINATOR_NONE &&
- s->combinator->data.name != state->universal) {
+ s->combinator->data.qname.name !=
+ state->universal) {
/* Named combinator */
error = match_named_combinator(ctx, s->data.comb,
s->combinator, state, node, &next_node);
@@ -1133,7 +1137,8 @@ css_error match_selector_chain(css_select_ctx *ctx,
if (next_node == NULL)
return CSS_OK;
} else if (s->data.comb != CSS_COMBINATOR_NONE &&
- s->combinator->data.name == state->universal) {
+ s->combinator->data.qname.name ==
+ state->universal) {
/* Universal combinator */
error = match_universal_combinator(ctx, s->data.comb,
s->combinator, state, node, &next_node);
@@ -1187,25 +1192,26 @@ css_error match_named_combinator(css_select_ctx *ctx, css_combinator type,
switch (type) {
case CSS_COMBINATOR_ANCESTOR:
error = state->handler->named_ancestor_node(state->pw,
- n, selector->data.name, &n);
+ n, &selector->data.qname, &n);
if (error != CSS_OK)
return error;
break;
case CSS_COMBINATOR_PARENT:
error = state->handler->named_parent_node(state->pw,
- n, selector->data.name, &n);
+ n, &selector->data.qname, &n);
if (error != CSS_OK)
return error;
break;
case CSS_COMBINATOR_SIBLING:
error = state->handler->named_sibling_node(state->pw,
- n, selector->data.name, &n);
+ n, &selector->data.qname, &n);
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);
+ state->pw, n, &selector->data.qname,
+ &n);
if (error != CSS_OK)
return error;
case CSS_COMBINATOR_NONE:
@@ -1358,28 +1364,24 @@ css_error match_detail(css_select_ctx *ctx, void *node,
switch (detail->type) {
case CSS_SELECTOR_ELEMENT:
- if (lwc_string_length(detail->name) == 1 &&
- lwc_string_data(detail->name)[0] == '*') {
- *match = true;
- } else {
- error = state->handler->node_has_name(state->pw, node,
- detail->name, match);
- }
+ error = state->handler->node_has_name(state->pw, node,
+ &detail->qname, match);
break;
case CSS_SELECTOR_CLASS:
error = state->handler->node_has_class(state->pw, node,
- detail->name, match);
+ detail->qname.name, match);
break;
case CSS_SELECTOR_ID:
error = state->handler->node_has_id(state->pw, node,
- detail->name, match);
+ detail->qname.name, match);
break;
case CSS_SELECTOR_PSEUDO_CLASS:
error = state->handler->node_is_root(state->pw, node, &is_root);
if (error != CSS_OK)
return error;
- if (is_root == false && detail->name == state->first_child) {
+ if (is_root == false &&
+ detail->qname.name == state->first_child) {
int32_t num_before = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1387,7 +1389,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
if (error == CSS_OK)
*match = (num_before == 0);
} else if (is_root == false &&
- detail->name == state->nth_child) {
+ detail->qname.name == state->nth_child) {
int32_t num_before = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1399,7 +1401,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
*match = match_nth(a, b, num_before + 1);
}
} else if (is_root == false &&
- detail->name == state->nth_last_child) {
+ detail->qname.name == state->nth_last_child) {
int32_t num_after = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1411,7 +1413,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
*match = match_nth(a, b, num_after + 1);
}
} else if (is_root == false &&
- detail->name == state->nth_of_type) {
+ detail->qname.name == state->nth_of_type) {
int32_t num_before = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1423,7 +1425,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
*match = match_nth(a, b, num_before + 1);
}
} else if (is_root == false &&
- detail->name == state->nth_last_of_type) {
+ detail->qname.name == state->nth_last_of_type) {
int32_t num_after = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1435,7 +1437,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
*match = match_nth(a, b, num_after + 1);
}
} else if (is_root == false &&
- detail->name == state->last_child) {
+ detail->qname.name == state->last_child) {
int32_t num_after = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1443,7 +1445,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
if (error == CSS_OK)
*match = (num_after == 0);
} else if (is_root == false &&
- detail->name == state->first_of_type) {
+ detail->qname.name == state->first_of_type) {
int32_t num_before = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1451,7 +1453,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
if (error == CSS_OK)
*match = (num_before == 0);
} else if (is_root == false &&
- detail->name == state->last_of_type) {
+ detail->qname.name == state->last_of_type) {
int32_t num_after = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1459,7 +1461,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
if (error == CSS_OK)
*match = (num_after == 0);
} else if (is_root == false &&
- detail->name == state->only_child) {
+ detail->qname.name == state->only_child) {
int32_t num_before = 0, num_after = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1473,7 +1475,7 @@ css_error match_detail(css_select_ctx *ctx, void *node,
(num_after == 0);
}
} else if (is_root == false &&
- detail->name == state->only_of_type) {
+ detail->qname.name == state->only_of_type) {
int32_t num_before = 0, num_after = 0;
error = state->handler->node_count_siblings(state->pw,
@@ -1486,39 +1488,39 @@ css_error match_detail(css_select_ctx *ctx, void *node,
*match = (num_before == 0) &&
(num_after == 0);
}
- } else if (detail->name == state->root) {
+ } else if (detail->qname.name == state->root) {
*match = is_root;
- } else if (detail->name == state->empty) {
+ } else if (detail->qname.name == state->empty) {
error = state->handler->node_is_empty(state->pw,
node, match);
- } else if (detail->name == state->link) {
+ } else if (detail->qname.name == state->link) {
error = state->handler->node_is_link(state->pw,
node, match);
- } else if (detail->name == state->visited) {
+ } else if (detail->qname.name == state->visited) {
error = state->handler->node_is_visited(state->pw,
node, match);
- } else if (detail->name == state->hover) {
+ } else if (detail->qname.name == state->hover) {
error = state->handler->node_is_hover(state->pw,
node, match);
- } else if (detail->name == state->active) {
+ } else if (detail->qname.name == state->active) {
error = state->handler->node_is_active(state->pw,
node, match);
- } else if (detail->name == state->focus) {
+ } else if (detail->qname.name == state->focus) {
error = state->handler->node_is_focus(state->pw,
node, match);
- } else if (detail->name == state->target) {
+ } else if (detail->qname.name == state->target) {
error = state->handler->node_is_target(state->pw,
node, match);
- } else if (detail->name == state->lang) {
+ } else if (detail->qname.name == state->lang) {
error = state->handler->node_is_lang(state->pw,
node, detail->value.string, match);
- } else if (detail->name == state->enabled) {
+ } else if (detail->qname.name == state->enabled) {
error = state->handler->node_is_enabled(state->pw,
node, match);
- } else if (detail->name == state->disabled) {
+ } else if (detail->qname.name == state->disabled) {
error = state->handler->node_is_disabled(state->pw,
node, match);
- } else if (detail->name == state->checked) {
+ } else if (detail->qname.name == state->checked) {
error = state->handler->node_is_checked(state->pw,
node, match);
} else
@@ -1527,49 +1529,49 @@ css_error match_detail(css_select_ctx *ctx, void *node,
case CSS_SELECTOR_PSEUDO_ELEMENT:
*match = true;
- if (detail->name == state->first_line) {
+ if (detail->qname.name == state->first_line) {
*pseudo_element = CSS_PSEUDO_ELEMENT_FIRST_LINE;
- } else if (detail->name == state->first_letter) {
+ } else if (detail->qname.name == state->first_letter) {
*pseudo_element = CSS_PSEUDO_ELEMENT_FIRST_LETTER;
- } else if (detail->name == state->before) {
+ } else if (detail->qname.name == state->before) {
*pseudo_element = CSS_PSEUDO_ELEMENT_BEFORE;
- } else if (detail->name == state->after) {
+ } else if (detail->qname.name == state->after) {
*pseudo_element = CSS_PSEUDO_ELEMENT_AFTER;
} else
*match = false;
break;
case CSS_SELECTOR_ATTRIBUTE:
error = state->handler->node_has_attribute(state->pw, node,
- detail->name, match);
+ &detail->qname, match);
break;
case CSS_SELECTOR_ATTRIBUTE_EQUAL:
error = state->handler->node_has_attribute_equal(state->pw,
- node, detail->name, detail->value.string,
+ node, &detail->qname, detail->value.string,
match);
break;
case CSS_SELECTOR_ATTRIBUTE_DASHMATCH:
error = state->handler->node_has_attribute_dashmatch(state->pw,
- node, detail->name, detail->value.string,
+ node, &detail->qname, detail->value.string,
match);
break;
case CSS_SELECTOR_ATTRIBUTE_INCLUDES:
error = state->handler->node_has_attribute_includes(state->pw,
- node, detail->name, detail->value.string,
+ node, &detail->qname, 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,
+ node, &detail->qname, 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,
+ node, &detail->qname, 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,
+ node, &detail->qname, detail->value.string,
match);
break;
}
diff --git a/src/stylesheet.c b/src/stylesheet.c
index 382260a..604d824 100644
--- a/src/stylesheet.c
+++ b/src/stylesheet.c
@@ -781,18 +781,19 @@ css_error css__stylesheet_style_destroy(css_style *style)
* Create an element selector
*
* \param sheet The stylesheet context
- * \param name Name of selector
+ * \param qname Qualified name of selector
* \param selector Pointer to location to receive selector object
* \return CSS_OK on success,
* CSS_BADPARM on bad parameters,
* CSS_NOMEM on memory exhaustion
*/
css_error css__stylesheet_selector_create(css_stylesheet *sheet,
- lwc_string *name, css_selector **selector)
+ css_qname *qname, css_selector **selector)
{
css_selector *sel;
- if (sheet == NULL || name == NULL || selector == NULL)
+ if (sheet == NULL || qname == NULL || qname->name == NULL ||
+ selector == NULL)
return CSS_BADPARM;
sel = sheet->alloc(NULL, sizeof(css_selector), sheet->pw);
@@ -802,7 +803,11 @@ css_error css__stylesheet_selector_create(css_stylesheet *sheet,
memset(sel, 0, sizeof(css_selector));
sel->data.type = CSS_SELECTOR_ELEMENT;
- sel->data.name = lwc_string_ref(name);
+ if (qname->ns != NULL)
+ sel->data.qname.ns = lwc_string_ref(qname->ns);
+ else
+ sel->data.qname.ns = NULL;
+ sel->data.qname.name = lwc_string_ref(qname->name);
sel->data.value.string = NULL;
sel->data.value_type = CSS_SELECTOR_DETAIL_VALUE_STRING;
@@ -810,8 +815,8 @@ css_error css__stylesheet_selector_create(css_stylesheet *sheet,
sel->specificity = CSS_SPECIFICITY_A;
} else {
/* Initial specificity -- 1 for an element, 0 for universal */
- if (lwc_string_length(name) != 1 ||
- lwc_string_data(name)[0] != '*')
+ if (lwc_string_length(qname->name) != 1 ||
+ lwc_string_data(qname->name)[0] != '*')
sel->specificity = CSS_SPECIFICITY_D;
else
sel->specificity = 0;
@@ -848,7 +853,9 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet,
d = c->combinator;
for (detail = &c->data; detail;) {
- lwc_string_unref(detail->name);
+ if (detail->qname.ns != NULL)
+ lwc_string_unref(detail->qname.ns);
+ lwc_string_unref(detail->qname.name);
if (detail->value_type ==
CSS_SELECTOR_DETAIL_VALUE_STRING &&
@@ -866,7 +873,9 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet,
}
for (detail = &selector->data; detail;) {
- lwc_string_unref(detail->name);
+ if (detail->qname.ns != NULL)
+ lwc_string_unref(detail->qname.ns);
+ lwc_string_unref(detail->qname.name);
if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING &&
detail->value.string != NULL) {
@@ -891,27 +900,31 @@ css_error css__stylesheet_selector_destroy(css_stylesheet *sheet,
*
* \param sheet The stylesheet context
* \param type The type of selector to create
- * \param name Name of selector
+ * \param qname Qualified 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
+ *
+ * \note No strings are referenced at this point: they will be
+ * referenced when appending the detail to a selector.
*/
css_error css__stylesheet_selector_detail_init(css_stylesheet *sheet,
- css_selector_type type, lwc_string *name,
+ css_selector_type type, css_qname *qname,
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)
+ if (sheet == NULL || qname == NULL || qname->name == NULL ||
+ detail == NULL)
return CSS_BADPARM;
memset(detail, 0, sizeof(css_selector_detail));
detail->type = type;
- detail->name = name;
+ detail->qname = *qname;
detail->value = value;
detail->value_type = value_type;
detail->negate = negate;
@@ -959,7 +972,9 @@ css_error css__stylesheet_selector_append_specific(css_stylesheet *sheet,
(&temp->data)[num_details].next = 1;
/* Ref the strings */
- lwc_string_ref(detail->name);
+ if (detail->qname.ns != NULL)
+ lwc_string_ref(detail->qname.ns);
+ lwc_string_ref(detail->qname.name);
if (detail->value_type == CSS_SELECTOR_DETAIL_VALUE_STRING &&
detail->value.string != NULL)
lwc_string_ref(detail->value.string);
diff --git a/src/stylesheet.h b/src/stylesheet.h
index 9734f02..6333d76 100644
--- a/src/stylesheet.h
+++ b/src/stylesheet.h
@@ -69,7 +69,7 @@ typedef union css_selector_detail_value {
} css_selector_detail_value;
typedef struct css_selector_detail {
- lwc_string *name; /**< Interned name */
+ css_qname qname; /**< Interned name */
css_selector_detail_value value; /**< Detail value */
unsigned int type : 4, /**< Type of selector */
@@ -240,12 +240,12 @@ static inline css_error css_stylesheet_style_inherit(css_style *style,
}
css_error css__stylesheet_selector_create(css_stylesheet *sheet,
- lwc_string *name, css_selector **selector);
+ css_qname *qname, css_selector **selector);
css_error css__stylesheet_selector_destroy(css_stylesheet *sheet,
css_selector *selector);
css_error css__stylesheet_selector_detail_init(css_stylesheet *sheet,
- css_selector_type type, lwc_string *name,
+ css_selector_type type, css_qname *qname,
css_selector_detail_value value,
css_selector_detail_value_type value_type,
bool negate, css_selector_detail *detail);