From a57c4f03cc819c7eb2d5727157870e2aee947537 Mon Sep 17 00:00:00 2001 From: John Mark Bell Date: Fri, 26 Jun 2009 19:49:46 +0000 Subject: Support for parsing inline styles svn path=/trunk/libcss/; revision=8024 --- src/parse/language.c | 11 +- src/parse/parse.c | 409 ++++++++++++++++++++++++++++++++++++++++----------- src/parse/parse.h | 3 + src/select/select.c | 25 ++++ src/stylesheet.c | 29 +++- src/stylesheet.h | 2 + 6 files changed, 383 insertions(+), 96 deletions(-) (limited to 'src') diff --git a/src/parse/language.c b/src/parse/language.c index f4ba3ee..7054130 100644 --- a/src/parse/language.c +++ b/src/parse/language.c @@ -287,10 +287,13 @@ css_error handleStartRuleset(css_language *c, const parserutils_vector *vector) if (error != CSS_OK) return error; - error = parseSelectorList(c, vector, rule); - if (error != CSS_OK) { - css_stylesheet_rule_destroy(c->sheet, rule); - return error; + if (vector != NULL) { + /* Parse selectors, if there are any */ + error = parseSelectorList(c, vector, rule); + if (error != CSS_OK) { + css_stylesheet_rule_destroy(c->sheet, rule); + return error; + } } entry.data = rule; diff --git a/src/parse/parse.c b/src/parse/parse.c index d2b9fe6..15e3020 100644 --- a/src/parse/parse.c +++ b/src/parse/parse.c @@ -22,7 +22,7 @@ #include "utils/utils.h" #undef DEBUG_STACK -#undef DEBUG_EVENTS +#define DEBUG_EVENTS /** \todo The CSSOM expects us to preserve unknown rules. We currently discard * all of their content, so the higher levels never see them. */ @@ -67,7 +67,10 @@ enum { sAny = 19, sMalformedDecl = 20, sMalformedSelector = 21, - sMalformedAtRule = 22 + sMalformedAtRule = 22, + sInlineStyle = 23, + sISBody0 = 24, + sISBody = 25 }; /** @@ -112,6 +115,11 @@ struct css_parser void *pw; /**< Client-specific private data */ }; +static css_error css_parser_create_internal(const char *charset, + css_charset_source cs_source, lwc_context *dictionary, + css_allocator_fn alloc, void *pw, parser_state initial, + css_parser **parser); + static inline css_error transition(css_parser *parser, parser_state to, parser_state subsequent); static inline css_error transitionNoRet(css_parser *parser, parser_state to); @@ -144,6 +152,9 @@ static inline css_error parseAny(css_parser *parser); static inline css_error parseMalformedDeclaration(css_parser *parser); static inline css_error parseMalformedSelector(css_parser *parser); static inline css_error parseMalformedAtRule(css_parser *parser); +static inline css_error parseInlineStyle(css_parser *parser); +static inline css_error parseISBody0(css_parser *parser); +static inline css_error parseISBody(css_parser *parser); static inline void unref_interned_strings_in_tokens(css_parser *parser); @@ -173,7 +184,10 @@ static css_error (*parseFuncs[])(css_parser *parser) = { parseAny, parseMalformedDeclaration, parseMalformedSelector, - parseMalformedAtRule + parseMalformedAtRule, + parseInlineStyle, + parseISBody0, + parseISBody }; /** @@ -193,92 +207,33 @@ css_error css_parser_create(const char *charset, css_charset_source cs_source, lwc_context *dictionary, css_allocator_fn alloc, void *pw, css_parser **parser) { - css_parser *p; parser_state initial = { sStart, 0 }; - parserutils_error perror; - css_error error; - - if (alloc == NULL || parser == NULL) - return CSS_BADPARM; - - p = alloc(NULL, sizeof(css_parser), pw); - if (p == NULL) - return CSS_NOMEM; - perror = parserutils_inputstream_create(charset, cs_source, - css_charset_extract, (parserutils_alloc) alloc, pw, - &p->stream); - if (perror != PARSERUTILS_OK) { - alloc(p, 0, pw); - return css_error_from_parserutils_error(perror); - } - - error = css_lexer_create(p->stream, alloc, pw, &p->lexer); - if (error != CSS_OK) { - parserutils_inputstream_destroy(p->stream); - alloc(p, 0, pw); - return error; - } - - perror = parserutils_stack_create(sizeof(parser_state), - STACK_CHUNK, (parserutils_alloc) alloc, pw, - &p->states); - if (perror != PARSERUTILS_OK) { - css_lexer_destroy(p->lexer); - parserutils_inputstream_destroy(p->stream); - alloc(p, 0, pw); - return css_error_from_parserutils_error(perror); - } - - p->dictionary = dictionary; - - perror = parserutils_vector_create(sizeof(css_token), - STACK_CHUNK, (parserutils_alloc) alloc, pw, - &p->tokens); - if (perror != PARSERUTILS_OK) { - parserutils_stack_destroy(p->states); - css_lexer_destroy(p->lexer); - parserutils_inputstream_destroy(p->stream); - alloc(p, 0, pw); - return css_error_from_parserutils_error(perror); - } - - perror = parserutils_stack_create(sizeof(char), - STACK_CHUNK, (parserutils_alloc) alloc, pw, - &p->open_items); - if (perror != PARSERUTILS_OK) { - parserutils_vector_destroy(p->tokens); - parserutils_stack_destroy(p->states); - css_lexer_destroy(p->lexer); - parserutils_inputstream_destroy(p->stream); - alloc(p, 0, pw); - return css_error_from_parserutils_error(perror); - } - - perror = parserutils_stack_push(p->states, (void *) &initial); - if (perror != PARSERUTILS_OK) { - parserutils_stack_destroy(p->open_items); - parserutils_vector_destroy(p->tokens); - parserutils_stack_destroy(p->states); - css_lexer_destroy(p->lexer); - parserutils_inputstream_destroy(p->stream); - alloc(p, 0, pw); - return css_error_from_parserutils_error(perror); - } - - p->quirks = false; - p->pushback = NULL; - p->parseError = false; - p->match_char = 0; - p->event = NULL; - p->last_was_ws = false; - p->event_pw = NULL; - p->alloc = alloc; - p->pw = pw; + return css_parser_create_internal(charset, cs_source, dictionary, + alloc, pw, initial, parser); +} - *parser = p; +/** + * Create a CSS parser for an inline style + * + * \param charset Charset of data, if known, or NULL + * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT + * \param dictionary Dictionary in which to intern strings (not copied) + * \param alloc Memory (de)allocation function + * \param pw Pointer to client-specific private data + * \param parser Pointer to location to receive parser instance + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters, + * CSS_NOMEM on memory exhaustion + */ +css_error css_parser_create_for_inline_style(const char *charset, + css_charset_source cs_source, lwc_context *dictionary, + css_allocator_fn alloc, void *pw, css_parser **parser) +{ + parser_state initial = { sInlineStyle, 0 }; - return CSS_OK; + return css_parser_create_internal(charset, cs_source, dictionary, + alloc, pw, initial, parser); } /** @@ -426,6 +381,116 @@ bool css_parser_quirks_permitted(css_parser *parser) return parser->quirks; } +/****************************************************************************** + * Parser creation helper * + ******************************************************************************/ + +/** + * Create a CSS parser (internal) + * + * \param charset Charset of data, if known, or NULL + * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT + * \param dictionary Dictionary in which to intern strings (not copied) + * \param alloc Memory (de)allocation function + * \param pw Pointer to client-specific private data + * \param initial The required initial state of the parser + * \param parser Pointer to location to receive parser instance + * \return CSS_OK on success, + * CSS_BADPARM on bad parameters, + * CSS_NOMEM on memory exhaustion + */ +css_error css_parser_create_internal(const char *charset, + css_charset_source cs_source, lwc_context *dictionary, + css_allocator_fn alloc, void *pw, parser_state initial, + css_parser **parser) +{ + css_parser *p; + parserutils_error perror; + css_error error; + + if (alloc == NULL || parser == NULL) + return CSS_BADPARM; + + p = alloc(NULL, sizeof(css_parser), pw); + if (p == NULL) + return CSS_NOMEM; + + perror = parserutils_inputstream_create(charset, cs_source, + css_charset_extract, (parserutils_alloc) alloc, pw, + &p->stream); + if (perror != PARSERUTILS_OK) { + alloc(p, 0, pw); + return css_error_from_parserutils_error(perror); + } + + error = css_lexer_create(p->stream, alloc, pw, &p->lexer); + if (error != CSS_OK) { + parserutils_inputstream_destroy(p->stream); + alloc(p, 0, pw); + return error; + } + + perror = parserutils_stack_create(sizeof(parser_state), + STACK_CHUNK, (parserutils_alloc) alloc, pw, + &p->states); + if (perror != PARSERUTILS_OK) { + css_lexer_destroy(p->lexer); + parserutils_inputstream_destroy(p->stream); + alloc(p, 0, pw); + return css_error_from_parserutils_error(perror); + } + + p->dictionary = dictionary; + + perror = parserutils_vector_create(sizeof(css_token), + STACK_CHUNK, (parserutils_alloc) alloc, pw, + &p->tokens); + if (perror != PARSERUTILS_OK) { + parserutils_stack_destroy(p->states); + css_lexer_destroy(p->lexer); + parserutils_inputstream_destroy(p->stream); + alloc(p, 0, pw); + return css_error_from_parserutils_error(perror); + } + + perror = parserutils_stack_create(sizeof(char), + STACK_CHUNK, (parserutils_alloc) alloc, pw, + &p->open_items); + if (perror != PARSERUTILS_OK) { + parserutils_vector_destroy(p->tokens); + parserutils_stack_destroy(p->states); + css_lexer_destroy(p->lexer); + parserutils_inputstream_destroy(p->stream); + alloc(p, 0, pw); + return css_error_from_parserutils_error(perror); + } + + perror = parserutils_stack_push(p->states, (void *) &initial); + if (perror != PARSERUTILS_OK) { + parserutils_stack_destroy(p->open_items); + parserutils_vector_destroy(p->tokens); + parserutils_stack_destroy(p->states); + css_lexer_destroy(p->lexer); + parserutils_inputstream_destroy(p->stream); + alloc(p, 0, pw); + return css_error_from_parserutils_error(perror); + } + + p->quirks = false; + p->pushback = NULL; + p->parseError = false; + p->match_char = 0; + p->event = NULL; + p->last_was_ws = false; + p->event_pw = NULL; + p->alloc = alloc; + p->pw = pw; + + *parser = p; + + return CSS_OK; +} + /****************************************************************************** * Helper functions * ******************************************************************************/ @@ -2318,6 +2383,180 @@ css_error parseMalformedAtRule(css_parser *parser) return done(parser); } +css_error parseInlineStyle(css_parser *parser) +{ + enum { Initial = 0, AfterISBody0 = 1 }; + parser_state *state = parserutils_stack_get_current(parser->states); + css_error error; + + /* inline-style = ws is-body0 */ + + switch (state->substate) { + case Initial: + { + parser_state to = { sISBody0, Initial }; + parser_state subsequent = { sInlineStyle, AfterISBody0 }; + + /* Emit fake events such that the language parser knows + * no different from a normal parse. */ + + if (parser->event != NULL) { + /* 1) begin stylesheet */ + parser->event(CSS_PARSER_START_STYLESHEET, NULL, + parser->event_pw); + + /* 2) begin ruleset */ + parser->event(CSS_PARSER_START_RULESET, NULL, + parser->event_pw); + } + + /* Consume leading whitespace */ + error = eatWS(parser); + if (error != CSS_OK) + return error; + + return transition(parser, to, subsequent); + } + case AfterISBody0: + /* Emit remaining fake events to end the parse */ + + if (parser->event != NULL) { + /* 1) end ruleset */ + parser->event(CSS_PARSER_END_RULESET, NULL, + parser->event_pw); + + /* 2) end stylesheet */ + parser->event(CSS_PARSER_END_STYLESHEET, NULL, + parser->event_pw); + } + + break; + } + + return done(parser); +} + +css_error parseISBody0(css_parser *parser) +{ + enum { Initial = 0, AfterISBody = 1 }; + parser_state *state = parserutils_stack_get_current(parser->states); + const css_token *token; + css_error error; + + /* is-body0 -> is-body is-body0 + * -> + */ + + while (1) { + switch (state->substate) { + case Initial: + { + parser_state to = { sISBody, Initial }; + parser_state subsequent = { sISBody0, AfterISBody }; + + error = getToken(parser, &token); + if (error != CSS_OK) + return error; + + error = pushBack(parser, token); + if (error != CSS_OK) + return error; + + /* EOF is the only way out */ + if (token->type == CSS_TOKEN_EOF) + return done(parser); + + return transition(parser, to, subsequent); + } + case AfterISBody: + /* Unless there's a parse error */ + if (parser->parseError) + return done(parser); + + state->substate = Initial; + + break; + } + } + + return done(parser); +} + +css_error parseISBody(css_parser *parser) +{ + enum { Initial = 0, DeclList = 1, Brace = 2, WS = 3 }; + parser_state *state = parserutils_stack_get_current(parser->states); + const css_token *token; + css_error error; + + /* is-body -> declaration decl-list '}' ws + * -> decl-list '}' ws + * + * Note that this looks suspiciously similar to ruleset-end. + * The difference here, however, is that we never end the ruleset. + */ + + switch (state->substate) { + case Initial: + error = getToken(parser, &token); + if (error != CSS_OK) + return error; + + error = pushBack(parser, token); + if (error != CSS_OK) + return error; + + /* If this can't possibly be the start of a decl-list, then + * attempt to parse a declaration. This will catch any invalid + * input at this point and read to the start of the next + * declaration. FIRST(decl-list) = (';', '}') */ + if (token->type != CSS_TOKEN_CHAR || + lwc_string_length(token->ilower) != 1 || + (lwc_string_data(token->ilower)[0] != '}' && + lwc_string_data(token->ilower)[0] != ';')) { + parser_state to = { sDeclaration, Initial }; + parser_state subsequent = { sISBody, DeclList }; + + return transition(parser, to, subsequent); + } + + state->substate = DeclList; + /* Fall through */ + case DeclList: + { + parser_state to = { sDeclList, Initial }; + parser_state subsequent = { sISBody, Brace }; + + return transition(parser, to, subsequent); + } + case Brace: + error = getToken(parser, &token); + if (error != CSS_OK) + return error; + + if (token->type == CSS_TOKEN_EOF) + break; + + if (token->type != CSS_TOKEN_CHAR || + lwc_string_length(token->ilower) != 1 || + lwc_string_data(token->ilower)[0] != '}') { + /* This should never happen, as FOLLOW(decl-list) + * contains only '}' */ + assert(0 && "Expected }"); + } + + state->substate = WS; + /* Fall through */ + case WS: + error = eatWS(parser); + if (error != CSS_OK) + return error; + + break; + } + + return done(parser); +} /** * Iterate the token vector and unref any interned strings in the tokens. diff --git a/src/parse/parse.h b/src/parse/parse.h index c590858..269ce29 100644 --- a/src/parse/parse.h +++ b/src/parse/parse.h @@ -60,6 +60,9 @@ typedef union css_parser_optparams { css_error css_parser_create(const char *charset, css_charset_source cs_source, lwc_context *dict, css_allocator_fn alloc, void *pw, css_parser **parser); +css_error css_parser_create_for_inline_style(const char *charset, + css_charset_source cs_source, lwc_context *dict, + css_allocator_fn alloc, void *pw, css_parser **parser); css_error css_parser_destroy(css_parser *parser); css_error css_parser_setopt(css_parser *parser, css_parser_opttype type, diff --git a/src/select/select.c b/src/select/select.c index e6e9116..b9dd5ef 100644 --- a/src/select/select.c +++ b/src/select/select.c @@ -137,6 +137,10 @@ css_error css_select_ctx_insert_sheet(css_select_ctx *ctx, if (ctx == NULL || sheet == NULL) return CSS_BADPARM; + /* Inline styles cannot be inserted into a selection context */ + if (sheet->inline_style) + return CSS_INVALID; + /* Index must be in the range [0, n_sheets] * The latter being equivalent to append */ if (index > ctx->n_sheets) @@ -240,6 +244,7 @@ css_error css_select_ctx_get_sheet(css_select_ctx *ctx, uint32_t index, * \param node Node to select style for * \param pseudo_element Pseudo element to select for, instead * \param media Currently active media types + * \param inline_style Corresponding inline style for node, or NULL * \param result Pointer to style to populate (assumed clean) * \param handler Dispatch table of handler functions * \param pw Client-specific private data for handler functions @@ -256,6 +261,7 @@ css_error css_select_ctx_get_sheet(css_select_ctx *ctx, uint32_t index, */ css_error css_select_style(css_select_ctx *ctx, void *node, uint32_t pseudo_element, uint64_t media, + const css_stylesheet *inline_style, css_computed_style *result, css_select_handler *handler, void *pw) { @@ -288,6 +294,25 @@ css_error css_select_style(css_select_ctx *ctx, void *node, } } + /* Consider any inline style for the node */ + if (inline_style != NULL) { + css_rule_selector *sel = + (css_rule_selector *) inline_style->rule_list; + + /* Sanity check style */ + if (inline_style->rule_count != 1 || + inline_style->rule_list->type != CSS_RULE_SELECTOR || + inline_style->rule_list->items != 0 || + sel->style == NULL) { + error = CSS_INVALID; + goto cleanup; + } + + error = cascade_style(sel->style, &state); + if (error != CSS_OK) + goto cleanup; + } + /* Take account of presentational hints */ /** \todo Optimisation: merge this loop and the next together */ for (i = 0; i < CSS_N_PROPERTIES; i++) { diff --git a/src/stylesheet.c b/src/stylesheet.c index c48240a..9afed2b 100644 --- a/src/stylesheet.c +++ b/src/stylesheet.c @@ -27,6 +27,7 @@ static css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule); * \param origin Origin of stylesheet * \param media Media stylesheet applies to * \param allow_quirks Permit quirky parsing of stylesheets + * \param inline_style This stylesheet is an inline style * \param dict Dictionary in which to intern strings * \param alloc Memory (de)allocation function * \param alloc_pw Client private data for alloc @@ -38,7 +39,8 @@ static css_error _remove_selectors(css_stylesheet *sheet, css_rule *rule); css_error css_stylesheet_create(css_language_level level, const char *charset, const char *url, const char *title, css_origin origin, uint64_t media, bool allow_quirks, - lwc_context *dict, css_allocator_fn alloc, void *alloc_pw, + bool inline_style, lwc_context *dict, + css_allocator_fn alloc, void *alloc_pw, css_stylesheet **stylesheet) { css_parser_optparams params; @@ -56,10 +58,18 @@ css_error css_stylesheet_create(css_language_level level, memset(sheet, 0, sizeof(css_stylesheet)); sheet->dictionary = dict; + sheet->inline_style = inline_style; - error = css_parser_create(charset, + if (inline_style) { + error = css_parser_create_for_inline_style(charset, charset ? CSS_CHARSET_DICTATED : CSS_CHARSET_DEFAULT, sheet->dictionary, alloc, alloc_pw, &sheet->parser); + } else { + error = css_parser_create(charset, + charset ? CSS_CHARSET_DICTATED : CSS_CHARSET_DEFAULT, + sheet->dictionary, alloc, alloc_pw, &sheet->parser); + } + if (error != CSS_OK) { alloc(sheet, 0, alloc_pw); return error; @@ -568,11 +578,16 @@ css_error css_stylesheet_selector_create(css_stylesheet *sheet, sel->data.name = lwc_context_string_ref(sheet->dictionary, name); sel->data.value = NULL; - /* Initial specificity -- 1 for an element, 0 for universal */ - if (lwc_string_length(name) != 1 || lwc_string_data(name)[0] != '*') - sel->specificity = CSS_SPECIFICITY_D; - else - sel->specificity = 0; + if (sheet->inline_style) { + 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] != '*') + sel->specificity = CSS_SPECIFICITY_D; + else + sel->specificity = 0; + } sel->data.comb = CSS_COMBINATOR_NONE; diff --git a/src/stylesheet.h b/src/stylesheet.h index 1fab87e..f3b2079 100644 --- a/src/stylesheet.h +++ b/src/stylesheet.h @@ -173,6 +173,8 @@ struct css_stylesheet { bool quirks_allowed; /**< Quirks permitted */ bool quirks_used; /**< Quirks actually used */ + bool inline_style; /**< Is an inline style */ + css_allocator_fn alloc; /**< Allocation function */ void *pw; /**< Private word */ }; -- cgit v1.2.3