From f8c906e4c6ff36cefe0803cba6b2a083efe5bcb6 Mon Sep 17 00:00:00 2001 From: John-Mark Bell Date: Sun, 20 Nov 2016 13:10:02 +0000 Subject: Media Queries: datastructures and plumbing. No parse implementation as yet. Selection hasn't been updated, either. One item of note in that area is that a client currently provides the media for top-level sheets being added to a selection context. This probably needs to change to providing a lwc_string containing the verbatim media query from the containing document's import mechanism. That way, the internal representation of media queries can remain opaque to clients. --- include/libcss/stylesheet.h | 5 +- src/parse/Makefile | 2 +- src/parse/language.c | 115 +++++++++----------------------------------- src/parse/mq.c | 96 ++++++++++++++++++++++++++++++++++++ src/parse/mq.h | 100 ++++++++++++++++++++++++++++++++++++++ src/stylesheet.c | 15 +++--- src/stylesheet.h | 9 ++-- 7 files changed, 233 insertions(+), 109 deletions(-) create mode 100644 src/parse/mq.c create mode 100644 src/parse/mq.h diff --git a/include/libcss/stylesheet.h b/include/libcss/stylesheet.h index 68c4dfc..542f199 100644 --- a/include/libcss/stylesheet.h +++ b/include/libcss/stylesheet.h @@ -36,7 +36,6 @@ typedef css_error (*css_url_resolution_fn)(void *pw, * \param pw Client data * \param parent Stylesheet requesting the import * \param url URL of the imported sheet - * \param media Applicable media for the imported sheet * \return CSS_OK on success, appropriate error otherwise * * \note This function will be invoked for notification purposes @@ -46,7 +45,7 @@ typedef css_error (*css_url_resolution_fn)(void *pw, * registration API. */ typedef css_error (*css_import_notification_fn)(void *pw, - css_stylesheet *parent, lwc_string *url, uint64_t media); + css_stylesheet *parent, lwc_string *url); /** * Callback use to resolve system colour names to RGB values @@ -145,7 +144,7 @@ css_error css_stylesheet_append_data(css_stylesheet *sheet, css_error css_stylesheet_data_done(css_stylesheet *sheet); css_error css_stylesheet_next_pending_import(css_stylesheet *parent, - lwc_string **url, uint64_t *media); + lwc_string **url); css_error css_stylesheet_register_import(css_stylesheet *parent, css_stylesheet *child); diff --git a/src/parse/Makefile b/src/parse/Makefile index 99f55d0..6d11096 100644 --- a/src/parse/Makefile +++ b/src/parse/Makefile @@ -1,4 +1,4 @@ # Sources -DIR_SOURCES := parse.c language.c important.c propstrings.c font_face.c +DIR_SOURCES := parse.c language.c important.c propstrings.c font_face.c mq.c include $(NSBUILD)/Makefile.subdir diff --git a/src/parse/language.c b/src/parse/language.c index 9af2547..a5f57d3 100644 --- a/src/parse/language.c +++ b/src/parse/language.c @@ -15,6 +15,7 @@ #include "parse/font_face.h" #include "parse/important.h" #include "parse/language.h" +#include "parse/mq.h" #include "parse/parse.h" #include "parse/propstrings.h" #include "parse/properties/properties.h" @@ -53,9 +54,6 @@ static css_error handleDeclaration(css_language *c, const parserutils_vector *vector); /* At-rule parsing */ -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, @@ -416,10 +414,10 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) match) { if (c->state <= IMPORT_PERMITTED) { lwc_string *url; - uint64_t media = 0; + css_mq_query *media = NULL; /* any0 = (STRING | URI) ws - * (IDENT ws (',' ws IDENT ws)* )? */ + * (media query)? */ const css_token *uri = parserutils_vector_iterate(vector, &ctx); if (uri == NULL || (uri->type != CSS_TOKEN_STRING && @@ -429,21 +427,24 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) consumeWhitespace(vector, &ctx); /* Parse media list */ - error = parseMediaList(c, vector, &ctx, &media); + error = css__mq_parse_media_list(c, vector, &ctx, &media); if (error != CSS_OK) return error; /* Create rule */ error = css__stylesheet_rule_create(c->sheet, CSS_RULE_IMPORT, &rule); - if (error != CSS_OK) + if (error != CSS_OK) { + css__mq_query_unref(media); return error; + } /* Resolve import URI */ error = c->sheet->resolve(c->sheet->resolve_pw, c->sheet->url, uri->idata, &url); if (error != CSS_OK) { + css__mq_query_unref(media); css__stylesheet_rule_destroy(c->sheet, rule); return error; } @@ -453,6 +454,7 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) rule, url, media); if (error != CSS_OK) { lwc_string_unref(url); + css__mq_query_unref(media); css__stylesheet_rule_destroy(c->sheet, rule); return error; } @@ -460,17 +462,19 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) /* Inform client of need for import */ if (c->sheet->import != NULL) { error = c->sheet->import(c->sheet->import_pw, - c->sheet, url, media); + c->sheet, url); if (error != CSS_OK) { lwc_string_unref(url); + css__mq_query_unref(media); css__stylesheet_rule_destroy(c->sheet, rule); return error; } } - /* No longer care about url */ + /* No longer care about url or media */ lwc_string_unref(url); + css__mq_query_unref(media); /* Add rule to sheet */ error = css__stylesheet_add_rule(c->sheet, rule, NULL); @@ -527,31 +531,37 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector) } } else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[MEDIA], &match) == lwc_error_ok && match) { - uint64_t media = 0; + css_mq_query *media = NULL; - /* any0 = IDENT ws (',' ws IDENT ws)* */ + /* any0 = media query */ - error = parseMediaList(c, vector, &ctx, &media); + error = css__mq_parse_media_list(c, vector, &ctx, &media); if (error != CSS_OK) return error; error = css__stylesheet_rule_create(c->sheet, CSS_RULE_MEDIA, &rule); - if (error != CSS_OK) + if (error != CSS_OK) { + css__mq_query_unref(media); return error; + } error = css__stylesheet_rule_set_media(c->sheet, rule, media); if (error != CSS_OK) { css__stylesheet_rule_destroy(c->sheet, rule); + css__mq_query_unref(media); return error; } error = css__stylesheet_add_rule(c->sheet, rule, NULL); if (error != CSS_OK) { css__stylesheet_rule_destroy(c->sheet, rule); + css__mq_query_unref(media); return error; } + css__mq_query_unref(media); + /* Rule is now owned by the sheet, * so no need to destroy it */ @@ -795,85 +805,6 @@ 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) -{ - uint64_t ret = 0; - bool match = false; - const css_token *token; - - token = parserutils_vector_iterate(vector, ctx); - - while (token != NULL) { - if (token->type != CSS_TOKEN_IDENT) - return CSS_INVALID; - - if (lwc_string_caseless_isequal(token->idata, c->strings[AURAL], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_AURAL; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[BRAILLE], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_BRAILLE; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[EMBOSSED], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_EMBOSSED; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[HANDHELD], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_HANDHELD; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[PRINT], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_PRINT; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[PROJECTION], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_PROJECTION; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[SCREEN], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_SCREEN; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[SPEECH], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_SPEECH; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[TTY], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_TTY; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[TV], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_TV; - } else if (lwc_string_caseless_isequal( - token->idata, c->strings[ALL], - &match) == lwc_error_ok && match) { - ret |= CSS_MEDIA_ALL; - } else - return CSS_INVALID; - - consumeWhitespace(vector, ctx); - - token = parserutils_vector_iterate(vector, ctx); - if (token != NULL && tokenIsChar(token, ',') == false) - return CSS_INVALID; - - consumeWhitespace(vector, ctx); - } - - /* If, after parsing the media list, we still have no media, - * then it must be ALL. */ - if (ret == 0) - ret = CSS_MEDIA_ALL; - - *media = ret; - - return CSS_OK; -} - /** * Add a namespace mapping * diff --git a/src/parse/mq.c b/src/parse/mq.c new file mode 100644 index 0000000..5c9c7fa --- /dev/null +++ b/src/parse/mq.c @@ -0,0 +1,96 @@ +/* + * This file is part of LibCSS. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2016 John-Mark Bell + */ + +/* https://drafts.csswg.org/mediaqueries/ */ + +#include "parse/mq.h" + +css_error css__mq_parse_media_list(css_language *c, + const parserutils_vector *vector, int *ctx, + css_mq_query **media) +{ + css_mq_query *ret = NULL; + const css_token *token; + + /* (IDENT ws (',' ws IDENT ws)* )? */ + + UNUSED(c); + + token = parserutils_vector_iterate(vector, ctx); + + while (token != NULL) { + if (token->type != CSS_TOKEN_IDENT) + return CSS_INVALID; + +#if 0 + if (lwc_string_caseless_isequal(token->idata, c->strings[AURAL], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_AURAL; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[BRAILLE], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_BRAILLE; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[EMBOSSED], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_EMBOSSED; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[HANDHELD], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_HANDHELD; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[PRINT], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_PRINT; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[PROJECTION], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_PROJECTION; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[SCREEN], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_SCREEN; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[SPEECH], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_SPEECH; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[TTY], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_TTY; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[TV], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_TV; + } else if (lwc_string_caseless_isequal( + token->idata, c->strings[ALL], + &match) == lwc_error_ok && match) { + ret |= CSS_MEDIA_ALL; + } else + return CSS_INVALID; +#endif + consumeWhitespace(vector, ctx); + + token = parserutils_vector_iterate(vector, ctx); + if (token != NULL && tokenIsChar(token, ',') == false) + return CSS_INVALID; + + consumeWhitespace(vector, ctx); + } + +#if 0 + /* If, after parsing the media list, we still have no media, + * then it must be ALL. */ + if (ret == 0) + ret = CSS_MEDIA_ALL; +#endif + + *media = ret; + + return CSS_OK; +} + diff --git a/src/parse/mq.h b/src/parse/mq.h new file mode 100644 index 0000000..eeb55da --- /dev/null +++ b/src/parse/mq.h @@ -0,0 +1,100 @@ +/* + * This file is part of LibCSS. + * Licensed under the MIT License, + * http://www.opensource.org/licenses/mit-license.php + * Copyright 2016 John-Mark Bell + */ + +#ifndef css_parse_mq_h_ +#define css_parse_mq_h_ + +#include +#include "parse/language.h" + +typedef struct { + enum { + CSS_MQ_VALUE_TYPE_NUM, + CSS_MQ_VALUE_TYPE_DIM, + CSS_MQ_VALUE_TYPE_IDENT, + CSS_MQ_VALUE_TYPE_RATIO + } type; + union { + css_fixed num_or_ratio; /* Where ratio is the result of a/b */ + struct { + css_fixed len; + css_unit unit; + } dim; + lwc_string *ident; + } data; +} css_mq_value; + +/* + * "name : value" is encoded as "name = value" + * "name" is encoded by setting the operator to "bool" + * "name op value" is encoded verbatim (with op2 set to "unused") + * "value op name" inverts the operator to encode (i.e < becomes >=) (and sets op2 to "unused") + * "value op name op value" is encoded using op2 and value2 + */ +typedef enum { + CSS_MQ_FEATURE_OP_BOOL, /* op only */ + CSS_MQ_FEATURE_OP_UNUSED = CSS_MQ_FEATURE_OP_BOOL, /* op2 only */ + + CSS_MQ_FEATURE_OP_LT, + CSS_MQ_FEATURE_OP_LTE, + CSS_MQ_FEATURE_OP_EQ, /* op only */ + CSS_MQ_FEATURE_OP_GTE, + CSS_MQ_FEATURE_OP_GT +} css_mq_feature_op; + +typedef struct { + lwc_string *name; + css_mq_feature_op op; + css_mq_feature_op op2; + css_mq_value value; + css_mq_value value2; +} css_mq_feature; + +typedef struct css_mq_cond_or_feature css_mq_cond_or_feature; + +typedef struct { + uint32_t nparts; + css_mq_cond_or_feature **parts; +} css_mq_cond_parts; + +typedef struct { + uint32_t negate : 1, /* set if "not" */ + op : 1; /* clear if "and", set if "or" */ + css_mq_cond_parts *parts; +} css_mq_cond; + +struct css_mq_cond_or_feature { + enum { + CSS_MQ_FEATURE, + CSS_MQ_COND + } type; + union { + css_mq_cond cond; + css_mq_feature feat; + } data; +}; + +typedef struct css_mq_query { + struct css_mq_query *next; + + uint32_t negate_type : 1, /* set if "not type" */ + cond_op : 1; /* clear if "and", set if "or" */ + lwc_string *type; /* or NULL */ + + uint32_t nconds; + css_mq_cond **conds; +} css_mq_query; + +css_error css__mq_parse_media_list(css_language *c, + const parserutils_vector *vector, int *ctx, + css_mq_query **media); + +/** \todo is this necessary? */ +css_mq_query *css__mq_query_ref(css_mq_query *media); +css_mq_query *css__mq_query_unref(css_mq_query *media); + +#endif diff --git a/src/stylesheet.c b/src/stylesheet.c index 7c6728b..b1801ed 100644 --- a/src/stylesheet.c +++ b/src/stylesheet.c @@ -377,8 +377,6 @@ css_error css_stylesheet_data_done(css_stylesheet *sheet) * \param parent Parent stylesheet * \param url Pointer to object to be populated with details of URL of * imported stylesheet (potentially relative) - * \param media Pointer to location to receive applicable media types for - * imported sheet, * \return CSS_OK on success, * CSS_INVALID if there are no pending imports remaining * @@ -396,11 +394,11 @@ css_error css_stylesheet_data_done(css_stylesheet *sheet) * register an empty stylesheet with the parent in its place. */ css_error css_stylesheet_next_pending_import(css_stylesheet *parent, - lwc_string **url, uint64_t *media) + lwc_string **url) { const css_rule *r; - if (parent == NULL || url == NULL || media == NULL) + if (parent == NULL || url == NULL) return CSS_BADPARM; for (r = parent->rule_list; r != NULL; r = r->next) { @@ -413,7 +411,6 @@ css_error css_stylesheet_next_pending_import(css_stylesheet *parent, if (r->type == CSS_RULE_IMPORT && i->sheet == NULL) { *url = lwc_string_ref(i->url); - *media = i->media; return CSS_OK; } @@ -1326,7 +1323,7 @@ css_error css__stylesheet_rule_set_charset(css_stylesheet *sheet, */ css_error css__stylesheet_rule_set_nascent_import(css_stylesheet *sheet, css_rule *rule, lwc_string *url, - uint64_t media) + css_mq_query *media) { css_rule_import *r = (css_rule_import *) rule; @@ -1338,7 +1335,7 @@ css_error css__stylesheet_rule_set_nascent_import(css_stylesheet *sheet, /* Set the rule's sheet field */ r->url = lwc_string_ref(url); - r->media = media; + r->media = css__mq_query_ref(media); return CSS_OK; } @@ -1352,7 +1349,7 @@ css_error css__stylesheet_rule_set_nascent_import(css_stylesheet *sheet, * \return CSS_OK on success, appropriate error otherwise */ css_error css__stylesheet_rule_set_media(css_stylesheet *sheet, - css_rule *rule, uint64_t media) + css_rule *rule, css_mq_query *media) { css_rule_media *r = (css_rule_media *) rule; @@ -1363,7 +1360,7 @@ css_error css__stylesheet_rule_set_media(css_stylesheet *sheet, assert(rule->type == CSS_RULE_MEDIA); /* Set the rule's media */ - r->media = media; + r->media = css__mq_query_ref(media); return CSS_OK; } diff --git a/src/stylesheet.h b/src/stylesheet.h index 18e077e..a44ad1f 100644 --- a/src/stylesheet.h +++ b/src/stylesheet.h @@ -20,6 +20,7 @@ #include "bytecode/bytecode.h" #include "parse/parse.h" +#include "parse/mq.h" #include "select/hash.h" typedef struct css_rule css_rule; @@ -132,7 +133,7 @@ typedef struct css_rule_selector { typedef struct css_rule_media { css_rule base; - uint64_t media; + css_mq_query *media; css_rule *first_child; css_rule *last_child; @@ -155,7 +156,7 @@ typedef struct css_rule_import { css_rule base; lwc_string *url; - uint64_t media; + css_mq_query *media; css_stylesheet *sheet; } css_rule_import; @@ -268,10 +269,10 @@ css_error css__stylesheet_rule_set_charset(css_stylesheet *sheet, css_rule *rule, lwc_string *charset); css_error css__stylesheet_rule_set_nascent_import(css_stylesheet *sheet, - css_rule *rule, lwc_string *url, uint64_t media); + css_rule *rule, lwc_string *url, css_mq_query *media); css_error css__stylesheet_rule_set_media(css_stylesheet *sheet, - css_rule *rule, uint64_t media); + css_rule *rule, css_mq_query *media); css_error css__stylesheet_rule_set_page_selector(css_stylesheet *sheet, css_rule *rule, css_selector *sel); -- cgit v1.2.3