summaryrefslogtreecommitdiff
path: root/src/parse
diff options
context:
space:
mode:
Diffstat (limited to 'src/parse')
-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
4 files changed, 316 insertions, 38 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,