/** * Create a string from a list of IDENT/S tokens * * \param c Parsing context * \param vector Vector containing tokens * \param ctx Vector iteration context * \param reserved Callback to determine if an identifier is reserved * \param result Pointer to location to receive resulting string * \return CSS_OK on success, appropriate error otherwise. * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. * * The resulting string's reference is passed to the caller */ css_error css__ident_list_to_string(css_language *c, const parserutils_vector *vector, int *ctx, bool (*reserved)(css_language *c, const css_token *ident), lwc_string **result) { int orig_ctx = *ctx; const css_token *token; css_error error = CSS_OK; parserutils_buffer *buffer; parserutils_error perror; lwc_string *interned; lwc_error lerror; perror = parserutils_buffer_create(&buffer); if (perror != PARSERUTILS_OK) return css_error_from_parserutils_error(perror); /* We know this token exists, and is an IDENT */ token = parserutils_vector_iterate(vector, ctx); /* Consume all subsequent IDENT or S tokens */ while (token != NULL && (token->type == CSS_TOKEN_IDENT || token->type == CSS_TOKEN_S)) { if (token->type == CSS_TOKEN_IDENT) { /* IDENT -- if reserved, reject style */ if (reserved != NULL && reserved(c, token)) { error = CSS_INVALID; goto cleanup; } perror = parserutils_buffer_append(buffer, (const uint8_t *) lwc_string_data(token->idata), lwc_string_length(token->idata)); } else { /* S */ perror = parserutils_buffer_append(buffer, (const uint8_t *) " ", 1); } if (perror != PARSERUTILS_OK) { error = css_error_from_parserutils_error(perror); goto cleanup; } token = parserutils_vector_iterate(vector, ctx); } /* Rewind context by one step if we consumed an unacceptable token */ if (token != NULL) *ctx = *ctx - 1; /* Strip trailing whitespace */ while (buffer->length > 0 && buffer->data[buffer->length - 1] == ' ') buffer->length--; /* Intern the buffer contents */ lerror = lwc_intern_string((char *) buffer->data, buffer->length, &interned); if (lerror != lwc_error_ok) { error = css_error_from_lwc_error(lerror); goto cleanup; } *result = interned; cleanup: parserutils_buffer_destroy(buffer); if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse a comma separated list, converting to bytecode * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param reserved Callback to determine if an identifier is reserved * \param get_value Callback to retrieve bytecode value for a token * \param style Pointer to output style * \return CSS_OK on success, * CSS_INVALID if the input is invalid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__comma_list_to_style(css_language *c, const parserutils_vector *vector, int *ctx, bool (*reserved)(css_language *c, const css_token *ident), css_code_t (*get_value)(css_language *c, const css_token *token, bool first), css_style *result) { int orig_ctx = *ctx; int prev_ctx = orig_ctx; const css_token *token; bool first = true; css_error error = CSS_OK; token = parserutils_vector_iterate(vector, ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } while (token != NULL) { if (token->type == CSS_TOKEN_IDENT) { css_code_t value = get_value(c, token, first); if (reserved(c, token) == false) { lwc_string *str = NULL; uint32_t snumber; *ctx = prev_ctx; error = css__ident_list_to_string(c, vector, ctx, reserved, &str); if (error != CSS_OK) goto cleanup; error = css__stylesheet_string_add(c->sheet, str, &snumber); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, value); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, snumber); if (error != CSS_OK) goto cleanup; } else { error = css__stylesheet_style_append(result, value); if (error != CSS_OK) goto cleanup; } } else if (token->type == CSS_TOKEN_STRING) { css_code_t value = get_value(c, token, first); uint32_t snumber; error = css__stylesheet_string_add(c->sheet, lwc_string_ref(token->idata), &snumber); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, value); if (error != CSS_OK) goto cleanup; error = css__stylesheet_style_append(result, snumber); if (error != CSS_OK) goto cleanup; } else { error = CSS_INVALID; goto cleanup; } consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token != NULL && tokenIsChar(token, ',')) { parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_STRING)) { error = CSS_INVALID; goto cleanup; } } else { break; } first = false; prev_ctx = *ctx; token = parserutils_vector_iterate(vector, ctx); } cleanup: if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse font_size * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_font_size(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error; const css_token *token; bool match; token = parserutils_vector_iterate(vector, ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { error = css_stylesheet_style_inherit(result, CSS_PROP_FONT_SIZE); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[XX_SMALL], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_XX_SMALL); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[X_SMALL], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_X_SMALL); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[SMALL], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_SMALL); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[MEDIUM], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_MEDIUM); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[LARGE], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_LARGE); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[X_LARGE], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_X_LARGE); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[XX_LARGE], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_XX_LARGE); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[LARGER], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_LARGER); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[SMALLER], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0,FONT_SIZE_SMALLER); } else { css_fixed length = 0; uint32_t unit = 0; *ctx = orig_ctx; error = css__parse_unit_specifier(c, vector, ctx, UNIT_PX, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if (unit&UNIT_ANGLE||unit&UNIT_TIME||unit&UNIT_FREQ) { *ctx = orig_ctx; return CSS_INVALID; } if (length <0) { *ctx = orig_ctx; return CSS_INVALID; } error = css__stylesheet_style_appendOPV(result, CSS_PROP_FONT_SIZE, 0, FONT_SIZE_DIMENSION); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_vappend(result, 2, length, unit); } if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse clip * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_clip(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error; const css_token *token; int num_lengths = 0; css_fixed length[4] = { 0 }; uint32_t unit[4] = { 0 }; bool match; /* FUNCTION(rect) [ [ IDENT(auto) | length ] CHAR(,)? ]{3} * [ IDENT(auto) | length ] CHAR{)} | * IDENT(auto, inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_CLIP, FLAG_INHERIT, 0); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_CLIP, 0, CLIP_AUTO); } else if ((token->type == CSS_TOKEN_FUNCTION) && (lwc_string_caseless_isequal( token->idata, c->strings[RECT], &match) == lwc_error_ok && match)) { int i; uint16_t value = CLIP_SHAPE_RECT; for (i = 0; i < 4; i++) { consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT) { /* Slightly magical way of generating the auto * values. These are bits 3-6 of the value. */ if ((lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) value |= 1 << (i + 3); else { *ctx = orig_ctx; return CSS_INVALID; } parserutils_vector_iterate(vector, ctx); } else { error = css__parse_unit_specifier(c, vector, ctx, UNIT_PX, &length[num_lengths], &unit[num_lengths]); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if (unit[num_lengths] & UNIT_ANGLE || unit[num_lengths] & UNIT_TIME || unit[num_lengths] & UNIT_FREQ || unit[num_lengths] & UNIT_PCT) { *ctx = orig_ctx; return CSS_INVALID; } num_lengths++; } consumeWhitespace(vector, ctx); /* Consume optional comma after first 3 parameters */ if (i < 3) { token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (tokenIsChar(token, ',')) parserutils_vector_iterate(vector, ctx); } } consumeWhitespace(vector, ctx); /* Finally, consume closing parenthesis */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ')') == false) { *ctx = orig_ctx; return CSS_INVALID; } /* output bytecode */ error = css__stylesheet_style_appendOPV(result, CSS_PROP_CLIP, 0, value); if (error != CSS_OK) { *ctx = orig_ctx; return error; } for (i = 0; i < num_lengths; i++) { error = css__stylesheet_style_vappend(result, 2, length[i], unit[i]); if (error != CSS_OK) break; } } else { error = CSS_INVALID; } if (error != CSS_OK) { *ctx = orig_ctx; } return error; }
/** * Parse display * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error parse_display(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *ident; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; bool match; /* IDENT (inline, block, list-item, run-in, inline-block, table, * inline-table, table-row-group, table-header-group, * table-footer-group, table-row, table-column-group, table-column, * table-cell, table-caption, none, inherit) */ ident = parserutils_vector_iterate(vector, ctx); if (ident == NULL || ident->type != CSS_TOKEN_IDENT) { *ctx = orig_ctx; return CSS_INVALID; } if ((lwc_string_caseless_isequal( ident->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { flags |= FLAG_INHERIT; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[INLINE], &match) == lwc_error_ok && match)) { value = DISPLAY_INLINE; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[BLOCK], &match) == lwc_error_ok && match)) { value = DISPLAY_BLOCK; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[LIST_ITEM], &match) == lwc_error_ok && match)) { value = DISPLAY_LIST_ITEM; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[RUN_IN], &match) == lwc_error_ok && match)) { value = DISPLAY_RUN_IN; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[INLINE_BLOCK], &match) == lwc_error_ok && match)) { value = DISPLAY_INLINE_BLOCK; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[INLINE_TABLE], &match) == lwc_error_ok && match)) { value = DISPLAY_INLINE_TABLE; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_ROW_GROUP], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_ROW_GROUP; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_HEADER_GROUP], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_HEADER_GROUP; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_FOOTER_GROUP], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_FOOTER_GROUP; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_ROW], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_ROW; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_COLUMN_GROUP], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_COLUMN_GROUP; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_COLUMN], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_COLUMN; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_CELL], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_CELL; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[TABLE_CAPTION], &match) == lwc_error_ok && match)) { value = DISPLAY_TABLE_CAPTION; } else if ((lwc_string_caseless_isequal( ident->idata, c->strings[NONE], &match) == lwc_error_ok && match)) { value = DISPLAY_NONE; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_DISPLAY, flags, value); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, sizeof(opv), result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); return CSS_OK; }
/** * Parse width * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error parse_width(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *token; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; bool match; /* length | percentage | IDENT(auto, inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) { parserutils_vector_iterate(vector, ctx); value = WIDTH_AUTO; } else { error = parse_unit_specifier(c, vector, ctx, UNIT_PX, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if (unit & UNIT_ANGLE || unit & UNIT_TIME || unit & UNIT_FREQ) { *ctx = orig_ctx; return CSS_INVALID; } /* Must be positive */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = WIDTH_SET; } opv = buildOPV(CSS_PROP_WIDTH, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == WIDTH_SET) required_size += sizeof(length) + sizeof(unit); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, required_size, result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); if ((flags & FLAG_INHERIT) == false && value == WIDTH_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); } return CSS_OK; }
/** * Parse play-during * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_play_during(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error; const css_token *token; uint8_t flags = 0; uint16_t value = 0; lwc_string *uri; bool match; uint32_t uri_snumber; /* URI [ IDENT(mix) || IDENT(repeat) ]? | IDENT(auto,none,inherit) */ token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || ((token->type != CSS_TOKEN_IDENT) && (token->type != CSS_TOKEN_URI))) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT) { if ((lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { flags |= FLAG_INHERIT; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[NONE], &match) == lwc_error_ok && match)) { value = PLAY_DURING_NONE; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) { value = PLAY_DURING_AUTO; } else { *ctx = orig_ctx; return CSS_INVALID; } } else { int modifiers; value = PLAY_DURING_URI; error = c->sheet->resolve(c->sheet->resolve_pw, c->sheet->url, token->idata, &uri); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_string_add(c->sheet, uri, &uri_snumber); if (error != CSS_OK) { *ctx = orig_ctx; return error; } for (modifiers = 0; modifiers < 2; modifiers++) { consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token != NULL && token->type == CSS_TOKEN_IDENT) { if ((lwc_string_caseless_isequal( token->idata, c->strings[MIX], &match) == lwc_error_ok && match)) { if ((value & PLAY_DURING_MIX) == 0) value |= PLAY_DURING_MIX; else { *ctx = orig_ctx; return CSS_INVALID; } } else if (lwc_string_caseless_isequal( token->idata, c->strings[REPEAT], &match) == lwc_error_ok && match) { if ((value & PLAY_DURING_REPEAT) == 0) value |= PLAY_DURING_REPEAT; else { *ctx = orig_ctx; return CSS_INVALID; } } else { *ctx = orig_ctx; return CSS_INVALID; } parserutils_vector_iterate(vector, ctx); } } } error = css__stylesheet_style_appendOPV(result, CSS_PROP_PLAY_DURING, flags, value); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if ((flags & FLAG_INHERIT) == false && (value & PLAY_DURING_TYPE_MASK) == PLAY_DURING_URI) { error = css__stylesheet_style_append(result, uri_snumber); } if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse z-index * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error parse_z_index(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *token; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; css_fixed num = 0; uint32_t required_size; bool match; /* <integer> | IDENT (auto, inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_NUMBER)) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { flags |= FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) { value = Z_INDEX_AUTO; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; num = number_from_lwc_string(token->idata, true, &consumed); /* Invalid if there are trailing characters */ if (consumed != lwc_string_length(token->idata)) { *ctx = orig_ctx; return CSS_INVALID; } value = Z_INDEX_SET; } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_Z_INDEX, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == Z_INDEX_SET) required_size += sizeof(num); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, required_size, result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); if ((flags & FLAG_INHERIT) == false && value == Z_INDEX_SET) { memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &num, sizeof(num)); } return CSS_OK; }
/** * Parse border-spacing * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_border_spacing(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error; const css_token *token; css_fixed length[2] = { 0 }; uint32_t unit[2] = { 0 }; bool match; /* length length? | IDENT(inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { parserutils_vector_iterate(vector, ctx); /* inherit */ error = css__stylesheet_style_appendOPV(result, CSS_PROP_BORDER_SPACING, FLAG_INHERIT, 0); } else { int num_lengths = 0; error = css__parse_unit_specifier(c, vector, ctx, UNIT_PX, &length[0], &unit[0]); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if (unit[0] & UNIT_ANGLE || unit[0] & UNIT_TIME || unit[0] & UNIT_FREQ || unit[0] & UNIT_PCT) { *ctx = orig_ctx; return CSS_INVALID; } num_lengths = 1; consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token != NULL) { /* Attempt second length, ignoring errors. * The core !important parser will ensure * any remaining junk is thrown out. * Ctx will be preserved on error, as usual */ error = css__parse_unit_specifier(c, vector, ctx, UNIT_PX, &length[1], &unit[1]); if (error == CSS_OK) { if (unit[1] & UNIT_ANGLE || unit[1] & UNIT_TIME || unit[1] & UNIT_FREQ || unit[1] & UNIT_PCT) { *ctx = orig_ctx; return CSS_INVALID; } num_lengths = 2; } } if (num_lengths == 1) { /* Only one length specified. Use for both axes. */ length[1] = length[0]; unit[1] = unit[0]; } /* Lengths must not be negative */ if (length[0] < 0 || length[1] < 0) { *ctx = orig_ctx; return CSS_INVALID; } error = css__stylesheet_style_appendOPV(result, CSS_PROP_BORDER_SPACING, 0, BORDER_SPACING_SET); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_vappend(result, 4, length[0], unit[0], length[1], unit[1]); } if (error != CSS_OK) { *ctx = orig_ctx; return error; } return CSS_OK; }
/** * Parse border-color shorthand * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_border_color(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; int prev_ctx; const css_token *token; uint16_t side_val[4]; uint32_t side_color[4]; uint32_t side_count = 0; css_error error; /* Firstly, handle inherit */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) return CSS_INVALID; if (is_css_inherit(c, token)) { error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_COLOR); if (error != CSS_OK) return error; error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_RIGHT_COLOR); if (error != CSS_OK) return error; error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_BOTTOM_COLOR); if (error != CSS_OK) return error; error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_LEFT_COLOR); if (error == CSS_OK) parserutils_vector_iterate(vector, ctx); return error; } /* Attempt to parse up to 4 colours */ do { prev_ctx = *ctx; if ((token != NULL) && is_css_inherit(c, token)) { *ctx = orig_ctx; return CSS_INVALID; } error = css__parse_colour_specifier(c, vector, ctx, &side_val[side_count], &side_color[side_count]); if (error == CSS_OK) { side_count++; consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); } else { /* Forcibly cause loop to exit */ token = NULL; } } while ((*ctx != prev_ctx) && (token != NULL) && (side_count < 4)); #define SIDE_APPEND(OP,NUM) \ error = css__stylesheet_style_appendOPV(result, (OP), 0, side_val[(NUM)]); \ if (error != CSS_OK) \ break; \ if (side_val[(NUM)] == BORDER_COLOR_SET) \ error = css__stylesheet_style_append(result, side_color[(NUM)]); \ if (error != CSS_OK) \ break; switch (side_count) { case 1: SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 0); break; case 2: SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 1); SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 1); break; case 3: SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 1); SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 2); SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 1); break; case 4: SIDE_APPEND(CSS_PROP_BORDER_TOP_COLOR, 0); SIDE_APPEND(CSS_PROP_BORDER_RIGHT_COLOR, 1); SIDE_APPEND(CSS_PROP_BORDER_BOTTOM_COLOR, 2); SIDE_APPEND(CSS_PROP_BORDER_LEFT_COLOR, 3); break; default: error = CSS_INVALID; break; } if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse speech_rate * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_speech_rate(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error; const css_token *token; bool match; token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || ((token->type != CSS_TOKEN_IDENT) && (token->type != CSS_TOKEN_NUMBER))) { *ctx = orig_ctx; return CSS_INVALID; } if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { error = css_stylesheet_style_inherit(result, CSS_PROP_SPEECH_RATE); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[X_SLOW], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_X_SLOW); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[SLOW], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_SLOW); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[MEDIUM], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_MEDIUM); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[FAST], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_FAST); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[X_FAST], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_X_FAST); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[FASTER], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_FASTER); } else if ((token->type == CSS_TOKEN_IDENT) && (lwc_string_caseless_isequal(token->idata, c->strings[SLOWER], &match) == lwc_error_ok && match)) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0,SPEECH_RATE_SLOWER); } else if (token->type == CSS_TOKEN_NUMBER) { css_fixed num = 0; size_t consumed = 0; num = css__number_from_lwc_string(token->idata, false, &consumed); /* Invalid if there are trailing characters */ if (consumed != lwc_string_length(token->idata)) { *ctx = orig_ctx; return CSS_INVALID; } if (num<0) { *ctx = orig_ctx; return CSS_INVALID; } error = css__stylesheet_style_appendOPV(result, CSS_PROP_SPEECH_RATE, 0, SPEECH_RATE_SET); if (error != CSS_OK) { *ctx = orig_ctx; return error; } error = css__stylesheet_style_append(result, num); } else { error = CSS_INVALID; } if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse text-decoration * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_text_decoration(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; css_error error = CSS_INVALID; const css_token *token; bool match; /* IDENT([ underline || overline || line-through || blink ]) * | IDENT (none, inherit) */ token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_IDENT) ) { *ctx = orig_ctx; return CSS_INVALID; } if (lwc_string_caseless_isequal(token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match) { error = css_stylesheet_style_inherit(result, CSS_PROP_TEXT_DECORATION); } else if (lwc_string_caseless_isequal(token->idata, c->strings[NONE], &match) == lwc_error_ok && match) { error = css__stylesheet_style_appendOPV(result, CSS_PROP_TEXT_DECORATION, 0, TEXT_DECORATION_NONE); } else { uint16_t value = 0; while (token != NULL) { if ((lwc_string_caseless_isequal( token->idata, c->strings[UNDERLINE], &match) == lwc_error_ok && match)) { if ((value & TEXT_DECORATION_UNDERLINE) == 0) value |= TEXT_DECORATION_UNDERLINE; else { *ctx = orig_ctx; return CSS_INVALID; } } else if ((lwc_string_caseless_isequal( token->idata, c->strings[OVERLINE], &match) == lwc_error_ok && match)) { if ((value & TEXT_DECORATION_OVERLINE) == 0) value |= TEXT_DECORATION_OVERLINE; else { *ctx = orig_ctx; return CSS_INVALID; } } else if ((lwc_string_caseless_isequal( token->idata, c->strings[LINE_THROUGH], &match) == lwc_error_ok && match)) { if ((value & TEXT_DECORATION_LINE_THROUGH) == 0) value |= TEXT_DECORATION_LINE_THROUGH; else { *ctx = orig_ctx; return CSS_INVALID; } } else if ((lwc_string_caseless_isequal( token->idata, c->strings[BLINK], &match) == lwc_error_ok && match)) { if ((value & TEXT_DECORATION_BLINK) == 0) value |= TEXT_DECORATION_BLINK; else { *ctx = orig_ctx; return CSS_INVALID; } } else { *ctx = orig_ctx; return CSS_INVALID; } consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token != NULL && token->type != CSS_TOKEN_IDENT) break; token = parserutils_vector_iterate(vector, ctx); } error = css__stylesheet_style_appendOPV(result, CSS_PROP_TEXT_DECORATION, 0, value); } if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse columns shorthand * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_columns(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result) { int orig_ctx = *ctx; int prev_ctx; const css_token *token; css_error error = CSS_OK; bool width = true; bool count = true; css_style *width_style; css_style *count_style; /* Firstly, handle inherit */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) return CSS_INVALID; if (is_css_inherit(c, token)) { error = css_stylesheet_style_inherit(result, CSS_PROP_COLUMN_WIDTH); if (error != CSS_OK) return error; error = css_stylesheet_style_inherit(result, CSS_PROP_COLUMN_COUNT); if (error == CSS_OK) parserutils_vector_iterate(vector, ctx); return error; } /* Allocate for styles */ error = css__stylesheet_style_create(c->sheet, &width_style); if (error != CSS_OK) return error; error = css__stylesheet_style_create(c->sheet, &count_style); if (error != CSS_OK) { css__stylesheet_style_destroy(width_style); return error; } /* Attempt to parse the various longhand properties */ do { prev_ctx = *ctx; error = CSS_OK; if (is_css_inherit(c, token)) { /* Can't have another inherit */ error = CSS_INVALID; goto css__parse_columns_cleanup; } /* Try each property parser in turn, but only if we * haven't already got a value for this property. */ if ((width) && (error = css__parse_column_width(c, vector, ctx, width_style)) == CSS_OK) { width = false; } else if ((count) && (error = css__parse_column_count(c, vector, ctx, count_style)) == CSS_OK) { count = false; } if (error == CSS_OK) { consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); } else { /* Forcibly cause loop to exit */ token = NULL; } } while (*ctx != prev_ctx && token != NULL); /* Set unset properties to initial values */ if (width) { error = css__stylesheet_style_appendOPV(width_style, CSS_PROP_COLUMN_WIDTH, 0, COLUMN_WIDTH_AUTO); if (error != CSS_OK) goto css__parse_columns_cleanup; } if (count) { error = css__stylesheet_style_appendOPV(count_style, CSS_PROP_COLUMN_COUNT, 0, COLUMN_COUNT_AUTO); if (error != CSS_OK) goto css__parse_columns_cleanup; } /* Merge styles into the result */ error = css__stylesheet_merge_style(result, width_style); if (error != CSS_OK) goto css__parse_columns_cleanup; error = css__stylesheet_merge_style(result, count_style); css__parse_columns_cleanup: css__stylesheet_style_destroy(width_style); css__stylesheet_style_destroy(count_style); if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse border-{top,right,bottom,left} shorthand * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param side The side we're parsing for * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_border_side(css_language *c, const parserutils_vector *vector, int *ctx, css_style *result, enum border_side_e side) { int orig_ctx = *ctx; int prev_ctx; const css_token *token; css_error error = CSS_OK; bool color = true; bool style = true; bool width = true; css_style *color_style; css_style *style_style; css_style *width_style; /* Firstly, handle inherit */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) return CSS_INVALID; if (is_css_inherit(c, token)) { error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_COLOR + side); if (error != CSS_OK) return error; error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_STYLE + side); if (error != CSS_OK) return error; error = css_stylesheet_style_inherit(result, CSS_PROP_BORDER_TOP_WIDTH + side); if (error == CSS_OK) parserutils_vector_iterate(vector, ctx); return error; } /* allocate styles */ error = css__stylesheet_style_create(c->sheet, &color_style); if (error != CSS_OK) return error; error = css__stylesheet_style_create(c->sheet, &style_style); if (error != CSS_OK) { css__stylesheet_style_destroy(color_style); return error; } error = css__stylesheet_style_create(c->sheet, &width_style); if (error != CSS_OK) { css__stylesheet_style_destroy(color_style); css__stylesheet_style_destroy(style_style); return error; } /* Attempt to parse the various longhand properties */ do { prev_ctx = *ctx; error = CSS_OK; /* Ensure that we're not about to parse another inherit */ token = parserutils_vector_peek(vector, *ctx); if (token != NULL && is_css_inherit(c, token)) { error = CSS_INVALID; goto css__parse_border_side_cleanup; } /* Try each property parser in turn, but only if we * haven't already got a value for this property. */ if ((color) && (error = css__parse_border_side_color(c, vector, ctx, color_style, CSS_PROP_BORDER_TOP_COLOR + side)) == CSS_OK) { color = false; } else if ((style) && (error = css__parse_border_side_style(c, vector, ctx, style_style, CSS_PROP_BORDER_TOP_STYLE + side)) == CSS_OK) { style = false; } else if ((width) && (error = css__parse_border_side_width(c, vector, ctx, width_style, CSS_PROP_BORDER_TOP_WIDTH + side)) == CSS_OK) { width = false; } if (error == CSS_OK) { consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); } else { /* Forcibly cause loop to exit */ token = NULL; } } while (*ctx != prev_ctx && token != NULL); if (style) { error = css__stylesheet_style_appendOPV(style_style, CSS_PROP_BORDER_TOP_STYLE + side, 0, BORDER_STYLE_NONE); if (error != CSS_OK) goto css__parse_border_side_cleanup; } if (width) { error = css__stylesheet_style_appendOPV(width_style, CSS_PROP_BORDER_TOP_WIDTH + side, 0, BORDER_WIDTH_MEDIUM); if (error != CSS_OK) goto css__parse_border_side_cleanup; } if (color) { error = css__stylesheet_style_appendOPV(color_style, CSS_PROP_BORDER_TOP_COLOR + side, 0, BORDER_COLOR_CURRENT_COLOR); if (error != CSS_OK) goto css__parse_border_side_cleanup; } error = css__stylesheet_merge_style(result, color_style); if (error != CSS_OK) goto css__parse_border_side_cleanup; error = css__stylesheet_merge_style(result, style_style); if (error != CSS_OK) goto css__parse_border_side_cleanup; error = css__stylesheet_merge_style(result, width_style); css__parse_border_side_cleanup: css__stylesheet_style_destroy(color_style); css__stylesheet_style_destroy(style_style); css__stylesheet_style_destroy(width_style); if (error != CSS_OK) *ctx = orig_ctx; return error; }
/** * Parse line-height * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error parse_line_height(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *token; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; css_fixed length = 0; uint32_t unit = 0; uint32_t required_size; bool match; /* number | length | percentage | IDENT(normal, inherit) */ token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { parserutils_vector_iterate(vector, ctx); flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[NORMAL], &match) == lwc_error_ok && match)) { parserutils_vector_iterate(vector, ctx); value = LINE_HEIGHT_NORMAL; } else if (token->type == CSS_TOKEN_NUMBER) { size_t consumed = 0; length = number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) { *ctx = orig_ctx; return CSS_INVALID; } /* Negative values are illegal */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } parserutils_vector_iterate(vector, ctx); value = LINE_HEIGHT_NUMBER; } else { error = parse_unit_specifier(c, vector, ctx, UNIT_PX, &length, &unit); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if (unit & UNIT_ANGLE || unit & UNIT_TIME || unit & UNIT_FREQ) { *ctx = orig_ctx; return CSS_INVALID; } /* Negative values are illegal */ if (length < 0) { *ctx = orig_ctx; return CSS_INVALID; } value = LINE_HEIGHT_DIMENSION; } opv = buildOPV(CSS_PROP_LINE_HEIGHT, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && value == LINE_HEIGHT_NUMBER) required_size += sizeof(length); else if ((flags & FLAG_INHERIT) == false && value == LINE_HEIGHT_DIMENSION) required_size += sizeof(length) + sizeof(unit); /* Allocate result */ error = css_stylesheet_style_create(c->sheet, required_size, result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); if ((flags & FLAG_INHERIT) == false && (value == LINE_HEIGHT_NUMBER || value == LINE_HEIGHT_DIMENSION)) memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv), &length, sizeof(length)); if ((flags & FLAG_INHERIT) == false && value == LINE_HEIGHT_DIMENSION) memcpy(((uint8_t *) (*result)->bytecode) + sizeof(opv) + sizeof(length), &unit, sizeof(unit)); return CSS_OK; }
/** * Parse a colour specifier * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param value Pointer to location to receive value * \param result Pointer to location to receive result (AARRGGBB) * \return CSS_OK on success, * CSS_INVALID if the input is invalid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error css__parse_colour_specifier(css_language *c, const parserutils_vector *vector, int *ctx, uint16_t *value, uint32_t *result) { int orig_ctx = *ctx; const css_token *token; bool match; css_error error; consumeWhitespace(vector, ctx); /* IDENT(<colour name>) | * HASH(rgb | rrggbb) | * FUNCTION(rgb) [ [ NUMBER | PERCENTAGE ] ',' ] {3} ')' * FUNCTION(rgba) [ [ NUMBER | PERCENTAGE ] ',' ] {4} ')' * FUNCTION(hsl) ANGLE ',' PERCENTAGE ',' PERCENTAGE ')' * FUNCTION(hsla) ANGLE ',' PERCENTAGE ',' PERCENTAGE ',' NUMBER ')' * * For quirks, NUMBER | DIMENSION | IDENT, too * I.E. "123456" -> NUMBER, "1234f0" -> DIMENSION, "f00000" -> IDENT */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || (token->type != CSS_TOKEN_IDENT && token->type != CSS_TOKEN_HASH && token->type != CSS_TOKEN_FUNCTION)) { if (c->sheet->quirks_allowed == false || token == NULL || (token->type != CSS_TOKEN_NUMBER && token->type != CSS_TOKEN_DIMENSION)) goto invalid; } if (token->type == CSS_TOKEN_IDENT) { if ((lwc_string_caseless_isequal( token->idata, c->strings[TRANSPARENT], &match) == lwc_error_ok && match)) { *value = COLOR_TRANSPARENT; *result = 0; /* black transparent */ return CSS_OK; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[CURRENTCOLOR], &match) == lwc_error_ok && match)) { *value = COLOR_CURRENT_COLOR; *result = 0; return CSS_OK; } error = css__parse_named_colour(c, token->idata, result); if (error != CSS_OK && c->sheet->quirks_allowed) { error = css__parse_hash_colour(token->idata, result); if (error == CSS_OK) c->sheet->quirks_used = true; } if (error != CSS_OK) goto invalid; } else if (token->type == CSS_TOKEN_HASH) { error = css__parse_hash_colour(token->idata, result); if (error != CSS_OK) goto invalid; } else if (c->sheet->quirks_allowed && token->type == CSS_TOKEN_NUMBER) { error = css__parse_hash_colour(token->idata, result); if (error == CSS_OK) c->sheet->quirks_used = true; else goto invalid; } else if (c->sheet->quirks_allowed && token->type == CSS_TOKEN_DIMENSION) { error = css__parse_hash_colour(token->idata, result); if (error == CSS_OK) c->sheet->quirks_used = true; else goto invalid; } else if (token->type == CSS_TOKEN_FUNCTION) { uint8_t r = 0, g = 0, b = 0, a = 0xff; int colour_channels = 0; if ((lwc_string_caseless_isequal( token->idata, c->strings[RGB], &match) == lwc_error_ok && match)) { colour_channels = 3; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[RGBA], &match) == lwc_error_ok && match)) { colour_channels = 4; } if ((lwc_string_caseless_isequal( token->idata, c->strings[HSL], &match) == lwc_error_ok && match)) { colour_channels = 5; } else if ((lwc_string_caseless_isequal( token->idata, c->strings[HSLA], &match) == lwc_error_ok && match)) { colour_channels = 6; } if (colour_channels == 3 || colour_channels == 4) { int i; css_token_type valid = CSS_TOKEN_NUMBER; uint8_t *components[4] = { &r, &g, &b, &a }; for (i = 0; i < colour_channels; i++) { uint8_t *component; css_fixed num; size_t consumed = 0; int32_t intval; bool int_only; component = components[i]; consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL || (token->type != CSS_TOKEN_NUMBER && token->type != CSS_TOKEN_PERCENTAGE)) goto invalid; if (i == 0) valid = token->type; else if (i < 3 && token->type != valid) goto invalid; /* The alpha channel may be a float */ if (i < 3) int_only = (valid == CSS_TOKEN_NUMBER); else int_only = false; num = css__number_from_lwc_string(token->idata, int_only, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; if (valid == CSS_TOKEN_NUMBER) { if (i == 3) { /* alpha channel */ intval = FIXTOINT( FMUL(num, F_255)); } else { /* colour channels */ intval = FIXTOINT(num); } } else { intval = FIXTOINT( FDIV(FMUL(num, F_255), F_100)); } if (intval > 255) *component = 255; else if (intval < 0) *component = 0; else *component = intval; parserutils_vector_iterate(vector, ctx); consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL) goto invalid; if (i != (colour_channels - 1) && tokenIsChar(token, ',')) { parserutils_vector_iterate(vector, ctx); } else if (i == (colour_channels - 1) && tokenIsChar(token, ')')) { parserutils_vector_iterate(vector, ctx); } else { goto invalid; } } } else if (colour_channels == 5 || colour_channels == 6) { /* hue - saturation - lightness */ size_t consumed = 0; css_fixed hue, sat, lit; int32_t alpha = 255; /* hue is a number without a unit representing an * angle (0-360) degrees */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER)) goto invalid; hue = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ /* Normalise hue to the range [0, 360) */ while (hue < 0) hue += F_360; while (hue >= F_360) hue -= F_360; consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if (!tokenIsChar(token, ',')) goto invalid; /* saturation */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE)) goto invalid; sat = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ /* Normalise saturation to the range [0, 100] */ if (sat < INTTOFIX(0)) sat = INTTOFIX(0); else if (sat > INTTOFIX(100)) sat = INTTOFIX(100); consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if (!tokenIsChar(token, ',')) goto invalid; /* lightness */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE)) goto invalid; lit = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ /* Normalise lightness to the range [0, 100] */ if (lit < INTTOFIX(0)) lit = INTTOFIX(0); else if (lit > INTTOFIX(100)) lit = INTTOFIX(100); consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if (colour_channels == 6) { /* alpha */ if (!tokenIsChar(token, ',')) goto invalid; consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER)) goto invalid; alpha = css__number_from_lwc_string(token->idata, false, &consumed); if (consumed != lwc_string_length(token->idata)) goto invalid; /* failed to consume the whole string as a number */ alpha = FIXTOINT(FMUL(alpha, F_255)); consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); } if (!tokenIsChar(token, ')')) goto invalid; /* have a valid HSV entry, convert to RGB */ HSL_to_RGB(hue, sat, lit, &r, &g, &b); /* apply alpha */ if (alpha > 255) a = 255; else if (alpha < 0) a = 0; else a = alpha; } else { goto invalid; } *result = (a << 24) | (r << 16) | (g << 8) | b; } *value = COLOR_SET; return CSS_OK; invalid: *ctx = orig_ctx; return CSS_INVALID; }
/** * Parse clip * * \param c Parsing context * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context * \param result Pointer to location to receive resulting style * \return CSS_OK on success, * CSS_NOMEM on memory exhaustion, * CSS_INVALID if the input is not valid * * Post condition: \a *ctx is updated with the next token to process * If the input is invalid, then \a *ctx remains unchanged. */ css_error parse_clip(css_language *c, const parserutils_vector *vector, int *ctx, css_style **result) { int orig_ctx = *ctx; css_error error; const css_token *token; uint8_t flags = 0; uint16_t value = 0; uint32_t opv; int num_lengths = 0; css_fixed length[4] = { 0 }; uint32_t unit[4] = { 0 }; uint32_t required_size; bool match; /* FUNCTION(rect) [ [ IDENT(auto) | length ] CHAR(,)? ]{3} * [ IDENT(auto) | length ] CHAR{)} | * IDENT(auto, inherit) */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[INHERIT], &match) == lwc_error_ok && match)) { flags = FLAG_INHERIT; } else if (token->type == CSS_TOKEN_IDENT && (lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) { value = CLIP_AUTO; } else if (token->type == CSS_TOKEN_FUNCTION && (lwc_string_caseless_isequal( token->idata, c->strings[RECT], &match) == lwc_error_ok && match)) { int i; value = CLIP_SHAPE_RECT; for (i = 0; i < 4; i++) { consumeWhitespace(vector, ctx); token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (token->type == CSS_TOKEN_IDENT) { /* Slightly magical way of generating the auto * values. These are bits 3-6 of the value. */ if ((lwc_string_caseless_isequal( token->idata, c->strings[AUTO], &match) == lwc_error_ok && match)) value |= 1 << (i + 3); else { *ctx = orig_ctx; return CSS_INVALID; } parserutils_vector_iterate(vector, ctx); } else { error = parse_unit_specifier(c, vector, ctx, UNIT_PX, &length[num_lengths], &unit[num_lengths]); if (error != CSS_OK) { *ctx = orig_ctx; return error; } if (unit[num_lengths] & UNIT_ANGLE || unit[num_lengths] & UNIT_TIME || unit[num_lengths] & UNIT_FREQ || unit[num_lengths] & UNIT_PCT) { *ctx = orig_ctx; return CSS_INVALID; } num_lengths++; } consumeWhitespace(vector, ctx); /* Consume optional comma after first 3 parameters */ if (i < 3) { token = parserutils_vector_peek(vector, *ctx); if (token == NULL) { *ctx = orig_ctx; return CSS_INVALID; } if (tokenIsChar(token, ',')) parserutils_vector_iterate(vector, ctx); } } consumeWhitespace(vector, ctx); /* Finally, consume closing parenthesis */ token = parserutils_vector_iterate(vector, ctx); if (token == NULL || tokenIsChar(token, ')') == false) { *ctx = orig_ctx; return CSS_INVALID; } } else { *ctx = orig_ctx; return CSS_INVALID; } opv = buildOPV(CSS_PROP_CLIP, flags, value); required_size = sizeof(opv); if ((flags & FLAG_INHERIT) == false && (value & CLIP_SHAPE_MASK) == CLIP_SHAPE_RECT) { required_size += num_lengths * (sizeof(length[0]) + sizeof(unit[0])); } /* Allocate result */ error = css_stylesheet_style_create(c->sheet, required_size, result); if (error != CSS_OK) { *ctx = orig_ctx; return error; } /* Copy the bytecode to it */ memcpy((*result)->bytecode, &opv, sizeof(opv)); if ((flags & FLAG_INHERIT) == false && (value & CLIP_SHAPE_MASK) == CLIP_SHAPE_RECT) { int i; uint8_t *ptr = ((uint8_t *) (*result)->bytecode) + sizeof(opv); for (i = 0; i < num_lengths; i++) { memcpy(ptr, &length[i], sizeof(length[i])); ptr += sizeof(length[i]); memcpy(ptr, &unit[i], sizeof(unit[i])); ptr += sizeof(unit[i]); } } return CSS_OK; }