diff --git a/src/parse/properties/utils.c b/src/parse/properties/utils.c index 687e8d9..01e1219 100644 --- a/src/parse/properties/utils.c +++ b/src/parse/properties/utils.c @@ -487,34 +487,75 @@ static bool parse_rgb( * * \param vector Vector of tokens to process * \param ctx Pointer to vector iteration context - * \param colour_channels Number of colour channels to expect * \param result Pointer to location to receive result (AARRGGBB) * \return true on success, false on error. */ static bool parse_hsl( const parserutils_vector *vector, int32_t *ctx, - int colour_channels, uint32_t *result) { const css_token *token; size_t consumed = 0; css_fixed hue, sat, lit; int32_t alpha = 255; + bool legacy = false; + css_error error; uint8_t r = 0, g = 0, b = 0, a = 0xff; /* hue is a number without a unit representing an - * angle (0-360) degrees + * angle (0-360) degrees, or it can be an angle dimension. */ consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); - if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER)) + if ((token == NULL) || + (token->type != CSS_TOKEN_NUMBER && + token->type != CSS_TOKEN_DIMENSION)) { return false; + } hue = css__number_from_lwc_string(token->idata, false, &consumed); - if (consumed != lwc_string_length(token->idata)) - return false; /* failed to consume the whole string as a number */ + + switch (token->type) { + case CSS_TOKEN_NUMBER: + if (consumed != lwc_string_length(token->idata)) { + return false; /* failed to consume the whole string as a number */ + } + break; + case CSS_TOKEN_DIMENSION: { + size_t len = lwc_string_length(token->idata); + const char *data = lwc_string_data(token->idata); + uint32_t unit = UNIT_DEG; + + error = css__parse_unit_keyword( + data + consumed, + len - consumed, + &unit); + if (error != CSS_OK) { + return false; + } + + switch (unit) { + case UNIT_DEG: + break; + case UNIT_RAD: + hue = FDIV(FMUL(hue, F_180), F_PI); + break; + case UNIT_GRAD: + hue = FMUL(hue, FLTTOFIX(0.9)); + break; + case UNIT_TURN: + hue = FMUL(hue, F_360); + break; + default: + return false; + } + } + break; + default: + return false; /* unexpected token type */ + } /* Normalise hue to the range [0, 360) */ while (hue < 0) @@ -524,21 +565,32 @@ static bool parse_hsl( consumeWhitespace(vector, ctx); - token = parserutils_vector_iterate(vector, ctx); - if (!tokenIsChar(token, ',')) + token = parserutils_vector_peek(vector, *ctx); + if (token == NULL) { return false; + } + if (tokenIsChar(token, ',')) { + parserutils_vector_iterate(vector, ctx); + consumeWhitespace(vector, ctx); + legacy = true; + } /* saturation */ - consumeWhitespace(vector, ctx); - token = parserutils_vector_iterate(vector, ctx); - if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE)) + if (token == NULL) return false; + if ((token->type != CSS_TOKEN_PERCENTAGE) && + (token->type != CSS_TOKEN_NUMBER || legacy)) { + return false; + } + sat = css__number_from_lwc_string(token->idata, false, &consumed); - if (consumed != lwc_string_length(token->idata)) - return false; /* failed to consume the whole string as a number */ + if (consumed != lwc_string_length(token->idata)) { + /* failed to consume the whole string as a number */ + return false; + } /* Normalise saturation to the range [0, 100] */ if (sat < INTTOFIX(0)) @@ -548,21 +600,30 @@ static bool parse_hsl( consumeWhitespace(vector, ctx); - token = parserutils_vector_iterate(vector, ctx); - if (!tokenIsChar(token, ',')) - return false; + if (legacy) { + token = parserutils_vector_iterate(vector, ctx); + if (token == NULL || !tokenIsChar(token, ',')) { + return false; + } + consumeWhitespace(vector, ctx); + } /* lightness */ - consumeWhitespace(vector, ctx); - token = parserutils_vector_iterate(vector, ctx); - if ((token == NULL) || (token->type != CSS_TOKEN_PERCENTAGE)) + if (token == NULL) return false; + if ((token->type != CSS_TOKEN_PERCENTAGE) && + (token->type != CSS_TOKEN_NUMBER || legacy)) { + return false; + } + lit = css__number_from_lwc_string(token->idata, false, &consumed); - if (consumed != lwc_string_length(token->idata)) - return false; /* failed to consume the whole string as a number */ + if (consumed != lwc_string_length(token->idata)) { + /* failed to consume the whole string as a number */ + return false; + } /* Normalise lightness to the range [0, 100] */ if (lit < INTTOFIX(0)) @@ -574,28 +635,32 @@ static bool parse_hsl( token = parserutils_vector_iterate(vector, ctx); - if (colour_channels == 6) { - /* alpha */ - - if (!tokenIsChar(token, ',')) - return false; - + if (( legacy && tokenIsChar(token, ',')) || + (!legacy && tokenIsChar(token, '/'))) { consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); - if ((token == NULL) || (token->type != CSS_TOKEN_NUMBER)) + if ((token == NULL) || + (token->type != CSS_TOKEN_NUMBER && + token->type != CSS_TOKEN_PERCENTAGE)) { return false; + } alpha = css__number_from_lwc_string(token->idata, false, &consumed); - if (consumed != lwc_string_length(token->idata)) - return false; /* failed to consume the whole string as a number */ + if (consumed != lwc_string_length(token->idata)) { + /* failed to consume the whole string as a number */ + return false; + } - alpha = FIXTOINT(FMUL(alpha, F_255)); + if (token->type != CSS_TOKEN_NUMBER) { + alpha = FIXTOINT(FMUL(alpha, F_255)); + } else { + alpha = FIXTOINT(FDIV(FMUL(alpha, F_255), F_100)); + } consumeWhitespace(vector, ctx); token = parserutils_vector_iterate(vector, ctx); - } if (!tokenIsChar(token, ')')) @@ -731,7 +796,7 @@ css_error css__parse_colour_specifier(css_language *c, goto invalid; } } else if (colour_channels == 5 || colour_channels == 6) { - if (!parse_hsl(vector, ctx, colour_channels, result)) { + if (!parse_hsl(vector, ctx, result)) { goto invalid; } } else {