summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Drake <Michael Drake tlsa@netsurf-browser.org>2019-05-04 14:54:04 +0100
committerMichael Drake <Michael Drake tlsa@netsurf-browser.org>2019-05-04 14:54:04 +0100
commitace5978891ce0c2e1700945a296881fc62109701 (patch)
treecbc8d71fb5116b7d65b9b607b71335ecc61c6af2
parent4591e4851068ab16afdecbaef5eccacc71344ffa (diff)
parent44feffba4ab7178cca1dbdb4e218d6a93861f804 (diff)
downloadlibcss-ace5978891ce0c2e1700945a296881fc62109701.tar.gz
libcss-ace5978891ce0c2e1700945a296881fc62109701.tar.bz2
Merge branch 'tlsa/jmb/mq2'
-rw-r--r--docs/Bytecode9
-rw-r--r--examples/example1.c7
-rw-r--r--include/libcss/select.h8
-rw-r--r--include/libcss/stylesheet.h5
-rw-r--r--include/libcss/types.h137
-rw-r--r--src/bytecode/bytecode.h8
-rw-r--r--src/parse/Makefile2
-rw-r--r--src/parse/language.c112
-rw-r--r--src/parse/mq.c1150
-rw-r--r--src/parse/mq.h100
-rw-r--r--src/parse/parse.c204
-rw-r--r--src/parse/parse.h2
-rw-r--r--src/parse/properties/utils.c8
-rw-r--r--src/parse/propstrings.c4
-rw-r--r--src/parse/propstrings.h2
-rw-r--r--src/select/computed.c2
-rw-r--r--src/select/hash.h2
-rw-r--r--src/select/mq.h53
-rw-r--r--src/select/select.c60
-rw-r--r--src/select/select.h2
-rw-r--r--src/stylesheet.c19
-rw-r--r--src/stylesheet.h9
-rw-r--r--test/css21.c4
-rw-r--r--test/data/parse2/illegal-values.dat2
-rw-r--r--test/parse-auto.c4
-rw-r--r--test/select.c27
26 files changed, 1698 insertions, 244 deletions
diff --git a/docs/Bytecode b/docs/Bytecode
index f64656a..dd0f424 100644
--- a/docs/Bytecode
+++ b/docs/Bytecode
@@ -69,6 +69,7 @@ Length is a 32bit numeric value (as described above) and unit is as follows:
00000000 => deg
00000001 => grad
00000010 => rad
+ 00000011 => turn
bit 10 set => time unit
bits 11-31: MBZ
@@ -84,6 +85,14 @@ Length is a 32bit numeric value (as described above) and unit is as follows:
00000000 => Hz
00000001 => kHz
+ bit 12 set => resolution unit
+ bits 13-31: MBZ
+ bits 8-11 : MBZ
+ bits 0-7 :
+ 00000000 => dpi
+ 00000001 => dpcm
+ 00000010 => dppx
+
CSS colours are stored as one 32bit value. See "Colour" for their format.
Shorthand properties
diff --git a/examples/example1.c b/examples/example1.c
index 1c0dcf9..c36a94d 100644
--- a/examples/example1.c
+++ b/examples/example1.c
@@ -161,6 +161,9 @@ int main(int argc, char **argv)
uint32_t count;
unsigned int hh;
css_stylesheet_params params;
+ css_media media = {
+ .type = CSS_MEDIA_SCREEN,
+ };
UNUSED(argc);
UNUSED(argv);
@@ -210,7 +213,7 @@ int main(int argc, char **argv)
if (code != CSS_OK)
die("css_select_ctx_create", code);
code = css_select_ctx_append_sheet(select_ctx, sheet, CSS_ORIGIN_AUTHOR,
- CSS_MEDIA_ALL);
+ NULL);
if (code != CSS_OK)
die("css_select_ctx_append_sheet", code);
code = css_select_ctx_count_sheets(select_ctx, &count);
@@ -234,7 +237,7 @@ int main(int argc, char **argv)
lwc_intern_string(element, strlen(element), &element_name);
code = css_select_style(select_ctx, element_name,
- CSS_MEDIA_SCREEN, NULL,
+ &media, NULL,
&select_handler, 0,
&style);
if (code != CSS_OK)
diff --git a/include/libcss/select.h b/include/libcss/select.h
index f1de409..ca57456 100644
--- a/include/libcss/select.h
+++ b/include/libcss/select.h
@@ -206,10 +206,10 @@ css_error css_select_ctx_destroy(css_select_ctx *ctx);
css_error css_select_ctx_append_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet,
- css_origin origin, uint64_t media);
+ css_origin origin, const char *media);
css_error css_select_ctx_insert_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet, uint32_t index,
- css_origin origin, uint64_t media);
+ css_origin origin, const char *media);
css_error css_select_ctx_remove_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet);
@@ -221,13 +221,13 @@ css_error css_select_default_style(css_select_ctx *ctx,
css_select_handler *handler, void *pw,
css_computed_style **style);
css_error css_select_style(css_select_ctx *ctx, void *node,
- uint64_t media, const css_stylesheet *inline_style,
+ const css_media *media, const css_stylesheet *inline_style,
css_select_handler *handler, void *pw,
css_select_results **result);
css_error css_select_results_destroy(css_select_results *results);
css_error css_select_font_faces(css_select_ctx *ctx,
- uint64_t media, lwc_string *font_family,
+ const css_media *media, lwc_string *font_family,
css_select_font_faces_results **result);
css_error css_select_font_faces_results_destroy(
css_select_font_faces_results *results);
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/include/libcss/types.h b/include/libcss/types.h
index 4f35737..44aef12 100644
--- a/include/libcss/types.h
+++ b/include/libcss/types.h
@@ -60,10 +60,10 @@ typedef enum css_media_type {
CSS_MEDIA_TTY = (1 << 8),
CSS_MEDIA_TV = (1 << 9),
CSS_MEDIA_ALL = CSS_MEDIA_AURAL | CSS_MEDIA_BRAILLE |
- CSS_MEDIA_EMBOSSED | CSS_MEDIA_HANDHELD |
- CSS_MEDIA_PRINT | CSS_MEDIA_PROJECTION |
- CSS_MEDIA_SCREEN | CSS_MEDIA_SPEECH |
- CSS_MEDIA_TTY | CSS_MEDIA_TV
+ CSS_MEDIA_EMBOSSED | CSS_MEDIA_HANDHELD |
+ CSS_MEDIA_PRINT | CSS_MEDIA_PROJECTION |
+ CSS_MEDIA_SCREEN | CSS_MEDIA_SPEECH |
+ CSS_MEDIA_TTY | CSS_MEDIA_TV
} css_media_type;
/**
@@ -116,6 +116,135 @@ typedef enum css_unit {
} css_unit;
/**
+ * Media orienations
+ */
+typedef enum css_media_orientation {
+ CSS_MEDIA_ORIENTATION_PORTRAIT = 0,
+ CSS_MEDIA_ORIENTATION_LANDSCAPE = 1
+} css_media_orientation;
+
+/**
+ * Media scans
+ */
+typedef enum css_media_scan {
+ CSS_MEDIA_SCAN_PROGRESSIVE = 0,
+ CSS_MEDIA_SCAN_INTERLACE = 1
+} css_media_scan;
+
+/**
+ * Media update-frequencies
+ */
+typedef enum css_media_update_frequency {
+ CSS_MEDIA_UPDATE_FREQUENCY_NORMAL = 0,
+ CSS_MEDIA_UPDATE_FREQUENCY_SLOW = 1,
+ CSS_MEDIA_UPDATE_FREQUENCY_NONE = 2
+} css_media_update_frequency;
+
+/**
+ * Media block overflows
+ */
+typedef enum css_media_overflow_block {
+ CSS_MEDIA_OVERFLOW_BLOCK_NONE = 0,
+ CSS_MEDIA_OVERFLOW_BLOCK_SCROLL = 1,
+ CSS_MEDIA_OVERFLOW_BLOCK_OPTIONAL_PAGED = 2,
+ CSS_MEDIA_OVERFLOW_BLOCK_PAGED = 3
+} css_media_overflow_block;
+
+/**
+ * Media inline overflows
+ */
+typedef enum css_media_overflow_inline {
+ CSS_MEDIA_OVERFLOW_INLINE_NONE = 0,
+ CSS_MEDIA_OVERFLOW_INLINE_SCROLL = 1
+} css_media_overflow_inline;
+
+/**
+ * Media pointers
+ */
+typedef enum css_media_pointer {
+ CSS_MEDIA_POINTER_NONE = 0,
+ CSS_MEDIA_POINTER_COARSE = 1,
+ CSS_MEDIA_POINTER_FINE = 2
+} css_media_pointer;
+
+/**
+ * Media hovers
+ */
+typedef enum css_media_hover {
+ CSS_MEDIA_HOVER_NONE = 0,
+ CSS_MEDIA_HOVER_ON_DEMAND = 1,
+ CSS_MEDIA_HOVER_HOVER = 2
+} css_media_hover;
+
+/**
+ * Media light-levels
+ */
+typedef enum css_media_light_level {
+ CSS_MEDIA_LIGHT_LEVEL_NORMAL = 0,
+ CSS_MEDIA_LIGHT_LEVEL_DIM = 1,
+ CSS_MEDIA_LIGHT_LEVEL_WASHED = 2
+} css_media_light_level;
+
+/**
+ * Media scriptings
+ */
+typedef enum css_media_scripting {
+ CSS_MEDIA_SCRIPTING_NONE = 0,
+ CSS_MEDIA_SCRIPTING_INITIAL_ONLY = 1,
+ CSS_MEDIA_SCRIPTING_ENABLED = 2
+} css_media_scripting;
+
+typedef struct css_media_length {
+ css_fixed value;
+ css_unit unit;
+} css_media_length;
+
+typedef struct css_media_resolution {
+ css_fixed value;
+ css_unit unit;
+} css_media_resolution;
+
+/**
+ * Media specification
+ */
+typedef struct css_media {
+ /* Media type */
+ css_media_type type;
+
+ /* Screen / Device media features */
+ css_media_length width;
+ css_media_length height;
+ css_fixed aspect_ratio;
+ css_media_orientation orientation;
+
+ /* Display quality media features */
+ css_media_resolution resolution;
+ css_media_scan scan;
+ css_fixed grid; /** boolean: {0|1} */
+ css_media_update_frequency update;
+ css_media_overflow_block overflow_block;
+ css_media_overflow_inline overflow_inline;
+
+ /* Color media features */
+ css_fixed color; /* colour bpp (0 for monochrome) */
+ css_fixed color_index;
+ css_fixed monochrome; /* monochrome bpp (0 for colour) */
+ css_fixed inverted_colors; /** boolean: {0|1} */
+
+ /* Interaction media features */
+ css_media_pointer pointer;
+ css_media_pointer any_pointer;
+ css_media_hover hover;
+ css_media_hover any_hover;
+
+ /* Environmental media features */
+ css_media_light_level light_level;
+
+ /* Scripting media features */
+ css_media_scripting scripting;
+} css_media;
+
+/**
* Type of a qualified name
*/
typedef struct css_qname {
diff --git a/src/bytecode/bytecode.h b/src/bytecode/bytecode.h
index 422f141..22703f7 100644
--- a/src/bytecode/bytecode.h
+++ b/src/bytecode/bytecode.h
@@ -52,6 +52,7 @@ typedef enum unit {
UNIT_DEG = (1 << 9) + 0,
UNIT_GRAD = (1 << 9) + 1,
UNIT_RAD = (1 << 9) + 2,
+ UNIT_TURN = (1 << 9) + 3,
UNIT_TIME = (1 << 10),
UNIT_MS = (1 << 10) + 0,
@@ -59,7 +60,12 @@ typedef enum unit {
UNIT_FREQ = (1 << 11),
UNIT_HZ = (1 << 11) + 0,
- UNIT_KHZ = (1 << 11) + 1
+ UNIT_KHZ = (1 << 11) + 1,
+
+ UNIT_RESOLUTION = (1 << 12),
+ UNIT_DPI = (1 << 12) + 0,
+ UNIT_DPCM = (1 << 12) + 1,
+ UNIT_DPPX = (1 << 12) + 2,
} unit;
typedef uint32_t colour;
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..54fac9a 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,9 @@ 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)* )? */
+ /* any0 = (STRING | URI) ws (media query)? */
const css_token *uri =
parserutils_vector_iterate(vector, &ctx);
if (uri == NULL || (uri->type != CSS_TOKEN_STRING &&
@@ -429,15 +426,18 @@ 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->strings, 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_destroy(media);
return error;
+ }
/* Resolve import URI */
error = c->sheet->resolve(c->sheet->resolve_pw,
@@ -445,6 +445,7 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
uri->idata, &url);
if (error != CSS_OK) {
css__stylesheet_rule_destroy(c->sheet, rule);
+ css__mq_query_destroy(media);
return error;
}
@@ -454,13 +455,14 @@ css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
if (error != CSS_OK) {
lwc_string_unref(url);
css__stylesheet_rule_destroy(c->sheet, rule);
+ css__mq_query_destroy(media);
return error;
}
/* 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__stylesheet_rule_destroy(c->sheet,
@@ -527,22 +529,25 @@ 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)* */
-
- error = parseMediaList(c, vector, &ctx, &media);
+ /* any0 = media query */
+ error = css__mq_parse_media_list(
+ c->strings, 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_destroy(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_destroy(media);
return error;
}
@@ -795,85 +800,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..cb9345d
--- /dev/null
+++ b/src/parse/mq.c
@@ -0,0 +1,1150 @@
+/*
+ * This file is part of LibCSS.
+ * Licensed under the MIT License,
+ * http://www.opensource.org/licenses/mit-license.php
+ * Copyright 2016 John-Mark Bell <jmb@netsurf-browser.org>
+ */
+
+/* https://drafts.csswg.org/mediaqueries/ */
+
+#include <string.h>
+
+#include <libcss/fpmath.h>
+
+#include "stylesheet.h"
+#include "bytecode/bytecode.h"
+#include "parse/mq.h"
+#include "parse/properties/utils.h"
+#include "utils/utils.h"
+
+static void css__mq_value_destroy(css_mq_value *value)
+{
+ assert(value != NULL);
+
+ if (value->type == CSS_MQ_VALUE_TYPE_IDENT) {
+ lwc_string_unref(value->data.ident);
+ }
+}
+
+static void css__mq_feature_destroy(css_mq_feature *feature)
+{
+ if (feature != NULL) {
+ lwc_string_unref(feature->name);
+ css__mq_value_destroy(&feature->value);
+ css__mq_value_destroy(&feature->value2);
+ free(feature);
+ }
+}
+
+static void css__mq_cond_or_feature_destroy(
+ css_mq_cond_or_feature *cond_or_feature);
+
+static void css__mq_cond_parts_destroy(css_mq_cond_parts *cond_parts)
+{
+ if (cond_parts != NULL) {
+ for (uint32_t i = 0; i < cond_parts->nparts; i++) {
+ css__mq_cond_or_feature_destroy(cond_parts->parts[i]);
+ }
+ free(cond_parts->parts);
+ free(cond_parts);
+ }
+}
+
+static void css__mq_cond_destroy(css_mq_cond *cond)
+{
+ if (cond != NULL) {
+ css__mq_cond_parts_destroy(cond->parts);
+ free(cond);
+ }
+}
+
+static void css__mq_cond_or_feature_destroy(
+ css_mq_cond_or_feature *cond_or_feature)
+{
+ if (cond_or_feature != NULL) {
+ switch (cond_or_feature->type) {
+ case CSS_MQ_FEATURE:
+ css__mq_feature_destroy(cond_or_feature->data.feat);
+ break;
+ case CSS_MQ_COND:
+ css__mq_cond_destroy(cond_or_feature->data.cond);
+ break;
+ }
+ free(cond_or_feature);
+ }
+}
+
+void css__mq_query_destroy(css_mq_query *media)
+{
+ while (media != NULL) {
+ css_mq_query *next = media->next;
+
+ css__mq_cond_destroy(media->cond);
+ free(media);
+
+ media = next;
+ }
+}
+
+static css_error mq_parse_condition(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ bool permit_or, css_mq_cond **cond);
+
+static css_error mq_parse_ratio(
+ const parserutils_vector *vector, int *ctx,
+ const css_token *numerator, css_fixed *ratio)
+{
+ const css_token *token;
+ css_fixed num, den;
+ size_t num_len, den_len;
+
+ /* NUMBER ws* '/' ws* NUMBER */
+
+ /* numerator, ws* already consumed */
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || tokenIsChar(token, '/') == false) {
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || token->type != CSS_TOKEN_NUMBER) {
+ return CSS_INVALID;
+ }
+
+ num = css__number_from_lwc_string(numerator->idata, true, &num_len);
+ den = css__number_from_lwc_string(token->idata, true, &den_len);
+
+ *ratio = css_divide_fixed(num, den);
+
+ return CSS_OK;
+}
+
+static css_error mq_create_feature(
+ lwc_string *name,
+ css_mq_feature **feature)
+{
+ css_mq_feature *f;
+
+ f = malloc(sizeof(*f));
+ if (f == NULL) {
+ return CSS_NOMEM;
+ }
+
+ memset(f, 0, sizeof(*f));
+
+ f->name = lwc_string_ref(name);
+
+ *feature = f;
+
+ return CSS_OK;
+}
+
+static css_error mq_populate_value(css_mq_value *value,
+ const css_token *token)
+{
+ if (token->type == CSS_TOKEN_NUMBER) {
+ size_t num_len;
+ value->type = CSS_MQ_VALUE_TYPE_NUM;
+ value->data.num_or_ratio = css__number_from_lwc_string(
+ token->idata, false, &num_len);
+ } else if (token->type == CSS_TOKEN_DIMENSION) {
+ size_t len = lwc_string_length(token->idata);
+ const char *data = lwc_string_data(token->idata);
+ uint32_t unit = UNIT_PX;
+ size_t consumed;
+ css_error error;
+
+ value->type = CSS_MQ_VALUE_TYPE_DIM;
+ value->data.dim.len = css__number_from_lwc_string(
+ token->idata, false, &consumed);
+ error = css__parse_unit_keyword(data + consumed, len - consumed,
+ &unit);
+ if (error != CSS_OK) {
+ return error;
+ }
+ value->data.dim.unit = unit;
+ } else if (token->type == CSS_TOKEN_IDENT) {
+ value->type = CSS_MQ_VALUE_TYPE_IDENT;
+ value->data.ident = lwc_string_ref(token->idata);
+ }
+
+ return CSS_OK;
+}
+
+static css_error mq_parse_op(const css_token *token,
+ css_mq_feature_op *op)
+{
+ size_t len;
+ const char *data;
+
+ if (token == NULL || token->type != CSS_TOKEN_CHAR)
+ return CSS_INVALID;
+
+ len = lwc_string_length(token->idata);
+ data = lwc_string_data(token->idata);
+
+ if (len == 2) {
+ if (strncasecmp(data, "<=", 2) == 0)
+ *op = CSS_MQ_FEATURE_OP_LTE;
+ else if (strncasecmp(data, ">=", 2) == 0)
+ *op = CSS_MQ_FEATURE_OP_GTE;
+ else
+ return CSS_INVALID;
+ } else if (len == 1) {
+ if (*data == '<')
+ *op = CSS_MQ_FEATURE_OP_LT;
+ else if (*data == '=')
+ *op = CSS_MQ_FEATURE_OP_EQ;
+ else if (*data == '>')
+ *op = CSS_MQ_FEATURE_OP_GT;
+ else
+ return CSS_INVALID;
+ } else {
+ return CSS_INVALID;
+ }
+
+ return CSS_OK;
+}
+
+static css_error mq_parse_range(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ const css_token *name_or_value,
+ css_mq_feature **feature)
+{
+ const css_token *token, *value_or_name, *name = NULL, *value2 = NULL;
+ css_mq_feature *result;
+ css_mq_feature_op op, op2;
+ css_fixed ratio, ratio2;
+ bool name_first = false, value_is_ratio = false, value2_is_ratio = false, match;
+ css_error error;
+
+ /* <mf-range> = <mf-name> [ '<' | '>' ]? '='? <mf-value>
+ * | <mf-value> [ '<' | '>' ]? '='? <mf-name>
+ * | <mf-value> '<' '='? <mf-name> '<' '='? <mf-value>
+ * | <mf-value> '>' '='? <mf-name> '>' '='? <mf-value>
+ */
+
+ if (name_or_value == NULL || (name_or_value->type != CSS_TOKEN_NUMBER &&
+ name_or_value->type != CSS_TOKEN_DIMENSION &&
+ name_or_value->type != CSS_TOKEN_IDENT)) {
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ /* Name-or-value */
+ if (name_or_value->type == CSS_TOKEN_NUMBER &&
+ tokenIsChar(parserutils_vector_peek(vector, *ctx), '/')) {
+ /* ratio */
+ error = mq_parse_ratio(vector, ctx, name_or_value, &ratio);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ value_is_ratio = true;
+ } else if (name_or_value->type == CSS_TOKEN_IDENT &&
+ lwc_string_caseless_isequal(name_or_value->idata,
+ strings[INFINITE], &match) == lwc_error_ok &&
+ match == false) {
+ /* The only ident permitted for mf-value is 'infinite', thus must have name */
+ name = name_or_value;
+ name_first = true;
+ }
+
+ /* Op */
+ token = parserutils_vector_iterate(vector, ctx);
+ error = mq_parse_op(token, &op);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ /* Value-or-name */
+ value_or_name = parserutils_vector_iterate(vector, ctx);
+ if (value_or_name == NULL || (value_or_name->type != CSS_TOKEN_NUMBER &&
+ value_or_name->type != CSS_TOKEN_DIMENSION &&
+ value_or_name->type != CSS_TOKEN_IDENT)) {
+ return CSS_INVALID;
+ }
+
+ if (name == NULL) {
+ if (value_or_name->type != CSS_TOKEN_IDENT) {
+ return CSS_INVALID;
+ } else {
+ name = value_or_name;
+ }
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ if (value_or_name->type == CSS_TOKEN_NUMBER &&
+ tokenIsChar(parserutils_vector_peek(vector, *ctx), '/')) {
+ /* ratio */
+ error = mq_parse_ratio(vector, ctx, token, &ratio);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ value_is_ratio = true;
+ }
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (name_first == false && token != NULL && tokenIsChar(token, ')') == false) {
+ /* Op2 */
+ token = parserutils_vector_iterate(vector, ctx);
+ error = mq_parse_op(token, &op2);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ /* Validate operators: must both be LT(E) or GT(E) */
+ if (op == CSS_MQ_FEATURE_OP_LT || op == CSS_MQ_FEATURE_OP_LTE) {
+ if (op2 != CSS_MQ_FEATURE_OP_LT && op2 != CSS_MQ_FEATURE_OP_LTE) {
+ return CSS_INVALID;
+ }
+ } else if (op == CSS_MQ_FEATURE_OP_GT || op == CSS_MQ_FEATURE_OP_GTE) {
+ if (op2 != CSS_MQ_FEATURE_OP_GT && op2 != CSS_MQ_FEATURE_OP_GTE) {
+ return CSS_INVALID;
+ }
+ } else {
+ return CSS_INVALID;
+ }
+
+ /* Value2 */
+ value2 = parserutils_vector_iterate(vector, ctx);
+ if (value2 == NULL || (value2->type != CSS_TOKEN_NUMBER &&
+ value2->type != CSS_TOKEN_DIMENSION &&
+ value2->type != CSS_TOKEN_IDENT)) {
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ if (value_or_name->type == CSS_TOKEN_NUMBER &&
+ tokenIsChar(parserutils_vector_peek(vector, *ctx), '/')) {
+ /* ratio */
+ error = mq_parse_ratio(vector, ctx, token, &ratio2);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ value2_is_ratio = true;
+ }
+ }
+
+ error = mq_create_feature(name->idata, &result);
+ if (error != CSS_OK) {
+ return error;
+ }
+ if (name_first) {
+ /* Invert operator */
+ if (op == CSS_MQ_FEATURE_OP_LT) {
+ op = CSS_MQ_FEATURE_OP_GTE;
+ } else if (op == CSS_MQ_FEATURE_OP_LTE) {
+ op = CSS_MQ_FEATURE_OP_GT;
+ } else if (op == CSS_MQ_FEATURE_OP_GT) {
+ op = CSS_MQ_FEATURE_OP_LTE;
+ } else if (op == CSS_MQ_FEATURE_OP_GTE) {
+ op = CSS_MQ_FEATURE_OP_LT;
+ }
+ }
+ result->op = op;
+ if (value_is_ratio) {
+ result->value.type = CSS_MQ_VALUE_TYPE_RATIO;
+ result->value.data.num_or_ratio = ratio;
+ } else {
+ /* num/dim/ident */
+ error = mq_populate_value(&result->value, token);
+ if (error != CSS_OK) {
+ free(result);
+ return error;
+ }
+ }
+ if (value2 != NULL) {
+ result->op2 = op2;
+ if (value2_is_ratio) {
+ result->value2.type = CSS_MQ_VALUE_TYPE_RATIO;
+ result->value2.data.num_or_ratio = ratio;
+ } else {
+ /* num/dim/ident */
+ error = mq_populate_value(&result->value2, token);
+ if (error != CSS_OK) {
+ css__mq_feature_destroy(result);
+ return error;
+ }
+ }
+ }
+
+ *feature = result;
+
+ return CSS_OK;
+}
+
+static css_error mq_parse_media_feature(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ css_mq_feature **feature)
+{
+ const css_token *name_or_value, *token;
+ css_mq_feature *result;
+ css_error error;
+
+ /* <media-feature> = ( [ <mf-plain> | <mf-boolean> | <mf-range> ] )
+ * <mf-plain> = <mf-name> : <mf-value>
+ * <mf-boolean> = <mf-name>
+ * <mf-name> = <ident>
+ * <mf-value> = <number> | <dimension> | <ident> | <ratio>
+ */
+
+ /* ( already consumed */
+
+ consumeWhitespace(vector, ctx);
+
+ name_or_value = parserutils_vector_iterate(vector, ctx);
+ if (name_or_value == NULL)
+ return CSS_INVALID;
+
+ if (name_or_value->type == CSS_TOKEN_IDENT) {
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (tokenIsChar(token, ')')) {
+ /* mf-boolean */
+ error = mq_create_feature(name_or_value->idata, &result);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ result->op = CSS_MQ_FEATURE_OP_BOOL;
+ } else if (tokenIsChar(token, ':')) {
+ /* mf-plain */
+ parserutils_vector_iterate(vector, ctx);
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || (token->type != CSS_TOKEN_NUMBER &&
+ token->type != CSS_TOKEN_DIMENSION &&
+ token->type != CSS_TOKEN_IDENT)) {
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ error = mq_create_feature(name_or_value->idata, &result);
+ if (error != CSS_OK) {
+ return error;
+ }
+ result->op = CSS_MQ_FEATURE_OP_EQ;
+
+ if (token->type == CSS_TOKEN_NUMBER &&
+ tokenIsChar(parserutils_vector_peek(vector, *ctx), '/')) {
+ /* ratio */
+ css_fixed ratio;
+
+ error = mq_parse_ratio(vector, ctx, token, &ratio);
+ if (error != CSS_OK) {
+ free(result);
+ return error;
+ }
+
+ result->value.type = CSS_MQ_VALUE_TYPE_RATIO;
+ result->value.data.num_or_ratio = ratio;
+ } else {
+ /* num/dim/ident */
+ error = mq_populate_value(&result->value, token);
+ if (error != CSS_OK) {
+ free(result);
+ return error;
+ }
+ }
+
+ consumeWhitespace(vector, ctx);
+ } else {
+ /* mf-range */
+ error = mq_parse_range(strings, vector, ctx,
+ name_or_value, &result);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+ }
+ } else {
+ /* mf-range */
+ error = mq_parse_range(strings, vector, ctx,
+ name_or_value, &result);
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ consumeWhitespace(vector, ctx);
+ }
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (tokenIsChar(token, ')') == false) {
+ css__mq_feature_destroy(result);
+ return CSS_INVALID;
+ }
+
+ *feature = result;
+
+ return CSS_OK;
+}
+
+/*
+ * Consume any value
+ *
+ * CSS Syntax Module Level 3: 8.2
+ */
+static css_error mq_parse_consume_any_value(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ bool until, const char until_char)
+{
+ const css_token *token;
+ css_error error;
+
+ while (true) {
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL) {
+ return CSS_INVALID;
+ }
+
+ switch (token->type) {
+ case CSS_TOKEN_INVALID_STRING:
+ return CSS_INVALID;
+
+ case CSS_TOKEN_CHAR:
+ if (until && tokenIsChar(token, until_char)) {
+ /* Found matching close bracket */
+ return CSS_OK;
+
+ } else if (tokenIsChar(token, ')') ||
+ tokenIsChar(token, ']') ||
+ tokenIsChar(token, '}')) {
+ /* Non-matching close bracket */
+ return CSS_INVALID;
+ }
+ if (tokenIsChar(token, '(')) {
+ /* Need to consume until matching bracket. */
+ error = mq_parse_consume_any_value(strings,
+ vector, ctx, true, ')');
+ if (error != CSS_OK) {
+ return error;
+ }
+ } else if (tokenIsChar(token, '[')) {
+ /* Need to consume until matching bracket. */
+ error = mq_parse_consume_any_value(strings,
+ vector, ctx, true, ']');
+ if (error != CSS_OK) {
+ return error;
+ }
+ } else if (tokenIsChar(token, '{')) {
+ /* Need to consume until matching bracket. */
+ error = mq_parse_consume_any_value(strings,
+ vector, ctx, true, '}');
+ if (error != CSS_OK) {
+ return error;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ return CSS_OK;
+}
+
+static css_error mq_parse_general_enclosed(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx)
+{
+ const css_token *token;
+ css_error error;
+
+ /* <general-enclosed> = [ <function-token> <any-value> ) ]
+ * | ( <ident> <any-value> )
+ */
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL) {
+ return CSS_INVALID;
+ }
+
+ switch (token->type) {
+ case CSS_TOKEN_FUNCTION:
+ error = mq_parse_consume_any_value(strings, vector, ctx,
+ true, ')');
+ if (error != CSS_OK) {
+ return error;
+ }
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (!tokenIsChar(token, ')')) {
+ return CSS_INVALID;
+ }
+ break;
+
+ case CSS_TOKEN_IDENT:
+ error = mq_parse_consume_any_value(strings, vector, ctx,
+ false, '\0');
+ if (error != CSS_OK) {
+ return error;
+ }
+ break;
+
+ default:
+ return CSS_INVALID;
+ }
+
+ return CSS_OK;
+}
+
+static css_error mq_parse_media_in_parens(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ css_mq_cond_or_feature **cond_or_feature)
+{
+ const css_token *token;
+ bool match;
+ int old_ctx;
+ css_mq_cond_or_feature *result = NULL;
+ css_error error = CSS_OK;
+
+ /* <media-in-parens> = ( <media-condition> ) | <media-feature> | <general-enclosed>
+ */
+
+ //LPAREN -> condition-or-feature
+ // "not" or LPAREN -> condition
+ // IDENT | NUMBER | DIMENSION | RATIO -> feature
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || tokenIsChar(token, '(') == false) {
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (token == NULL) {
+ return CSS_INVALID;
+ }
+
+ old_ctx = *ctx;
+
+ if (tokenIsChar(token, '(') || (token->type == CSS_TOKEN_IDENT &&
+ lwc_string_caseless_isequal(token->idata,
+ strings[NOT], &match) == lwc_error_ok &&
+ match)) {
+ css_mq_cond *cond;
+ error = mq_parse_condition(strings, vector, ctx, true, &cond);
+ if (error == CSS_OK) {
+ token = parserutils_vector_iterate(vector, ctx);
+ if (tokenIsChar(token, ')') == false) {
+ return CSS_INVALID;
+ }
+
+ result = malloc(sizeof(*result));
+ if (result == NULL) {
+ css__mq_cond_destroy(cond);
+ return CSS_NOMEM;
+ }
+ memset(result, 0, sizeof(*result));
+ result->type = CSS_MQ_COND;
+ result->data.cond = cond;
+ *cond_or_feature = result;
+ return CSS_OK;
+ }
+ } else if (token->type == CSS_TOKEN_IDENT ||
+ token->type == CSS_TOKEN_NUMBER ||
+ token->type == CSS_TOKEN_DIMENSION) {
+ css_mq_feature *feature;
+ error = mq_parse_media_feature(strings, vector, ctx, &feature);
+ if (error == CSS_OK) {
+ result = malloc(sizeof(*result));
+ if (result == NULL) {
+ css__mq_feature_destroy(feature);
+ return CSS_NOMEM;
+ }
+ memset(result, 0, sizeof(*result));
+ result->type = CSS_MQ_FEATURE;
+ result->data.feat = feature;
+ *cond_or_feature = result;
+ return CSS_OK;
+ }
+ }
+
+ *ctx = old_ctx;
+ error = mq_parse_general_enclosed(strings, vector, ctx);
+
+ return error;
+}
+
+static css_error mq_parse_condition(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ bool permit_or, css_mq_cond **cond)
+{
+ const css_token *token;
+ bool match = false;
+ int op = 0; /* Will be AND | OR once we've had one */
+ css_mq_cond_or_feature *cond_or_feature, **parts;
+ css_mq_cond *result;
+ css_error error;
+
+ /* <media-condition> = <media-not> | <media-in-parens> [ <media-and>* | <media-or>* ]
+ * <media-condition-without-or> = <media-not> | <media-in-parens> <media-and>*
+ * <media-not> = not <media-in-parens>
+ * <media-and> = and <media-in-parens>
+ * <media-or> = or <media-in-parens>
+ */
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (token == NULL ||
+ (tokenIsChar(token, '(') == false &&
+ token->type != CSS_TOKEN_IDENT &&
+ lwc_string_caseless_isequal(token->idata,
+ strings[NOT], &match) != lwc_error_ok &&
+ match == false)) {
+ return CSS_INVALID;
+ }
+
+ result = malloc(sizeof(*result));
+ if (result == NULL) {
+ return CSS_NOMEM;
+ }
+ memset(result, 0, sizeof(*result));
+ result->parts = malloc(sizeof(*result->parts));
+ if (result->parts == NULL) {
+ free(result);
+ return CSS_NOMEM;
+ }
+ memset(result->parts, 0, sizeof(*result->parts));
+
+ if (tokenIsChar(token, '(') == false) {
+ /* Must be "not" */
+ parserutils_vector_iterate(vector, ctx);
+ consumeWhitespace(vector, ctx);
+
+ error = mq_parse_media_in_parens(strings,
+ vector, ctx, &cond_or_feature);
+ if (error != CSS_OK) {
+ css__mq_cond_destroy(result);
+ return CSS_INVALID;
+ }
+
+ result->negate = 1;
+ result->parts->nparts = 1;
+ result->parts->parts = malloc(sizeof(*result->parts->parts));
+ if (result->parts->parts == NULL) {
+ css__mq_cond_or_feature_destroy(cond_or_feature);
+ css__mq_cond_destroy(result);
+ return CSS_NOMEM;
+ }
+ result->parts->parts[0] = cond_or_feature;
+
+ *cond = result;
+
+ return CSS_OK;
+ }
+
+ /* FOLLOW(media-condition) := RPAREN | COMMA | EOF */
+ while (token != NULL && tokenIsChar(token, ')') == false &&
+ tokenIsChar(token, ',') == false) {
+ error = mq_parse_media_in_parens(strings, vector, ctx,
+ &cond_or_feature);
+ if (error != CSS_OK) {
+ css__mq_cond_destroy(result);
+ return CSS_INVALID;
+ }
+
+ parts = realloc(result->parts->parts,
+ (result->parts->nparts+1)*sizeof(*result->parts->parts));
+ if (parts == NULL) {
+ css__mq_cond_or_feature_destroy(cond_or_feature);
+ css__mq_cond_destroy(result);
+ return CSS_NOMEM;
+ }
+ parts[result->parts->nparts] = cond_or_feature;
+ result->parts->parts = parts;
+ result->parts->nparts++;
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (token != NULL && tokenIsChar(token, ')') == false &&
+ tokenIsChar(token, ',') == false) {
+ if (token->type != CSS_TOKEN_IDENT) {
+ css__mq_cond_destroy(result);
+ return CSS_INVALID;
+ } else if (lwc_string_caseless_isequal(token->idata,
+ strings[AND], &match) == lwc_error_ok &&
+ match) {
+ if (op != 0 && op != AND) {
+ css__mq_cond_destroy(result);
+ return CSS_INVALID;
+ }
+ op = AND;
+ } else if (lwc_string_caseless_isequal(token->idata,
+ strings[OR], &match) == lwc_error_ok &&
+ match) {
+ if (permit_or == false || (op != 0 && op != OR)) {
+ css__mq_cond_destroy(result);
+ return CSS_INVALID;
+ }
+ op = OR;
+ } else {
+ /* Neither AND nor OR */
+ css__mq_cond_destroy(result);
+ return CSS_INVALID;
+ }
+
+ parserutils_vector_iterate(vector, ctx);
+ consumeWhitespace(vector, ctx);
+ }
+ }
+
+ if (op == OR) {
+ result->op = 1;
+ }
+
+ *cond = result;
+
+ return CSS_OK;
+}
+
+/**
+ * Parse a media query type.
+ */
+static uint64_t mq_parse_type(lwc_string **strings, lwc_string *type)
+{
+ bool match;
+
+ if (type == NULL) {
+ return CSS_MEDIA_ALL;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[AURAL],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_AURAL;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[BRAILLE],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_BRAILLE;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[EMBOSSED],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_EMBOSSED;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[HANDHELD],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_HANDHELD;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[PRINT],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_PRINT;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[PROJECTION],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_PROJECTION;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[SCREEN],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_SCREEN;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[SPEECH],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_SPEECH;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[TTY],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_TTY;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[TV],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_TV;
+ } else if (lwc_string_caseless_isequal(
+ type, strings[ALL],
+ &match) == lwc_error_ok && match) {
+ return CSS_MEDIA_ALL;
+ }
+
+ return 0;
+}
+
+static css_error mq_parse_media_query(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ css_mq_query **query)
+{
+ const css_token *token;
+ bool match, is_condition = false;
+ css_mq_query *result;
+ css_error error;
+
+ /* <media-query> = <media-condition>
+ * | [ not | only ]? <media-type> [ and <media-condition-without-or> ]?
+ * <media-type> = <ident> (except "not", "and", "or", "only")
+ */
+
+ // LPAREN -> media-condition
+ // not LPAREN -> media-condition
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (tokenIsChar(token, '(')) {
+ is_condition = true;
+ } else if (token->type == CSS_TOKEN_IDENT &&
+ lwc_string_caseless_isequal(token->idata,
+ strings[NOT], &match) == lwc_error_ok &&
+ match) {
+ int old_ctx = *ctx;
+
+ parserutils_vector_iterate(vector, ctx);
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_peek(vector, *ctx);
+ if (tokenIsChar(token, '(')) {
+ is_condition = true;
+ }
+
+ *ctx = old_ctx;
+ }
+
+ result = malloc(sizeof(*result));
+ if (result == NULL) {
+ return CSS_NOMEM;
+ }
+ memset(result, 0, sizeof(*result));
+
+ if (is_condition) {
+ /* media-condition */
+ error = mq_parse_condition(strings, vector, ctx, true,
+ &result->cond);
+ if (error != CSS_OK) {
+ free(result);
+ return error;
+ }
+
+ *query = result;
+ return CSS_OK;
+ }
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token == NULL || token->type != CSS_TOKEN_IDENT) {
+ free(result);
+ return CSS_INVALID;
+ }
+
+ if (lwc_string_caseless_isequal(token->idata,
+ strings[NOT], &match) == lwc_error_ok && match) {
+ result->negate_type = 1;
+ consumeWhitespace(vector, ctx);
+ token = parserutils_vector_iterate(vector, ctx);
+ } else if (lwc_string_caseless_isequal(token->idata,
+ strings[ONLY], &match) == lwc_error_ok && match) {
+ consumeWhitespace(vector, ctx);
+ token = parserutils_vector_iterate(vector, ctx);
+ }
+
+ if (token == NULL || token->type != CSS_TOKEN_IDENT) {
+ free(result);
+ return CSS_INVALID;
+ }
+
+ result->type = mq_parse_type(strings, token->idata);
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token != NULL) {
+ if (token->type != CSS_TOKEN_IDENT ||
+ lwc_string_caseless_isequal(token->idata,
+ strings[AND], &match) != lwc_error_ok ||
+ match == false) {
+ free(result);
+ return CSS_INVALID;
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ error = mq_parse_condition(strings, vector, ctx, false,
+ &result->cond);
+ if (error != CSS_OK) {
+ free(result);
+ return error;
+ }
+ }
+
+ *query = result;
+ return CSS_OK;
+}
+
+css_error css__mq_parse_media_list(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ css_mq_query **media)
+{
+ css_mq_query *result = NULL, *last = NULL;
+ const css_token *token;
+ css_error error;
+
+ /* <media-query-list> = <media-query> [ COMMA <media-query> ]* */
+
+ /* if {[(, push }]) to stack
+ * if func, push ) to stack
+ * on error, scan forward until stack is empty (or EOF), popping matching tokens off stack
+ * if stack is empty, the next input token must be comma or EOF
+ * if comma, consume, and start again from the next input token
+ */
+
+ token = parserutils_vector_peek(vector, *ctx);
+ while (token != NULL) {
+ css_mq_query *query;
+
+ error = mq_parse_media_query(strings, vector, ctx, &query);
+ if (error != CSS_OK) {
+ /* TODO: error recovery (see above) */
+ css__mq_query_destroy(result);
+ return error;
+ } else {
+ if (result == NULL) {
+ result = last = query;
+ } else {
+ assert(last != NULL);
+ last->next = query;
+ last = query;
+ }
+ }
+
+ consumeWhitespace(vector, ctx);
+
+ token = parserutils_vector_iterate(vector, ctx);
+ if (token != NULL && tokenIsChar(token, ',') == false) {
+ /* Give up */
+ break;
+ }
+ }
+
+ *media = result;
+
+ return CSS_OK;
+}
+
+typedef struct css_mq_parse_ctx {
+ lwc_string **strings;
+ css_mq_query *media;
+} css_mq_parse_ctx;
+
+static css_error css_parse_media_query_handle_event(
+ css_parser_event type,
+ const parserutils_vector *tokens,
+ void *pw)
+{
+ int idx = 0;
+ css_error err;
+ css_mq_query *media;
+ const css_token *tok;
+ css_mq_parse_ctx *ctx = pw;
+ lwc_string **strings = ctx->strings;
+
+ UNUSED(type);
+
+ /* Skip @media */
+ tok = parserutils_vector_iterate(tokens, &idx);
+ assert(tok->type == CSS_TOKEN_ATKEYWORD);
+ UNUSED(tok);
+
+ /* Skip whitespace */
+ tok = parserutils_vector_iterate(tokens, &idx);
+ assert(tok->type == CSS_TOKEN_S);
+ UNUSED(tok);
+
+ err = css__mq_parse_media_list(strings, tokens, &idx, &media);
+ if (err != CSS_OK) {
+ return CSS_OK;
+ }
+
+ ctx->media = media;
+ return CSS_OK;
+}
+
+css_error css_parse_media_query(lwc_string **strings,
+ const uint8_t *mq, size_t len,
+ css_mq_query **media_out)
+{
+ css_error err;
+ css_parser *parser;
+ css_mq_parse_ctx ctx = {
+ .strings = strings,
+ };
+ css_parser_optparams params_quirks = {
+ .quirks = false,
+ };
+ css_parser_optparams params_handler = {
+ .event_handler = {
+ .handler = css_parse_media_query_handle_event,
+ .pw = &ctx,
+ },
+ };
+
+ if (mq == NULL || len == 0) {
+ return CSS_BADPARM;
+ }
+
+ err = css__parser_create_for_media_query(NULL,
+ CSS_CHARSET_DEFAULT, &parser);
+ if (err != CSS_OK) {
+ return err;
+ }
+
+ err = css__parser_setopt(parser, CSS_PARSER_QUIRKS,
+ &params_quirks);
+ if (err != CSS_OK) {
+ css__parser_destroy(parser);
+ return err;
+ }
+
+ err = css__parser_setopt(parser, CSS_PARSER_EVENT_HANDLER,
+ &params_handler);
+ if (err != CSS_OK) {
+ css__parser_destroy(parser);
+ return err;
+ }
+
+ err = css__parser_parse_chunk(parser,
+ (const uint8_t *)"@media ",
+ strlen("@media "));
+ if (err != CSS_OK && err != CSS_NEEDDATA) {
+ css__parser_destroy(parser);
+ return err;
+ }
+
+ err = css__parser_parse_chunk(parser, mq, len);
+ if (err != CSS_OK && err != CSS_NEEDDATA) {
+ css__parser_destroy(parser);
+ return err;
+ }
+
+ err = css__parser_completed(parser);
+ if (err != CSS_OK) {
+ css__parser_destroy(parser);
+ return err;
+ }
+
+ css__parser_destroy(parser);
+
+ *media_out = ctx.media;
+ return CSS_OK;
+}
+
diff --git a/src/parse/mq.h b/src/parse/mq.h
new file mode 100644
index 0000000..0e2f845
--- /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 <jmb@netsurf-browser.org>
+ */
+
+#ifndef css_parse_mq_h_
+#define css_parse_mq_h_
+
+#include <parserutils/utils/vector.h>
+#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;
+ uint32_t 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"
+ * "value op name" is encoded verbatim (with op2 set to "unused")
+ * "name op value" 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" */
+ uint64_t type; /* or NULL */
+
+ css_mq_cond *cond;
+} css_mq_query;
+
+css_error css_parse_media_query(lwc_string **strings,
+ const uint8_t *mq, size_t len,
+ css_mq_query **media_out);
+
+css_error css__mq_parse_media_list(lwc_string **strings,
+ const parserutils_vector *vector, int *ctx,
+ css_mq_query **media);
+
+void css__mq_query_destroy(css_mq_query *media);
+
+#endif
diff --git a/src/parse/parse.c b/src/parse/parse.c
index 4cc1c98..cbd8b56 100644
--- a/src/parse/parse.c
+++ b/src/parse/parse.c
@@ -67,7 +67,8 @@ enum {
sMalformedAtRule = 22,
sInlineStyle = 23,
sISBody0 = 24,
- sISBody = 25
+ sISBody = 25,
+ sMediaQuery = 26,
};
/**
@@ -99,8 +100,6 @@ struct css_parser
bool parseError; /**< A parse error has occurred */
parserutils_stack *open_items; /**< Stack of open brackets */
- uint8_t match_char; /**< Close bracket type for parseAny */
-
bool last_was_ws; /**< Last token was whitespace */
css_parser_event_handler event; /**< Client's event handler */
@@ -146,8 +145,9 @@ static css_error parseMalformedAtRule(css_parser *parser);
static css_error parseInlineStyle(css_parser *parser);
static css_error parseISBody0(css_parser *parser);
static css_error parseISBody(css_parser *parser);
+static css_error parseMediaQuery(css_parser *parser);
-static void unref_interned_strings_in_tokens(css_parser *parser);
+static void discard_tokens(css_parser *parser);
/**
* Dispatch table for parsing, indexed by major state number
@@ -178,7 +178,8 @@ static css_error (*parseFuncs[])(css_parser *parser) = {
parseMalformedAtRule,
parseInlineStyle,
parseISBody0,
- parseISBody
+ parseISBody,
+ parseMediaQuery,
};
/**
@@ -220,6 +221,25 @@ css_error css__parser_create_for_inline_style(const char *charset,
}
/**
+ * Create a CSS parser for a media query
+ *
+ * \param charset Charset of data, if known, or NULL
+ * \param cs_source Source of charset information, or CSS_CHARSET_DEFAULT
+ * \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_media_query(const char *charset,
+ css_charset_source cs_source, css_parser **parser)
+{
+ parser_state initial = { sMediaQuery, 0 };
+
+ return css__parser_create_internal(charset, cs_source,
+ initial, parser);
+}
+
+/**
* Destroy a CSS parser
*
* \param parser The parser instance to destroy
@@ -459,7 +479,6 @@ css_error css__parser_create_internal(const char *charset,
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;
@@ -746,8 +765,7 @@ css_error parseStart(css_parser *parser)
parser->event_pw);
}
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -778,8 +796,7 @@ css_error parseStylesheet(css_parser *parser)
if (error != CSS_OK)
return error;
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
case CSS_TOKEN_CDO:
@@ -849,8 +866,7 @@ css_error parseRuleset(css_parser *parser)
switch (state->substate) {
case Initial:
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
error = getToken(parser, &token);
if (error != CSS_OK)
@@ -890,17 +906,6 @@ css_error parseRuleset(css_parser *parser)
}
break;
case Brace:
-#if !defined(NDEBUG) && defined(DEBUG_EVENTS)
- printf("Begin ruleset\n");
- parserutils_vector_dump(parser->tokens, __func__, tprinter);
-#endif
- if (parser->parseError == false && parser->event != NULL) {
- if (parser->event(CSS_PARSER_START_RULESET,
- parser->tokens, parser->event_pw) ==
- CSS_INVALID)
- parser->parseError = true;
- }
-
if (parser->parseError == true) {
parser_state to = { sMalformedSelector, Initial };
@@ -911,22 +916,40 @@ css_error parseRuleset(css_parser *parser)
if (error != CSS_OK)
return error;
- if (token->type == CSS_TOKEN_EOF) {
+ if (token->type != CSS_TOKEN_CHAR ||
+ lwc_string_length(token->idata) != 1 ||
+ lwc_string_data(token->idata)[0] != '{') {
+ /* FOLLOW(selector) contains only '{', but we may
+ * also have seen EOF, which is a parse error. */
error = pushBack(parser, token);
if (error != CSS_OK)
return error;
+ parser->parseError = true;
return done(parser);
}
- if (token->type != CSS_TOKEN_CHAR ||
- lwc_string_length(token->idata) != 1 ||
- lwc_string_data(token->idata)[0] != '{') {
- /* This should never happen, as FOLLOW(selector)
- * contains only '{' */
- assert(0 && "Expected {");
+ /* We don't want to emit the brace, so push it back */
+ error = pushBack(parser, token);
+ if (error != CSS_OK)
+ return error;
+
+#if !defined(NDEBUG) && defined(DEBUG_EVENTS)
+ printf("Begin ruleset\n");
+ parserutils_vector_dump(parser->tokens, __func__, tprinter);
+#endif
+ if (parser->parseError == false && parser->event != NULL) {
+ if (parser->event(CSS_PARSER_START_RULESET,
+ parser->tokens, parser->event_pw) ==
+ CSS_INVALID)
+ parser->parseError = true;
}
+ /* Re-read the brace */
+ error = getToken(parser, &token);
+ if (error != CSS_OK)
+ return error;
+
state->substate = WS;
/* Fall through */
case WS:
@@ -1041,8 +1064,7 @@ css_error parseAtRule(css_parser *parser)
switch (state->substate) {
case Initial:
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
error = getToken(parser, &token);
if (error != CSS_OK)
@@ -1212,8 +1234,7 @@ css_error parseBlock(css_parser *parser)
assert(0 && "Expected {");
}
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
state->substate = WS;
/* Fall through */
@@ -1269,8 +1290,7 @@ css_error parseBlock(css_parser *parser)
parser->event(CSS_PARSER_END_BLOCK, NULL, parser->event_pw);
}
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -1322,10 +1342,7 @@ css_error parseBlockContent(css_parser *parser)
parser->event_pw);
}
- unref_interned_strings_in_tokens(
- parser);
- parserutils_vector_clear(
- parser->tokens);
+ discard_tokens(parser);
return transition(parser, to,
subsequent);
@@ -1353,10 +1370,7 @@ css_error parseBlockContent(css_parser *parser)
if (error != CSS_OK)
return error;
- unref_interned_strings_in_tokens(
- parser);
- parserutils_vector_clear(
- parser->tokens);
+ discard_tokens(parser);
state->substate = WS;
} else if (lwc_string_length(
@@ -1379,10 +1393,7 @@ css_error parseBlockContent(css_parser *parser)
parser->event_pw);
}
- unref_interned_strings_in_tokens(
- parser);
- parserutils_vector_clear(
- parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -1401,8 +1412,7 @@ css_error parseBlockContent(css_parser *parser)
parser->event_pw);
}
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -1445,8 +1455,7 @@ css_error parseSelector(css_parser *parser)
parser_state to = { sAny1, Initial };
parser_state subsequent = { sSelector, AfterAny1 };
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return transition(parser, to, subsequent);
}
@@ -1472,8 +1481,7 @@ css_error parseDeclaration(css_parser *parser)
parser_state to = { sProperty, Initial };
parser_state subsequent = { sDeclaration, Colon };
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return transition(parser, to, subsequent);
}
@@ -1945,6 +1953,9 @@ css_error parseAny1(css_parser *parser)
if (error != CSS_OK)
return error;
+ if (token->type == CSS_TOKEN_EOF)
+ return done(parser);
+
/* Grammar ambiguity: any0 can be followed by
* '{', ';', ')', ']'. any1 can only be followed by '{'. */
if (token->type == CSS_TOKEN_CHAR &&
@@ -2025,18 +2036,19 @@ css_error parseAny(css_parser *parser)
}
if (token->type == CSS_TOKEN_FUNCTION) {
- parser->match_char = ')';
+ parserutils_stack_push(parser->open_items, &")"[0]);
state->substate = WS;
} else if (token->type == CSS_TOKEN_CHAR &&
lwc_string_length(token->idata) == 1 &&
(lwc_string_data(token->idata)[0] == '(' ||
lwc_string_data(token->idata)[0] == '[')) {
- parser->match_char = lwc_string_data(
- token->idata)[0] == '(' ? ')' : ']';
+ parserutils_stack_push(parser->open_items,
+ &(lwc_string_data(
+ token->idata)[0] == '(' ? ")" : "]")[0]);
state->substate = WS;
+ } else {
+ state->substate = WS2;
}
-
- state->substate = WS2;
/* Fall through */
case WS:
case WS2:
@@ -2063,11 +2075,24 @@ css_error parseAny(css_parser *parser)
if (error != CSS_OK)
return error;
+ if (token->type == CSS_TOKEN_EOF) {
+ error = pushBack(parser, token);
+ if (error != CSS_OK)
+ return error;
+
+ /* parse error */
+ parser->parseError = true;
+
+ return done(parser);
+ }
+
/* Match correct close bracket (grammar ambiguity) */
if (token->type == CSS_TOKEN_CHAR &&
lwc_string_length(token->idata) == 1 &&
lwc_string_data(token->idata)[0] ==
- parser->match_char) {
+ ((uint8_t *) parserutils_stack_get_current(
+ parser->open_items))[0]) {
+ parserutils_stack_pop(parser->open_items, NULL);
state->substate = WS2;
goto ws2;
}
@@ -2174,8 +2199,7 @@ css_error parseMalformedDeclaration(css_parser *parser)
return error;
/* Discard the tokens we've read */
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -2270,8 +2294,7 @@ css_error parseMalformedSelector(css_parser *parser)
return error;
/* Discard the tokens we've read */
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -2379,8 +2402,7 @@ css_error parseMalformedAtRule(css_parser *parser)
return error;
/* Discard the tokens we've read */
- unref_interned_strings_in_tokens(parser);
- parserutils_vector_clear(parser->tokens);
+ discard_tokens(parser);
return done(parser);
}
@@ -2425,7 +2447,7 @@ css_error parseInlineStyle(css_parser *parser)
}
case AfterISBody0:
/* Clean up any remaining tokens */
- unref_interned_strings_in_tokens(parser);
+ discard_tokens(parser);
/* Emit remaining fake events to end the parse */
if (parser->event != NULL) {
@@ -2571,22 +2593,48 @@ css_error parseISBody(css_parser *parser)
return done(parser);
}
+css_error parseMediaQuery(css_parser *parser)
+{
+ enum { Initial = 0, AfterAtRule = 1 };
+ parser_state *state = parserutils_stack_get_current(parser->states);
+
+ /* media-query = at-rule */
+
+ switch (state->substate) {
+ case Initial:
+ {
+ parser_state to = { sAtRule, Initial };
+ parser_state subsequent = { sMediaQuery, AfterAtRule };
+
+ return transition(parser, to, subsequent);
+ }
+ case AfterAtRule:
+ /* Clean up any remaining tokens */
+ discard_tokens(parser);
+ break;
+ }
+
+ return done(parser);
+}
+
/**
- * Iterate the token vector and unref any interned strings in the tokens.
+ * Discard the contents of the token vector
*
* \param parser The parser whose tokens we are cleaning up.
*/
-void unref_interned_strings_in_tokens(css_parser *parser)
+void discard_tokens(css_parser *parser)
{
- int32_t ctx = 0;
- const css_token *tok;
+ int32_t ctx = 0;
+ const css_token *tok;
- while ((tok = parserutils_vector_iterate(
+ while ((tok = parserutils_vector_iterate(
parser->tokens, &ctx)) != NULL) {
- if (tok->idata != NULL) {
- lwc_string_unref(tok->idata);
+ if (tok->idata != NULL) {
+ lwc_string_unref(tok->idata);
}
- }
+ }
+
+ parserutils_vector_clear(parser->tokens);
}
#ifndef NDEBUG
@@ -2604,7 +2652,11 @@ static void tprinter(void *token)
{
css_token *t = token;
- if (t->data.data)
+ if (t->idata) {
+ printf("%d: %.*s", t->type,
+ (int) lwc_string_length(t->idata),
+ lwc_string_data(t->idata));
+ } else if (t->data.data)
printf("%d: %.*s", t->type, (int) t->data.len, t->data.data);
else
printf("%d", t->type);
diff --git a/src/parse/parse.h b/src/parse/parse.h
index 833aa51..e65f055 100644
--- a/src/parse/parse.h
+++ b/src/parse/parse.h
@@ -61,6 +61,8 @@ css_error css__parser_create(const char *charset, css_charset_source cs_source,
css_parser **parser);
css_error css__parser_create_for_inline_style(const char *charset,
css_charset_source cs_source, css_parser **parser);
+css_error css__parser_create_for_media_query(const char *charset,
+ css_charset_source cs_source, 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/parse/properties/utils.c b/src/parse/properties/utils.c
index 7abef24..0e49853 100644
--- a/src/parse/properties/utils.c
+++ b/src/parse/properties/utils.c
@@ -1007,6 +1007,12 @@ css_error css__parse_unit_keyword(const char *ptr, size_t len, uint32_t *unit)
if (len == 4) {
if (strncasecmp(ptr, "grad", 4) == 0)
*unit = UNIT_GRAD;
+ else if (strncasecmp(ptr, "turn", 4) == 0)
+ *unit = UNIT_TURN;
+ else if (strncasecmp(ptr, "dppx", 4) == 0)
+ *unit = UNIT_DPPX;
+ else if (strncasecmp(ptr, "dpcm", 4) == 0)
+ *unit = UNIT_DPCM;
else if (strncasecmp(ptr, "vmin", 4) == 0)
*unit = UNIT_VMIN;
else if (strncasecmp(ptr, "vmax", 4) == 0)
@@ -1026,6 +1032,8 @@ css_error css__parse_unit_keyword(const char *ptr, size_t len, uint32_t *unit)
*unit = UNIT_REM;
else if (strncasecmp(ptr, "rlh", 3) == 0)
*unit = UNIT_RLH;
+ else if (strncasecmp(ptr, "dpi", 3) == 0)
+ *unit = UNIT_DPI;
else
return CSS_INVALID;
} else if (len == 2) {
diff --git a/src/parse/propstrings.c b/src/parse/propstrings.c
index bfd2965..3c9401b 100644
--- a/src/parse/propstrings.c
+++ b/src/parse/propstrings.c
@@ -439,6 +439,10 @@ const stringmap_entry stringmap[LAST_KNOWN] = {
{ "column-reverse", SLEN("column-reverse") },
{ "wrap", SLEN("wrap") },
{ "wrap-reverse", SLEN("wrap-reverse") },
+ { "and", SLEN("and") },
+ { "or", SLEN("or") },
+ { "only", SLEN("only") },
+ { "infinite", SLEN("infinite") },
{ "aliceblue", SLEN("aliceblue") },
{ "antiquewhite", SLEN("antiquewhite") },
diff --git a/src/parse/propstrings.h b/src/parse/propstrings.h
index 67eaa5f..24b681b 100644
--- a/src/parse/propstrings.h
+++ b/src/parse/propstrings.h
@@ -101,7 +101,7 @@ enum {
AVOID_PAGE, AVOID_COLUMN, BALANCE, HORIZONTAL_TB, VERTICAL_RL,
VERTICAL_LR, CONTENT_BOX, BORDER_BOX, STRETCH, INLINE_FLEX, FLEX_START,
FLEX_END, SPACE_BETWEEN, SPACE_AROUND, SPACE_EVENLY, ROW, ROW_REVERSE,
- COLUMN_REVERSE, WRAP_STRING, WRAP_REVERSE,
+ COLUMN_REVERSE, WRAP_STRING, WRAP_REVERSE, AND, OR, ONLY, INFINITE,
/* Named colours */
FIRST_COLOUR,
diff --git a/src/select/computed.c b/src/select/computed.c
index ebb2b29..506b079 100644
--- a/src/select/computed.c
+++ b/src/select/computed.c
@@ -250,7 +250,7 @@ css_error css__computed_style_initialise(css_computed_style *style,
return CSS_BADPARM;
state.node = NULL;
- state.media = CSS_MEDIA_ALL;
+ state.media = NULL;
state.results = NULL;
state.computed = style;
state.handler = handler;
diff --git a/src/select/hash.h b/src/select/hash.h
index 71f610f..aecf15a 100644
--- a/src/select/hash.h
+++ b/src/select/hash.h
@@ -25,7 +25,7 @@ struct css_hash_selection_requirments {
lwc_string *class; /* Name of class, or NULL */
lwc_string *id; /* Name of id, or NULL */
lwc_string *uni; /* Universal element string "*" */
- uint64_t media; /* Media type(s) we're selecting for */
+ const css_media *media; /* Media spec we're selecting for */
const css_bloom *node_bloom; /* Node's bloom filter */
};
diff --git a/src/select/mq.h b/src/select/mq.h
index a0a9f6d..290505c 100644
--- a/src/select/mq.h
+++ b/src/select/mq.h
@@ -10,13 +10,51 @@
#define css_select_mq_h_
/**
+ * Match media query conditions.
+ *
+ * \param[in] cond Condition to match.
+ * \return true if condition matches, otherwise false.
+ */
+static inline bool mq_match_condition(css_mq_cond *cond)
+{
+ /* TODO: Implement this. */
+ (void) cond;
+ return true;
+}
+
+/**
+ * Test whether media query list matches current media.
+ *
+ * If anything in the list matches, the list matches. If none match
+ * it doesn't match.
+ *
+ * \param m Media query list.
+ * \meaid media Current media spec, to check against m.
+ * \return true if media query list matches media
+ */
+static inline bool mq__list_match(const css_mq_query *m, const css_media *media)
+{
+ for (; m != NULL; m = m->next) {
+ /* Check type */
+ if (!!(m->type & media->type) != m->negate_type) {
+ if (mq_match_condition(m->cond)) {
+ /* We have a match, no need to look further. */
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/**
* Test whether the rule applies for current media.
*
- * \param rule Rule to test
- * \meaid media Current media type(s)
+ * \param rule Rule to test
+ * \param media Current media type(s)
* \return true iff chain's rule applies for media
*/
-static inline bool mq_rule_good_for_media(const css_rule *rule, uint64_t media)
+static inline bool mq_rule_good_for_media(const css_rule *rule, const css_media *media)
{
bool applies = true;
const css_rule *ancestor = rule;
@@ -24,10 +62,11 @@ static inline bool mq_rule_good_for_media(const css_rule *rule, uint64_t media)
while (ancestor != NULL) {
const css_rule_media *m = (const css_rule_media *) ancestor;
- if (ancestor->type == CSS_RULE_MEDIA &&
- (m->media & media) == 0) {
- applies = false;
- break;
+ if (ancestor->type == CSS_RULE_MEDIA) {
+ applies = mq__list_match(m->media, media);
+ if (applies == false) {
+ break;
+ }
}
if (ancestor->ptype != CSS_RULE_PARENT_STYLESHEET) {
diff --git a/src/select/select.c b/src/select/select.c
index 644369a..1b0cadd 100644
--- a/src/select/select.c
+++ b/src/select/select.c
@@ -38,7 +38,7 @@
typedef struct css_select_sheet {
const css_stylesheet *sheet; /**< Stylesheet */
css_origin origin; /**< Stylesheet origin */
- uint64_t media; /**< Applicable media */
+ css_mq_query *media; /**< Applicable media */
} css_select_sheet;
/**
@@ -97,7 +97,7 @@ typedef struct css_select_font_faces_list {
*/
typedef struct css_select_font_faces_state {
lwc_string *font_family;
- uint64_t media;
+ const css_media *media;
css_select_font_faces_list ua_font_faces;
css_select_font_faces_list user_font_faces;
@@ -289,8 +289,12 @@ css_error css_select_ctx_destroy(css_select_ctx *ctx)
if (ctx->default_style != NULL)
css_computed_style_destroy(ctx->default_style);
- if (ctx->sheets != NULL)
+ if (ctx->sheets != NULL) {
+ for (uint32_t index = 0; index < ctx->n_sheets; index++) {
+ css__mq_query_destroy(ctx->sheets[index].media);
+ }
free(ctx->sheets);
+ }
free(ctx);
@@ -303,12 +307,12 @@ css_error css_select_ctx_destroy(css_select_ctx *ctx)
* \param ctx The context to append to
* \param sheet The sheet to append
* \param origin Origin of the sheet
- * \param media Media types to which the sheet applies
+ * \param media Media string for the stylesheet
* \return CSS_OK on success, appropriate error otherwise
*/
css_error css_select_ctx_append_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet, css_origin origin,
- uint64_t media)
+ const char *media)
{
if (ctx == NULL || sheet == NULL)
return CSS_BADPARM;
@@ -324,14 +328,16 @@ css_error css_select_ctx_append_sheet(css_select_ctx *ctx,
* \param sheet Sheet to insert
* \param index Index in context to insert sheet
* \param origin Origin of the sheet
- * \param media Media types to which the sheet applies
+ * \param media Media string for the stylesheet
* \return CSS_OK on success, appropriate error otherwise
*/
css_error css_select_ctx_insert_sheet(css_select_ctx *ctx,
const css_stylesheet *sheet, uint32_t index,
- css_origin origin, uint64_t media)
+ css_origin origin, const char *media)
{
css_select_sheet *temp;
+ css_mq_query *mq;
+ css_error error;
if (ctx == NULL || sheet == NULL)
return CSS_BADPARM;
@@ -357,9 +363,23 @@ css_error css_select_ctx_insert_sheet(css_select_ctx *ctx,
(ctx->n_sheets - index) * sizeof(css_select_sheet));
}
+ error = css_parse_media_query(sheet->propstrings,
+ (const uint8_t *)media,
+ (media == NULL) ? 0 : strlen(media), &mq);
+ if (error == CSS_NOMEM) {
+ return error;
+ } else if (error != CSS_OK) {
+ /* Fall back to default media: "all". */
+ mq = calloc(1, sizeof(*mq));
+ if (mq == NULL) {
+ return CSS_NOMEM;
+ }
+ mq->type = CSS_MEDIA_ALL;
+ }
+
ctx->sheets[index].sheet = sheet;
ctx->sheets[index].origin = origin;
- ctx->sheets[index].media = media;
+ ctx->sheets[index].media = mq;
ctx->n_sheets++;
@@ -389,6 +409,8 @@ css_error css_select_ctx_remove_sheet(css_select_ctx *ctx,
if (index == ctx->n_sheets)
return CSS_INVALID;
+ css__mq_query_destroy(ctx->sheets[index].media);
+
ctx->n_sheets--;
memmove(&ctx->sheets[index], &ctx->sheets[index + 1],
@@ -1032,7 +1054,7 @@ static void css_select__finalise_selection_state(
* \param[in] state The selection state to initialise
* \param[in] node The node we are selecting for.
* \param[in] parent The node's parent node, or NULL.
- * \param[in] media The media type we're selecting for.
+ * \param[in] media The media specification we're selecting for.
* \param[in] handler The client selection callback table.
* \param[in] pw The client private data, passsed out to callbacks.
* \return CSS_OK or appropriate error otherwise.
@@ -1041,7 +1063,7 @@ static css_error css_select__initialise_selection_state(
css_select_state *state,
void *node,
void *parent,
- uint64_t media,
+ const css_media *media,
css_select_handler *handler,
void *pw)
{
@@ -1142,7 +1164,7 @@ failed:
*
* \param ctx Selection context to use
* \param node Node to select style for
- * \param media Currently active media types
+ * \param media Currently active media specification
* \param inline_style Corresponding inline style for node, or NULL
* \param handler Dispatch table of handler functions
* \param pw Client-specific private data for handler functions
@@ -1159,7 +1181,7 @@ failed:
* update the fully computed style for a node when layout changes.
*/
css_error css_select_style(css_select_ctx *ctx, void *node,
- uint64_t media, const css_stylesheet *inline_style,
+ const css_media *media, const css_stylesheet *inline_style,
css_select_handler *handler, void *pw,
css_select_results **result)
{
@@ -1244,7 +1266,7 @@ css_error css_select_style(css_select_ctx *ctx, void *node,
for (i = 0; i < ctx->n_sheets; i++) {
const css_select_sheet s = ctx->sheets[i];
- if ((s.media & media) != 0 &&
+ if (mq__list_match(s.media, media) &&
s.sheet->disabled == false) {
error = select_from_sheet(ctx, s.sheet,
s.origin, &state);
@@ -1394,13 +1416,13 @@ css_error css_select_results_destroy(css_select_results *results)
* Search a selection context for defined font faces
*
* \param ctx Selection context
- * \param media Currently active media types
+ * \param media Currently active media spec
* \param font_family Font family to search for
* \param result Pointer to location to receive result
* \return CSS_OK on success, appropriate error otherwise.
*/
css_error css_select_font_faces(css_select_ctx *ctx,
- uint64_t media, lwc_string *font_family,
+ const css_media *media, lwc_string *font_family,
css_select_font_faces_results **result)
{
uint32_t i;
@@ -1421,7 +1443,7 @@ css_error css_select_font_faces(css_select_ctx *ctx,
for (i = 0; i < ctx->n_sheets; i++) {
const css_select_sheet s = ctx->sheets[i];
- if ((s.media & media) != 0 &&
+ if (mq__list_match(s.media, media) &&
s.sheet->disabled == false) {
error = select_font_faces_from_sheet(s.sheet,
s.origin, &state);
@@ -1847,7 +1869,8 @@ css_error select_from_sheet(css_select_ctx *ctx, const css_stylesheet *sheet,
(const css_rule_import *) rule;
if (import->sheet != NULL &&
- (import->media & state->media) != 0) {
+ mq__list_match(import->media,
+ state->media)) {
/* It's applicable, so process it */
if (sp >= IMPORT_STACK_SIZE)
return CSS_NOMEM;
@@ -1954,7 +1977,8 @@ static css_error select_font_faces_from_sheet(
(const css_rule_import *) rule;
if (import->sheet != NULL &&
- (import->media & state->media) != 0) {
+ mq__list_match(import->media,
+ state->media)) {
/* It's applicable, so process it */
if (sp >= IMPORT_STACK_SIZE)
return CSS_NOMEM;
diff --git a/src/select/select.h b/src/select/select.h
index 70f1ced..dc9aa4a 100644
--- a/src/select/select.h
+++ b/src/select/select.h
@@ -63,7 +63,7 @@ struct css_node_data {
*/
typedef struct css_select_state {
void *node; /* Node we're selecting for */
- uint64_t media; /* Currently active media types */
+ const css_media *media; /* Currently active media spec */
css_select_results *results; /* Result set to populate */
css_pseudo_element current_pseudo; /* Current pseudo element */
diff --git a/src/stylesheet.c b/src/stylesheet.c
index 7c6728b..22c7681 100644
--- a/src/stylesheet.c
+++ b/src/stylesheet.c
@@ -12,6 +12,7 @@
#include "stylesheet.h"
#include "bytecode/bytecode.h"
#include "parse/language.h"
+#include "parse/mq.h"
#include "utils/parserutilserror.h"
#include "utils/utils.h"
#include "select/dispatch.h"
@@ -377,8 +378,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 +395,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 +412,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;
}
@@ -1154,6 +1152,9 @@ css_error css__stylesheet_rule_destroy(css_stylesheet *sheet, css_rule *rule)
css_rule_import *import = (css_rule_import *) rule;
lwc_string_unref(import->url);
+ if (import->media != NULL) {
+ css__mq_query_destroy(import->media);
+ }
/* Do not destroy imported sheet: it is owned by the client */
}
@@ -1163,6 +1164,10 @@ css_error css__stylesheet_rule_destroy(css_stylesheet *sheet, css_rule *rule)
css_rule_media *media = (css_rule_media *) rule;
css_rule *c, *d;
+ if (media->media != NULL) {
+ css__mq_query_destroy(media->media);
+ }
+
for (c = media->first_child; c != NULL; c = d) {
d = c->next;
@@ -1326,7 +1331,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;
@@ -1352,7 +1357,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;
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);
diff --git a/test/css21.c b/test/css21.c
index a29fae1..cdd66f7 100644
--- a/test/css21.c
+++ b/test/css21.c
@@ -99,10 +99,8 @@ int main(int argc, char **argv)
while (error == CSS_IMPORTS_PENDING) {
lwc_string *url;
- uint64_t media;
- error = css_stylesheet_next_pending_import(sheet,
- &url, &media);
+ error = css_stylesheet_next_pending_import(sheet, &url);
assert(error == CSS_OK || error == CSS_INVALID);
if (error == CSS_OK) {
diff --git a/test/data/parse2/illegal-values.dat b/test/data/parse2/illegal-values.dat
index 3187e18..2d58b54 100644
--- a/test/data/parse2/illegal-values.dat
+++ b/test/data/parse2/illegal-values.dat
@@ -864,7 +864,7 @@
#reset
#data
-* { display: 
+* { display: 
#errors
#expected
| *
diff --git a/test/parse-auto.c b/test/parse-auto.c
index 58ccf9a..5f926e3 100644
--- a/test/parse-auto.c
+++ b/test/parse-auto.c
@@ -395,10 +395,8 @@ void run_test(const uint8_t *data, size_t len, exp_entry *exp, size_t explen)
while (error == CSS_IMPORTS_PENDING) {
lwc_string *url;
- uint64_t media;
- error = css_stylesheet_next_pending_import(sheet,
- &url, &media);
+ error = css_stylesheet_next_pending_import(sheet, &url);
assert(error == CSS_OK || error == CSS_INVALID);
if (error == CSS_OK) {
diff --git a/test/select.c b/test/select.c
index f21d937..664994e 100644
--- a/test/select.c
+++ b/test/select.c
@@ -42,7 +42,7 @@ typedef struct node {
typedef struct sheet_ctx {
css_stylesheet *sheet;
css_origin origin;
- uint64_t media;
+ char *media;
} sheet_ctx;
typedef struct line_ctx {
@@ -62,7 +62,7 @@ typedef struct line_ctx {
uint32_t n_sheets;
sheet_ctx *sheets;
- uint64_t media;
+ css_media media;
uint32_t pseudo_element;
node *target;
@@ -77,7 +77,7 @@ static bool handle_line(const char *data, size_t datalen, void *pw);
static void css__parse_tree(line_ctx *ctx, const char *data, size_t len);
static void css__parse_tree_data(line_ctx *ctx, const char *data, size_t len);
static void css__parse_sheet(line_ctx *ctx, const char *data, size_t len);
-static void css__parse_media_list(const char **data, size_t *len, uint64_t *media);
+static void css__parse_media_list(const char **data, size_t *len, css_media *media);
static void css__parse_pseudo_list(const char **data, size_t *len,
uint32_t *element);
static void css__parse_expected(line_ctx *ctx, const char *data, size_t len);
@@ -363,7 +363,7 @@ void css__parse_tree(line_ctx *ctx, const char *data, size_t len)
/* [ <media_list> <pseudo>? ] ? */
- ctx->media = CSS_MEDIA_ALL;
+ ctx->media.type = CSS_MEDIA_ALL;
ctx->pseudo_element = CSS_PSEUDO_ELEMENT_NONE;
/* Consume any leading whitespace */
@@ -515,9 +515,9 @@ void css__parse_sheet(line_ctx *ctx, const char *data, size_t len)
const char *p;
const char *end = data + len;
css_origin origin = CSS_ORIGIN_AUTHOR;
- uint64_t media = CSS_MEDIA_ALL;
css_stylesheet *sheet;
sheet_ctx *temp;
+ char *media = NULL;
/* <origin> <media_list>? */
@@ -540,11 +540,11 @@ void css__parse_sheet(line_ctx *ctx, const char *data, size_t len)
while (p < end && isspace(*p))
p++;
- if (p < end) {
- size_t ignored = end - p;
-
- css__parse_media_list(&p, &ignored, &media);
- }
+ assert(end >= p);
+ media = malloc(end - p + 1);
+ assert(media != NULL);
+ memcpy(media, p, end - p);
+ media[end - p] = '\0';
params.params_version = CSS_STYLESHEET_PARAMS_VERSION_1;
params.level = CSS_LEVEL_21;
@@ -579,7 +579,7 @@ void css__parse_sheet(line_ctx *ctx, const char *data, size_t len)
ctx->n_sheets++;
}
-void css__parse_media_list(const char **data, size_t *len, uint64_t *media)
+void css__parse_media_list(const char **data, size_t *len, css_media *media)
{
const char *p = *data;
const char *end = p + *len;
@@ -646,7 +646,7 @@ void css__parse_media_list(const char **data, size_t *len, uint64_t *media)
p++;
}
- *media = result;
+ media->type = result;
*data = p;
*len = end - p;
@@ -765,7 +765,7 @@ static void run_test_select_tree(css_select_ctx *select,
css_select_results *sr;
struct node *n = NULL;
- assert(css_select_style(select, node, ctx->media, NULL,
+ assert(css_select_style(select, node, &ctx->media, NULL,
&select_handler, ctx, &sr) == CSS_OK);
if (node->parent != NULL) {
@@ -841,6 +841,7 @@ void run_test(line_ctx *ctx, const char *exp, size_t explen)
for (i = 0; i < ctx->n_sheets; i++) {
css_stylesheet_destroy(ctx->sheets[i].sheet);
+ free(ctx->sheets[i].media);
}
ctx->tree = NULL;