1 /*
2  * This file is part of LibCSS.
3  * Licensed under the MIT License,
4  *                http://www.opensource.org/licenses/mit-license.php
5  * Copyright 2008 John-Mark Bell <jmb@netsurf-browser.org>
6  */
7 
8 #include <assert.h>
9 #include <ctype.h>
10 #include <stdbool.h>
11 
12 #include <libwapcaplet/libwapcaplet.h>
13 
14 #include <parserutils/input/inputstream.h>
15 #include <parserutils/utils/stack.h>
16 #include <parserutils/utils/vector.h>
17 
18 #include "charset/detect.h"
19 #include "lex/lex.h"
20 #include "parse/parse.h"
21 #include "utils/parserutilserror.h"
22 #include "utils/utils.h"
23 
24 #undef DEBUG_STACK
25 #undef DEBUG_EVENTS
26 
27 #ifndef NDEBUG
28 #include <stdio.h>
29 extern void parserutils_stack_dump(parserutils_stack *stack, const char *prefix,
30 		void (*printer)(void *item));
31 extern void parserutils_vector_dump(parserutils_vector *vector,
32 		const char *prefix, void (*printer)(void *item));
33 #ifdef DEBUG_STACK
34 static void printer(void *item);
35 #endif
36 #ifdef DEBUG_EVENTS
37 static void tprinter(void *token);
38 #endif
39 #endif
40 
41 /**
42  * Major state numbers
43  */
44 enum {
45 	sStart = 0,
46 	sStylesheet = 1,
47 	sStatement = 2,
48 	sRuleset = 3,
49 	sRulesetEnd = 4,
50 	sAtRule = 5,
51 	sAtRuleEnd = 6,
52 	sBlock = 7,
53 	sBlockContent = 8,
54 	sSelector = 9,
55 	sDeclaration = 10,
56 	sDeclList = 11,
57 	sDeclListEnd = 12,
58 	sProperty = 13,
59 	sValue0 = 14,
60 	sValue1 = 15,
61 	sValue = 16,
62 	sAny0 = 17,
63 	sAny1 = 18,
64 	sAny = 19,
65 	sMalformedDecl = 20,
66 	sMalformedSelector = 21,
67 	sMalformedAtRule = 22,
68 	sInlineStyle = 23,
69 	sISBody0 = 24,
70 	sISBody = 25,
71 	sMediaQuery = 26,
72 };
73 
74 /**
75  * Representation of a parser state
76  */
77 typedef struct parser_state
78 {
79 	unsigned int state : 16,
80 	             substate : 16;
81 } parser_state;
82 
83 /**
84  * CSS parser object
85  */
86 struct css_parser
87 {
88 	parserutils_inputstream *stream;	/**< The inputstream */
89 	css_lexer *lexer;		/**< The lexer to use */
90 
91 	bool quirks;			/**< Whether to enable parsing quirks */
92 
93 #define STACK_CHUNK 32
94 	parserutils_stack *states;	/**< Stack of states */
95 
96 	parserutils_vector *tokens;	/**< Vector of pending tokens */
97 
98 	const css_token *pushback;	/**< Push back buffer */
99 
100 	bool parseError;		/**< A parse error has occurred */
101 	parserutils_stack *open_items;	/**< Stack of open brackets */
102 
103 	bool last_was_ws;		/**< Last token was whitespace */
104 
105 	css_parser_event_handler event;	/**< Client's event handler */
106 	void *event_pw;			/**< Client data for event handler */
107 };
108 
109 static css_error css__parser_create_internal(const char *charset,
110 		css_charset_source cs_source, parser_state initial,
111 		css_parser **parser);
112 
113 static css_error transition(css_parser *parser, parser_state to,
114 		parser_state subsequent);
115 static css_error transitionNoRet(css_parser *parser, parser_state to);
116 static css_error done(css_parser *parser);
117 static css_error expect(css_parser *parser, css_token_type type);
118 static css_error getToken(css_parser *parser, const css_token **token);
119 static css_error pushBack(css_parser *parser, const css_token *token);
120 static css_error eatWS(css_parser *parser);
121 
122 static css_error parseStart(css_parser *parser);
123 static css_error parseStylesheet(css_parser *parser);
124 static css_error parseStatement(css_parser *parser);
125 static css_error parseRuleset(css_parser *parser);
126 static css_error parseRulesetEnd(css_parser *parser);
127 static css_error parseAtRule(css_parser *parser);
128 static css_error parseAtRuleEnd(css_parser *parser);
129 static css_error parseBlock(css_parser *parser);
130 static css_error parseBlockContent(css_parser *parser);
131 static css_error parseSelector(css_parser *parser);
132 static css_error parseDeclaration(css_parser *parser);
133 static css_error parseDeclList(css_parser *parser);
134 static css_error parseDeclListEnd(css_parser *parser);
135 static css_error parseProperty(css_parser *parser);
136 static css_error parseValue0(css_parser *parser);
137 static css_error parseValue1(css_parser *parser);
138 static css_error parseValue(css_parser *parser);
139 static css_error parseAny0(css_parser *parser);
140 static css_error parseAny1(css_parser *parser);
141 static css_error parseAny(css_parser *parser);
142 static css_error parseMalformedDeclaration(css_parser *parser);
143 static css_error parseMalformedSelector(css_parser *parser);
144 static css_error parseMalformedAtRule(css_parser *parser);
145 static css_error parseInlineStyle(css_parser *parser);
146 static css_error parseISBody0(css_parser *parser);
147 static css_error parseISBody(css_parser *parser);
148 static css_error parseMediaQuery(css_parser *parser);
149 
150 static void discard_tokens(css_parser *parser);
151 
152 /**
153  * Dispatch table for parsing, indexed by major state number
154  */
155 static css_error (*parseFuncs[])(css_parser *parser) = {
156 	parseStart,
157 	parseStylesheet,
158 	parseStatement,
159 	parseRuleset,
160 	parseRulesetEnd,
161 	parseAtRule,
162 	parseAtRuleEnd,
163 	parseBlock,
164 	parseBlockContent,
165 	parseSelector,
166 	parseDeclaration,
167 	parseDeclList,
168 	parseDeclListEnd,
169 	parseProperty,
170 	parseValue0,
171 	parseValue1,
172 	parseValue,
173 	parseAny0,
174 	parseAny1,
175 	parseAny,
176 	parseMalformedDeclaration,
177 	parseMalformedSelector,
178 	parseMalformedAtRule,
179 	parseInlineStyle,
180 	parseISBody0,
181 	parseISBody,
182 	parseMediaQuery,
183 };
184 
185 /**
186  * Create a CSS parser
187  *
188  * \param charset     Charset of data, if known, or NULL
189  * \param cs_source   Source of charset information, or CSS_CHARSET_DEFAULT
190  * \param parser      Pointer to location to receive parser instance
191  * \return CSS_OK on success,
192  *         CSS_BADPARM on bad parameters,
193  *         CSS_NOMEM on memory exhaustion
194  */
css__parser_create(const char * charset,css_charset_source cs_source,css_parser ** parser)195 css_error css__parser_create(const char *charset, css_charset_source cs_source,
196 		css_parser **parser)
197 {
198 	parser_state initial = { sStart, 0 };
199 
200 	return css__parser_create_internal(charset, cs_source,
201 			initial, parser);
202 }
203 
204 /**
205  * Create a CSS parser for an inline style
206  *
207  * \param charset     Charset of data, if known, or NULL
208  * \param cs_source   Source of charset information, or CSS_CHARSET_DEFAULT
209  * \param parser      Pointer to location to receive parser instance
210  * \return CSS_OK on success,
211  *         CSS_BADPARM on bad parameters,
212  *         CSS_NOMEM on memory exhaustion
213  */
css__parser_create_for_inline_style(const char * charset,css_charset_source cs_source,css_parser ** parser)214 css_error css__parser_create_for_inline_style(const char *charset,
215 		css_charset_source cs_source, css_parser **parser)
216 {
217 	parser_state initial = { sInlineStyle, 0 };
218 
219 	return css__parser_create_internal(charset, cs_source,
220 			initial, parser);
221 }
222 
223 /**
224  * Create a CSS parser for a media query
225  *
226  * \param charset     Charset of data, if known, or NULL
227  * \param cs_source   Source of charset information, or CSS_CHARSET_DEFAULT
228  * \param parser      Pointer to location to receive parser instance
229  * \return CSS_OK on success,
230  *         CSS_BADPARM on bad parameters,
231  *         CSS_NOMEM on memory exhaustion
232  */
css__parser_create_for_media_query(const char * charset,css_charset_source cs_source,css_parser ** parser)233 css_error css__parser_create_for_media_query(const char *charset,
234 		css_charset_source cs_source, css_parser **parser)
235 {
236 	parser_state initial = { sMediaQuery, 0 };
237 
238 	return css__parser_create_internal(charset, cs_source,
239 			initial, parser);
240 }
241 
242 /**
243  * Destroy a CSS parser
244  *
245  * \param parser  The parser instance to destroy
246  * \return CSS_OK on success, appropriate error otherwise
247  */
css__parser_destroy(css_parser * parser)248 css_error css__parser_destroy(css_parser *parser)
249 {
250 	if (parser == NULL)
251 		return CSS_BADPARM;
252 
253 	parserutils_stack_destroy(parser->open_items);
254 
255 	parserutils_vector_destroy(parser->tokens);
256 
257 	parserutils_stack_destroy(parser->states);
258 
259 	css__lexer_destroy(parser->lexer);
260 
261 	parserutils_inputstream_destroy(parser->stream);
262 
263 	free(parser);
264 
265 	return CSS_OK;
266 }
267 
268 /**
269  * Configure a CSS parser
270  *
271  * \param parser  The parser instance to configure
272  * \param type    The option to configure
273  * \param params  Option-specific data
274  * \return CSS_OK on success, appropriate error otherwise
275  */
css__parser_setopt(css_parser * parser,css_parser_opttype type,css_parser_optparams * params)276 css_error css__parser_setopt(css_parser *parser, css_parser_opttype type,
277 		css_parser_optparams *params)
278 {
279 	if (parser == NULL || params == NULL)
280 		return CSS_BADPARM;
281 
282 	switch (type) {
283 	case CSS_PARSER_QUIRKS:
284 		parser->quirks = params->quirks;
285 		break;
286 	case CSS_PARSER_EVENT_HANDLER:
287 		parser->event = params->event_handler.handler;
288 		parser->event_pw = params->event_handler.pw;
289 		break;
290 	}
291 
292 	return CSS_OK;
293 }
294 
295 /**
296  * Parse a chunk of data using a CSS parser
297  *
298  * \param parser  The parser to use
299  * \param data    Pointer to the chunk to parse
300  * \param len     Length of chunk
301  * \return CSS_OK on success, appropriate error otherwise
302  */
css__parser_parse_chunk(css_parser * parser,const uint8_t * data,size_t len)303 css_error css__parser_parse_chunk(css_parser *parser, const uint8_t *data,
304 		size_t len)
305 {
306 	parserutils_error perror;
307 	parser_state *state;
308 	css_error error = CSS_OK;
309 
310 	if (parser == NULL || data == NULL)
311 		return CSS_BADPARM;
312 
313 	perror = parserutils_inputstream_append(parser->stream, data, len);
314 	if (perror != PARSERUTILS_OK)
315 		return css_error_from_parserutils_error(perror);
316 
317 	do {
318 		state = parserutils_stack_get_current(parser->states);
319 		if (state == NULL)
320 			break;
321 
322 		error = parseFuncs[state->state](parser);
323 	} while (error == CSS_OK);
324 
325 	return error;
326 }
327 
328 /**
329  * Inform a CSS parser that all data has been received.
330  *
331  * \param parser  The parser to inform
332  * \return CSS_OK on success, appropriate error otherwise
333  */
css__parser_completed(css_parser * parser)334 css_error css__parser_completed(css_parser *parser)
335 {
336 	parserutils_error perror;
337 	parser_state *state;
338 	css_error error = CSS_OK;
339 
340 	if (parser == NULL)
341 		return CSS_BADPARM;
342 
343 	/* Send EOF to input stream */
344 	perror = parserutils_inputstream_append(parser->stream, NULL, 0);
345 	if (perror != PARSERUTILS_OK)
346 		return css_error_from_parserutils_error(perror);
347 
348 	/* Flush through any remaining data */
349 	do {
350 		state = parserutils_stack_get_current(parser->states);
351 		if (state == NULL)
352 			break;
353 
354 		error = parseFuncs[state->state](parser);
355 	} while (error == CSS_OK);
356 
357 	return error;
358 }
359 
360 /**
361  * Retrieve document charset information from a CSS parser
362  *
363  * \param parser  The parser instance
364  * \param source  Pointer to location to receive charset source
365  * \return Pointer to charset name (constant; do not free)
366  */
css__parser_read_charset(css_parser * parser,css_charset_source * source)367 const char *css__parser_read_charset(css_parser *parser,
368 		css_charset_source *source)
369 {
370 	const char *charset;
371 	uint32_t src;
372 
373 	if (parser == NULL || source == NULL)
374 		return NULL;
375 
376 	charset = parserutils_inputstream_read_charset(parser->stream, &src);
377 
378 	*source = (css_charset_source) src;
379 
380 	return charset;
381 }
382 
383 /**
384  * Quirks permitted when parsing
385  *
386  * \param parser  Parser to query
387  * \return True if quirks permitted, false otherwise
388  */
css__parser_quirks_permitted(css_parser * parser)389 bool css__parser_quirks_permitted(css_parser *parser)
390 {
391 	return parser->quirks;
392 }
393 
394 /******************************************************************************
395  * Parser creation helper                                                     *
396  ******************************************************************************/
397 
398 /**
399  * Create a CSS parser (internal)
400  *
401  * \param charset     Charset of data, if known, or NULL
402  * \param cs_source   Source of charset information, or CSS_CHARSET_DEFAULT
403  * \param initial     The required initial state of the parser
404  * \param parser      Pointer to location to receive parser instance
405  * \return CSS_OK on success,
406  *         CSS_BADPARM on bad parameters,
407  *         CSS_NOMEM on memory exhaustion
408  */
css__parser_create_internal(const char * charset,css_charset_source cs_source,parser_state initial,css_parser ** parser)409 css_error css__parser_create_internal(const char *charset,
410 		css_charset_source cs_source, parser_state initial,
411 		css_parser **parser)
412 {
413 	css_parser *p;
414 	parserutils_error perror;
415 	css_error error;
416 
417 	if (parser == NULL)
418 		return CSS_BADPARM;
419 
420 	p = malloc(sizeof(css_parser));
421 	if (p == NULL)
422 		return CSS_NOMEM;
423 
424 	perror = parserutils_inputstream_create(charset, cs_source,
425 			css__charset_extract, &p->stream);
426 	if (perror != PARSERUTILS_OK) {
427 		free(p);
428 		return css_error_from_parserutils_error(perror);
429 	}
430 
431 	error = css__lexer_create(p->stream, &p->lexer);
432 	if (error != CSS_OK) {
433 		parserutils_inputstream_destroy(p->stream);
434 		free(p);
435 		return error;
436 	}
437 
438 	perror = parserutils_stack_create(sizeof(parser_state),
439 			STACK_CHUNK, &p->states);
440 	if (perror != PARSERUTILS_OK) {
441 		css__lexer_destroy(p->lexer);
442 		parserutils_inputstream_destroy(p->stream);
443 		free(p);
444 		return css_error_from_parserutils_error(perror);
445 	}
446 
447 	perror = parserutils_vector_create(sizeof(css_token),
448 			STACK_CHUNK, &p->tokens);
449 	if (perror != PARSERUTILS_OK) {
450 		parserutils_stack_destroy(p->states);
451 		css__lexer_destroy(p->lexer);
452 		parserutils_inputstream_destroy(p->stream);
453 		free(p);
454 		return css_error_from_parserutils_error(perror);
455 	}
456 
457 	perror = parserutils_stack_create(sizeof(char),
458 			STACK_CHUNK, &p->open_items);
459 	if (perror != PARSERUTILS_OK) {
460 		parserutils_vector_destroy(p->tokens);
461 		parserutils_stack_destroy(p->states);
462 		css__lexer_destroy(p->lexer);
463 		parserutils_inputstream_destroy(p->stream);
464 		free(p);
465 		return css_error_from_parserutils_error(perror);
466 	}
467 
468 	perror = parserutils_stack_push(p->states, (void *) &initial);
469 	if (perror != PARSERUTILS_OK) {
470 		parserutils_stack_destroy(p->open_items);
471 		parserutils_vector_destroy(p->tokens);
472 		parserutils_stack_destroy(p->states);
473 		css__lexer_destroy(p->lexer);
474 		parserutils_inputstream_destroy(p->stream);
475 		free(p);
476 		return css_error_from_parserutils_error(perror);
477 	}
478 
479 	p->quirks = false;
480 	p->pushback = NULL;
481 	p->parseError = false;
482 	p->event = NULL;
483 	p->last_was_ws = false;
484 	p->event_pw = NULL;
485 
486 	*parser = p;
487 
488 	return CSS_OK;
489 }
490 
491 /******************************************************************************
492  * Helper functions                                                           *
493  ******************************************************************************/
494 
495 /**
496  * Transition to a new state, ensuring return to the one specified
497  *
498  * \param parser      The parser instance
499  * \param to          Destination state
500  * \param subsequent  The state to return to
501  * \return CSS_OK on success, appropriate error otherwise
502  */
transition(css_parser * parser,parser_state to,parser_state subsequent)503 css_error transition(css_parser *parser, parser_state to,
504 		parser_state subsequent)
505 {
506 	parser_state *state = parserutils_stack_get_current(parser->states);
507 	parser_state current = *state;
508 	parserutils_error perror;
509 
510 	/* Replace current state on the stack with the subsequent one */
511 	*state = subsequent;
512 
513 	/* Push next state on the stack */
514 	perror = parserutils_stack_push(parser->states, (void *) &to);
515 	if (perror != PARSERUTILS_OK) {
516 		*state = current;
517 		return css_error_from_parserutils_error(perror);
518 	}
519 
520 #if !defined(NDEBUG) && defined(DEBUG_STACK)
521 	parserutils_stack_dump(parser->states, __func__, printer);
522 #endif
523 
524 	/* Clear the error flag */
525 	parser->parseError = false;
526 
527 	return CSS_OK;
528 }
529 
530 /**
531  * Transition to a new state, returning to previous state on stack
532  *
533  * \param parser  The parser instance
534  * \param to      Destination state
535  * \return CSS_OK on success, appropriate error otherwise
536  */
transitionNoRet(css_parser * parser,parser_state to)537 css_error transitionNoRet(css_parser *parser, parser_state to)
538 {
539 	parser_state *state = parserutils_stack_get_current(parser->states);
540 
541 	/* Replace current state on the stack with destination */
542 	*state = to;
543 
544 #if !defined(NDEBUG) && defined(DEBUG_STACK)
545 	parserutils_stack_dump(parser->states, __func__, printer);
546 #endif
547 
548 	/* Clear the error flag */
549 	parser->parseError = false;
550 
551 	return CSS_OK;
552 }
553 
554 /**
555  * Return to previous state on the stack
556  *
557  * \param parser  The parser instance
558  * \return CSS_OK on success, appropriate error otherwise
559  */
done(css_parser * parser)560 css_error done(css_parser *parser)
561 {
562 	parserutils_error perror;
563 
564 	/* Pop current state from stack */
565 	perror = parserutils_stack_pop(parser->states, NULL);
566 	if (perror != PARSERUTILS_OK)
567 		return css_error_from_parserutils_error(perror);
568 
569 #if !defined(NDEBUG) && defined(DEBUG_STACK)
570 	parserutils_stack_dump(parser->states, __func__, printer);
571 #endif
572 
573 	return CSS_OK;
574 }
575 
576 /**
577  * Assert that the expected token is next in the input
578  *
579  * \param parser  The parser instance
580  * \param type    The expected token type
581  * \return CSS_OK on success, appropriate error otherwise
582  */
expect(css_parser * parser,css_token_type type)583 css_error expect(css_parser *parser, css_token_type type)
584 {
585 	const css_token *token;
586 	css_error error;
587 
588 	error = getToken(parser, &token);
589 	if (error != CSS_OK)
590 		return error;
591 
592 	if (token->type != type) {
593 		error = pushBack(parser, token);
594 		if (error != CSS_OK)
595 			return error;
596 		return CSS_INVALID;
597 	}
598 
599 	return CSS_OK;
600 }
601 
602 /**
603  * Retrieve the next token in the input
604  *
605  * \param parser  The parser instance
606  * \param token   Pointer to location to receive token
607  * \return CSS_OK on success, appropriate error otherwise
608  */
getToken(css_parser * parser,const css_token ** token)609 css_error getToken(css_parser *parser, const css_token **token)
610 {
611 	parserutils_error perror;
612         lwc_error lerror;
613 	css_error error;
614 
615 	/* Use pushback, if it exists */
616 	if (parser->pushback != NULL) {
617 		*token = parser->pushback;
618 		parser->pushback = NULL;
619 	} else {
620 		/* Otherwise, ask the lexer */
621 		css_token *t;
622 
623 		error = css__lexer_get_token(parser->lexer, &t);
624 		if (error != CSS_OK)
625 			return error;
626 
627 		/* If the last token read was whitespace, keep reading
628 		 * tokens until we encounter one that isn't whitespace */
629 		while (parser->last_was_ws && t->type == CSS_TOKEN_S) {
630 			error = css__lexer_get_token(parser->lexer, &t);
631 			if (error != CSS_OK)
632 				return error;
633 		}
634 
635 		/* We need only intern for the following token types:
636 		 *
637 		 * CSS_TOKEN_IDENT, CSS_TOKEN_ATKEYWORD, CSS_TOKEN_STRING,
638 		 * CSS_TOKEN_INVALID_STRING, CSS_TOKEN_HASH, CSS_TOKEN_URI,
639 		 * CSS_TOKEN_UNICODE_RANGE?, CSS_TOKEN_FUNCTION, CSS_TOKEN_CHAR,
640 		 * CSS_TOKEN_NUMBER, CSS_TOKEN_PERCENTAGE, CSS_TOKEN_DIMENSION
641 		 *
642 		 * These token types all appear before CSS_TOKEN_LAST_INTERN.
643 		 * All other token types appear after this magic value.
644 		 */
645 
646 		if (t->type < CSS_TOKEN_LAST_INTERN && t->data.data != NULL) {
647 			/* Insert token text into the dictionary */
648                         lerror = lwc_intern_string((char *)t->data.data,
649                                                     t->data.len, &t->idata);
650                         if (lerror != lwc_error_ok)
651                                 return css_error_from_lwc_error(lerror);
652 		} else {
653 			t->idata = NULL;
654 		}
655 
656 		*token = t;
657 	}
658 
659 	/* Append token to vector */
660 	perror = parserutils_vector_append(parser->tokens,
661 			(css_token *) (*token));
662 	if (perror != PARSERUTILS_OK)
663 		return css_error_from_parserutils_error(perror);
664 
665 	parser->last_was_ws = ((*token)->type == CSS_TOKEN_S);
666 
667 	return CSS_OK;
668 }
669 
670 /**
671  * Push a token back on the input
672  *
673  * \param parser  The parser instance
674  * \param token   The token to push back
675  * \return CSS_OK on success.
676  */
pushBack(css_parser * parser,const css_token * token)677 css_error pushBack(css_parser *parser, const css_token *token)
678 {
679 	parserutils_error perror;
680 
681 	/* The pushback buffer depth is 1 token. Assert this. */
682 	assert(parser->pushback == NULL);
683 
684 	perror = parserutils_vector_remove_last(parser->tokens);
685 	if (perror != PARSERUTILS_OK)
686 		return css_error_from_parserutils_error(perror);
687 
688 	parser->pushback = token;
689 
690 	return CSS_OK;
691 }
692 
693 /**
694  * Eat whitespace tokens
695  *
696  * \param parser  The parser instance
697  * \return CSS_OK on success, appropriate error otherwise
698  */
eatWS(css_parser * parser)699 css_error eatWS(css_parser *parser)
700 {
701 	const css_token *token;
702 	css_error error;
703 
704 	error = getToken(parser, &token);
705 	if (error != CSS_OK)
706 		return error;
707 
708 	if (token->type != CSS_TOKEN_S) {
709 		error = pushBack(parser, token);
710 		if (error != CSS_OK)
711 			return error;
712 	}
713 
714 	return CSS_OK;
715 }
716 
717 /******************************************************************************
718  * Parser stages                                                              *
719  ******************************************************************************/
720 
parseStart(css_parser * parser)721 css_error parseStart(css_parser *parser)
722 {
723 	enum { Initial = 0, AfterWS = 1, AfterStylesheet = 2 };
724 	parser_state *state = parserutils_stack_get_current(parser->states);
725 	css_error error = CSS_OK;
726 
727 	/* start -> ws stylesheet EOF */
728 
729 	switch (state->substate) {
730 	case Initial:
731 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
732 		printf("Begin stylesheet\n");
733 #endif
734 		if (parser->event != NULL) {
735 			parser->event(CSS_PARSER_START_STYLESHEET, NULL,
736 					parser->event_pw);
737 		}
738 
739 		error = eatWS(parser);
740 		if (error != CSS_OK)
741 			return error;
742 		state->substate = AfterWS;
743 		/* Fall through */
744 	case AfterWS:
745 	{
746 		parser_state to = { sStylesheet, Initial };
747 		parser_state subsequent = { sStart, AfterStylesheet };
748 
749 		return transition(parser, to, subsequent);
750 	}
751 	case AfterStylesheet:
752 		error = expect(parser, CSS_TOKEN_EOF);
753 		if (error != CSS_OK)
754 			return error;
755 
756 		/* Flag completion, just in case */
757 	}
758 
759 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
760 	parserutils_vector_dump(parser->tokens, __func__, tprinter);
761 	printf("End stylesheet\n");
762 #endif
763 	if (parser->event != NULL) {
764 		parser->event(CSS_PARSER_END_STYLESHEET, NULL,
765 				parser->event_pw);
766 	}
767 
768 	discard_tokens(parser);
769 
770 	return done(parser);
771 }
772 
parseStylesheet(css_parser * parser)773 css_error parseStylesheet(css_parser *parser)
774 {
775 	enum { Initial = 0, WS = 1 };
776 	parser_state *state = parserutils_stack_get_current(parser->states);
777 	const css_token *token;
778 	css_error error;
779 
780 	/* stylesheet -> CDO ws stylesheet
781 	 *            -> CDC ws stylesheet
782 	 *            -> statement ws stylesheet
783 	 *            ->
784 	 */
785 
786 	while (1) {
787 		switch (state->substate) {
788 		case Initial:
789 			error = getToken(parser, &token);
790 			if (error != CSS_OK)
791 				return error;
792 
793 			switch (token->type) {
794 			case CSS_TOKEN_EOF:
795 				error = pushBack(parser, token);
796 				if (error != CSS_OK)
797 					return error;
798 
799 				discard_tokens(parser);
800 
801 				return done(parser);
802 			case CSS_TOKEN_CDO:
803 			case CSS_TOKEN_CDC:
804 				break;
805 			default:
806 			{
807 				parser_state to = { sStatement, Initial };
808 				parser_state subsequent = { sStylesheet, WS };
809 
810 				error = pushBack(parser, token);
811 				if (error != CSS_OK)
812 					return error;
813 
814 				return transition(parser, to, subsequent);
815 			}
816 			}
817 
818 			state->substate = WS;
819 			/* Fall through */
820 		case WS:
821 			error = eatWS(parser);
822 			if (error != CSS_OK)
823 				return error;
824 
825 			state->substate = Initial;
826 		}
827 	}
828 }
829 
parseStatement(css_parser * parser)830 css_error parseStatement(css_parser *parser)
831 {
832 	enum { Initial = 0 };
833 	const css_token *token;
834 	parser_state to = { sRuleset, Initial };
835 	css_error error;
836 
837 	/* statement -> ruleset
838 	 *              at-rule
839 	 */
840 
841 	error = getToken(parser, &token);
842 	if (error != CSS_OK)
843 		return error;
844 
845 	if (token->type == CSS_TOKEN_ATKEYWORD)
846 		to.state = sAtRule;
847 
848 	error = pushBack(parser, token);
849 	if (error != CSS_OK)
850 		return error;
851 
852 	return transitionNoRet(parser, to);
853 }
854 
parseRuleset(css_parser * parser)855 css_error parseRuleset(css_parser *parser)
856 {
857 	enum { Initial = 0, Brace = 1, WS = 2 };
858 	parser_state *state = parserutils_stack_get_current(parser->states);
859 	parser_state to = { sRulesetEnd, Initial };
860 	const css_token *token;
861 	css_error error;
862 
863 	/* ruleset -> selector '{' ws ruleset-end
864 	 *         -> '{' ws ruleset-end
865 	 */
866 
867 	switch (state->substate) {
868 	case Initial:
869 		discard_tokens(parser);
870 
871 		error = getToken(parser, &token);
872 		if (error != CSS_OK)
873 			return error;
874 
875 		/* The grammar's ambiguous here -- selectors may start with a
876 		 * brace. We're going to assume that that won't happen,
877 		 * however. */
878 		if (token->type == CSS_TOKEN_CHAR &&
879 				lwc_string_length(token->idata) == 1 &&
880 				lwc_string_data(token->idata)[0] == '{') {
881 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
882 			printf("Begin ruleset\n");
883 #endif
884 			if (parser->event != NULL) {
885 				if (parser->event(CSS_PARSER_START_RULESET,
886 						NULL, parser->event_pw) ==
887 						CSS_INVALID) {
888 					parser_state to =
889 						{ sMalformedSelector, Initial };
890 
891 					return transitionNoRet(parser, to);
892 				}
893 			}
894 
895 			state->substate = WS;
896 			goto ws;
897 		} else {
898 			parser_state to = { sSelector, Initial };
899 			parser_state subsequent = { sRuleset, Brace };
900 
901 			error = pushBack(parser, token);
902 			if (error != CSS_OK)
903 				return error;
904 
905 			return transition(parser, to, subsequent);
906 		}
907 		break;
908 	case Brace:
909 		if (parser->parseError == true) {
910 			parser_state to = { sMalformedSelector, Initial };
911 
912 			return transitionNoRet(parser, to);
913 		}
914 
915 		error = getToken(parser, &token);
916 		if (error != CSS_OK)
917 			return error;
918 
919 		if (token->type != CSS_TOKEN_CHAR ||
920 				lwc_string_length(token->idata) != 1 ||
921 				lwc_string_data(token->idata)[0] != '{') {
922 			/* FOLLOW(selector) contains only '{', but we may
923 			 * also have seen EOF, which is a parse error. */
924 			error = pushBack(parser, token);
925 			if (error != CSS_OK)
926 				return error;
927 
928 			parser->parseError = true;
929 			return done(parser);
930 		}
931 
932 		/* We don't want to emit the brace, so push it back */
933 		error = pushBack(parser, token);
934 		if (error != CSS_OK)
935 			return error;
936 
937 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
938 		printf("Begin ruleset\n");
939 		parserutils_vector_dump(parser->tokens, __func__, tprinter);
940 #endif
941 		if (parser->parseError == false && parser->event != NULL) {
942 			if (parser->event(CSS_PARSER_START_RULESET,
943 					parser->tokens, parser->event_pw) ==
944 					CSS_INVALID)
945 				parser->parseError = true;
946 		}
947 
948 		/* Re-read the brace */
949 		error = getToken(parser, &token);
950 		if (error != CSS_OK)
951 			return error;
952 
953 		state->substate = WS;
954 		/* Fall through */
955 	case WS:
956 	ws:
957 		error = eatWS(parser);
958 		if (error != CSS_OK)
959 			return error;
960 
961 		break;
962 	}
963 
964 	return transitionNoRet(parser, to);
965 }
966 
parseRulesetEnd(css_parser * parser)967 css_error parseRulesetEnd(css_parser *parser)
968 {
969 	enum { Initial = 0, DeclList = 1, Brace = 2, WS = 3 };
970 	parser_state *state = parserutils_stack_get_current(parser->states);
971 	const css_token *token;
972 	css_error error;
973 
974 	/* ruleset-end -> declaration decl-list '}' ws
975 	 *             -> decl-list '}' ws
976 	 */
977 
978 	switch (state->substate) {
979 	case Initial:
980 		error = getToken(parser, &token);
981 		if (error != CSS_OK)
982 			return error;
983 
984 		error = pushBack(parser, token);
985 		if (error != CSS_OK)
986 			return error;
987 
988 		if (token->type == CSS_TOKEN_EOF)
989 			return done(parser);
990 
991 		/* If this can't possibly be the start of a decl-list, then
992 		 * attempt to parse a declaration. This will catch any invalid
993 		 * input at this point and read to the start of the next
994 		 * declaration. FIRST(decl-list) = (';', '}') */
995 		if (token->type != CSS_TOKEN_CHAR ||
996 				lwc_string_length(token->idata) != 1 ||
997 				(lwc_string_data(token->idata)[0] != '}' &&
998 				lwc_string_data(token->idata)[0] != ';')) {
999 			parser_state to = { sDeclaration, Initial };
1000 			parser_state subsequent = { sRulesetEnd, DeclList };
1001 
1002 			return transition(parser, to, subsequent);
1003 		}
1004 
1005 		state->substate = DeclList;
1006 		/* Fall through */
1007 	case DeclList:
1008 	{
1009 		parser_state to = { sDeclList, Initial };
1010 		parser_state subsequent = { sRulesetEnd, Brace };
1011 
1012 		return transition(parser, to, subsequent);
1013 	}
1014 	case Brace:
1015 		error = getToken(parser, &token);
1016 		if (error != CSS_OK)
1017 			return error;
1018 
1019 		if (token->type == CSS_TOKEN_EOF) {
1020 			error = pushBack(parser, token);
1021 			if (error != CSS_OK)
1022 				return error;
1023 
1024 			return done(parser);
1025 		}
1026 
1027 		if (token->type != CSS_TOKEN_CHAR ||
1028 				lwc_string_length(token->idata) != 1 ||
1029 				lwc_string_data(token->idata)[0] != '}') {
1030 			/* This should never happen, as FOLLOW(decl-list)
1031 			 * contains only '}' */
1032 			assert(0 && "Expected }");
1033 		}
1034 
1035 		state->substate = WS;
1036 		/* Fall through */
1037 	case WS:
1038 		error = eatWS(parser);
1039 		if (error != CSS_OK)
1040 			return error;
1041 
1042 		break;
1043 	}
1044 
1045 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1046 	printf("End ruleset\n");
1047 #endif
1048 	if (parser->event != NULL) {
1049 		parser->event(CSS_PARSER_END_RULESET, NULL, parser->event_pw);
1050 	}
1051 
1052 	return done(parser);
1053 }
1054 
parseAtRule(css_parser * parser)1055 css_error parseAtRule(css_parser *parser)
1056 {
1057 	enum { Initial = 0, WS = 1, Any = 2, AfterAny = 3 };
1058 	parser_state *state = parserutils_stack_get_current(parser->states);
1059 	parser_state to = { sAtRuleEnd, Initial };
1060 	const css_token *token;
1061 	css_error error;
1062 
1063 	/* at-rule -> ATKEYWORD ws any0 at-rule-end */
1064 
1065 	switch (state->substate) {
1066 	case Initial:
1067 		discard_tokens(parser);
1068 
1069 		error = getToken(parser, &token);
1070 		if (error != CSS_OK)
1071 			return error;
1072 
1073 		assert(token->type == CSS_TOKEN_ATKEYWORD);
1074 
1075 		state->substate = WS;
1076 		/* Fall through */
1077 	case WS:
1078 		error = eatWS(parser);
1079 		if (error != CSS_OK)
1080 			return error;
1081 
1082 		state->substate = Any;
1083 		/* Fall through */
1084 	case Any:
1085 	{
1086 		parser_state to = { sAny0, Initial };
1087 		parser_state subsequent = { sAtRule, AfterAny };
1088 
1089 		return transition(parser, to, subsequent);
1090 	}
1091 	case AfterAny:
1092 		if (parser->parseError) {
1093 			parser_state to = { sMalformedAtRule, Initial };
1094 
1095 			return transitionNoRet(parser, to);
1096 		}
1097 
1098 		error = getToken(parser, &token);
1099 		if (error != CSS_OK)
1100 			return error;
1101 
1102 		/* Grammar ambiguity: any0 can be followed by '{',';',')',']'.
1103 		 * at-rule can only be followed by '{' and ';'. */
1104 		if (token->type == CSS_TOKEN_CHAR &&
1105 				lwc_string_length(token->idata) == 1) {
1106 			if (lwc_string_data(token->idata)[0] == ')' ||
1107 					lwc_string_data(
1108 						token->idata)[0] == ']') {
1109 				parser_state to = { sAny0, Initial };
1110 				parser_state subsequent = { sAtRule, AfterAny };
1111 
1112 				return transition(parser, to, subsequent);
1113 			}
1114 		}
1115 
1116 		error = pushBack(parser, token);
1117 		if (error != CSS_OK)
1118 			return error;
1119 
1120 		break;
1121 	}
1122 
1123 	return transitionNoRet(parser, to);
1124 }
1125 
parseAtRuleEnd(css_parser * parser)1126 css_error parseAtRuleEnd(css_parser *parser)
1127 {
1128 	enum { Initial = 0, WS = 1, AfterBlock = 2 };
1129 	parser_state *state = parserutils_stack_get_current(parser->states);
1130 	const css_token *token;
1131 	css_error error;
1132 
1133 	/* at-rule-end -> block
1134 	 *             -> ';' ws
1135 	 */
1136 
1137 	switch (state->substate) {
1138 	case Initial:
1139 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1140 		printf("Begin at-rule\n");
1141 		parserutils_vector_dump(parser->tokens, __func__, tprinter);
1142 #endif
1143 		if (parser->event != NULL) {
1144 			if (parser->event(CSS_PARSER_START_ATRULE,
1145 					parser->tokens, parser->event_pw) ==
1146 					CSS_INVALID) {
1147 				parser_state to = { sMalformedAtRule, Initial };
1148 
1149 				return transitionNoRet(parser, to);
1150 			}
1151 		}
1152 
1153 		error = getToken(parser, &token);
1154 		if (error != CSS_OK)
1155 			return error;
1156 
1157 		if (token->type == CSS_TOKEN_EOF) {
1158 			error = pushBack(parser, token);
1159 			if (error != CSS_OK)
1160 				return error;
1161 
1162 			return done(parser);
1163 		}
1164 
1165 		if (token->type != CSS_TOKEN_CHAR ||
1166 				lwc_string_length(token->idata) != 1) {
1167 			/* Should never happen FOLLOW(at-rule) == '{', ';'*/
1168 			assert(0 && "Expected { or ;");
1169 		}
1170 
1171 		if (lwc_string_data(token->idata)[0] == '{') {
1172 			parser_state to = { sBlock, Initial };
1173 			parser_state subsequent = { sAtRuleEnd, AfterBlock };
1174 
1175 			error = pushBack(parser, token);
1176 			if (error != CSS_OK)
1177 				return error;
1178 
1179 			return transition(parser, to, subsequent);
1180 		} else if (lwc_string_data(token->idata)[0] != ';') {
1181 			/* Again, should never happen */
1182 			assert(0 && "Expected ;");
1183 		}
1184 
1185 		state->substate = WS;
1186 		/* Fall through */
1187 	case WS:
1188 		error = eatWS(parser);
1189 		if (error != CSS_OK)
1190 			return error;
1191 
1192 		break;
1193 	case AfterBlock:
1194 		break;
1195 	}
1196 
1197 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1198 	printf("End at-rule\n");
1199 #endif
1200 	if (parser->event != NULL) {
1201 		parser->event(CSS_PARSER_END_ATRULE, NULL, parser->event_pw);
1202 	}
1203 
1204 	return done(parser);
1205 }
1206 
parseBlock(css_parser * parser)1207 css_error parseBlock(css_parser *parser)
1208 {
1209 	enum { Initial = 0, WS = 1, Content = 2, Brace = 3, WS2 = 4 };
1210 	parser_state *state = parserutils_stack_get_current(parser->states);
1211 	const css_token *token;
1212 	css_error error;
1213 
1214 	/* block -> '{' ws block-content '}' ws */
1215 
1216 	switch (state->substate) {
1217 	case Initial:
1218 		error = getToken(parser, &token);
1219 		if (error != CSS_OK)
1220 			return error;
1221 
1222 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1223 		printf("Begin block\n");
1224 #endif
1225 		if (parser->event != NULL) {
1226 			parser->event(CSS_PARSER_START_BLOCK, NULL,
1227 					parser->event_pw);
1228 		}
1229 
1230 		if (token->type != CSS_TOKEN_CHAR ||
1231 				lwc_string_length(token->idata) != 1 ||
1232 				lwc_string_data(token->idata)[0] != '{') {
1233 			/* This should never happen, as FIRST(block) == '{' */
1234 			assert(0 && "Expected {");
1235 		}
1236 
1237 		discard_tokens(parser);
1238 
1239 		state->substate = WS;
1240 		/* Fall through */
1241 	case WS:
1242 		error = eatWS(parser);
1243 		if (error != CSS_OK)
1244 			return error;
1245 
1246 		state->substate = Content;
1247 		/* Fall through */
1248 	case Content:
1249 	{
1250 		parser_state to = { sBlockContent, Initial };
1251 		parser_state subsequent = { sBlock, Brace };
1252 
1253 		return transition(parser, to, subsequent);
1254 	}
1255 	case Brace:
1256 		error = getToken(parser, &token);
1257 		if (error != CSS_OK)
1258 			return error;
1259 
1260 		if (token->type == CSS_TOKEN_EOF) {
1261 			error = pushBack(parser, token);
1262 			if (error != CSS_OK)
1263 				return error;
1264 
1265 			return done(parser);
1266 		}
1267 
1268 		if (token->type != CSS_TOKEN_CHAR ||
1269 				lwc_string_length(token->idata) != 1 ||
1270 				lwc_string_data(token->idata)[0] != '}') {
1271 			/* This should never happen, as
1272 			 * FOLLOW(block-content) == '}' */
1273 			assert(0 && "Expected }");
1274 		}
1275 
1276 		state->substate = WS2;
1277 		/* Fall through */
1278 	case WS2:
1279 		error = eatWS(parser);
1280 		if (error != CSS_OK)
1281 			return error;
1282 
1283 		break;
1284 	}
1285 
1286 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1287 	printf("End block\n");
1288 #endif
1289 	if (parser->event != NULL) {
1290 		parser->event(CSS_PARSER_END_BLOCK, NULL, parser->event_pw);
1291 	}
1292 
1293 	discard_tokens(parser);
1294 
1295 	return done(parser);
1296 }
1297 
parseBlockContent(css_parser * parser)1298 css_error parseBlockContent(css_parser *parser)
1299 {
1300 	enum { Initial = 0, WS = 1 };
1301 	parser_state *state = parserutils_stack_get_current(parser->states);
1302 	const css_token *token;
1303 	css_error error;
1304 
1305 	/* block-content -> any block-content
1306 	 *               -> block block-content
1307 	 *               -> ATKEYWORD ws block-content
1308 	 *               -> ';' ws block-content
1309 	 *               ->
1310 	 */
1311 
1312 	while (1) {
1313 		switch (state->substate) {
1314 		case Initial:
1315 			error = getToken(parser, &token);
1316 			if (error != CSS_OK)
1317 				return error;
1318 
1319 			if (token->type == CSS_TOKEN_ATKEYWORD) {
1320 				state->substate = WS;
1321 			} else if (token->type == CSS_TOKEN_CHAR) {
1322 				if (lwc_string_length(token->idata) == 1 &&
1323 						lwc_string_data(
1324 						token->idata)[0] == '{') {
1325 					/* Grammar ambiguity. Assume block */
1326 					parser_state to = { sBlock, Initial };
1327 					parser_state subsequent =
1328 						{ sBlockContent, Initial };
1329 
1330 					error = pushBack(parser, token);
1331 					if (error != CSS_OK)
1332 						return error;
1333 
1334 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1335 					parserutils_vector_dump(parser->tokens,
1336 							__func__, tprinter);
1337 #endif
1338 					if (parser->event != NULL) {
1339 						parser->event(
1340 							CSS_PARSER_BLOCK_CONTENT,
1341 							parser->tokens,
1342 							parser->event_pw);
1343 					}
1344 
1345 					discard_tokens(parser);
1346 
1347 					return transition(parser, to,
1348 							subsequent);
1349 				} else if (lwc_string_length(
1350 						token->idata) == 1 &&
1351 						lwc_string_data(
1352 						token->idata)[0] == ';') {
1353 					/* Grammar ambiguity. Assume semi */
1354 					error = pushBack(parser, token);
1355 					if (error != CSS_OK)
1356 						return error;
1357 
1358 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1359 					parserutils_vector_dump(parser->tokens,
1360 							__func__, tprinter);
1361 #endif
1362 					if (parser->event != NULL) {
1363 						parser->event(
1364 							CSS_PARSER_BLOCK_CONTENT,
1365 							parser->tokens,
1366 							parser->event_pw);
1367 					}
1368 
1369 					error = getToken(parser, &token);
1370 					if (error != CSS_OK)
1371 						return error;
1372 
1373 					discard_tokens(parser);
1374 
1375 					state->substate = WS;
1376 				} else if (lwc_string_length(
1377 						token->idata) == 1 &&
1378 						lwc_string_data(
1379 						token->idata)[0] == '}') {
1380 					/* Grammar ambiguity. Assume end */
1381 					error = pushBack(parser, token);
1382 					if (error != CSS_OK)
1383 						return error;
1384 
1385 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1386 					parserutils_vector_dump(parser->tokens,
1387 							__func__, tprinter);
1388 #endif
1389 					if (parser->event != NULL) {
1390 						parser->event(
1391 							CSS_PARSER_END_BLOCK_CONTENT,
1392 							parser->tokens,
1393 							parser->event_pw);
1394 					}
1395 
1396 					discard_tokens(parser);
1397 
1398 					return done(parser);
1399 				}
1400 			} else if (token->type == CSS_TOKEN_EOF) {
1401 				error = pushBack(parser, token);
1402 				if (error != CSS_OK)
1403 					return error;
1404 
1405 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1406 				parserutils_vector_dump(parser->tokens,
1407 						__func__, tprinter);
1408 #endif
1409 				if (parser->event != NULL) {
1410 					parser->event(CSS_PARSER_BLOCK_CONTENT,
1411 							parser->tokens,
1412 							parser->event_pw);
1413 				}
1414 
1415 				discard_tokens(parser);
1416 
1417 				return done(parser);
1418 			}
1419 
1420 			if (state->substate == Initial) {
1421 				parser_state to = { sAny, Initial };
1422 				parser_state subsequent =
1423 						{ sBlockContent, Initial };
1424 
1425 				error = pushBack(parser, token);
1426 				if (error != CSS_OK)
1427 					return error;
1428 
1429 				return transition(parser, to, subsequent);
1430 			}
1431 
1432 			break;
1433 		case WS:
1434 			error = eatWS(parser);
1435 			if (error != CSS_OK)
1436 				return error;
1437 
1438 			state->substate = Initial;
1439 		}
1440 	}
1441 
1442 	 return done(parser);
1443 }
1444 
parseSelector(css_parser * parser)1445 css_error parseSelector(css_parser *parser)
1446 {
1447 	enum { Initial = 0, AfterAny1 = 1 };
1448 	parser_state *state = parserutils_stack_get_current(parser->states);
1449 
1450 	/* selector -> any1 */
1451 
1452 	switch (state->substate) {
1453 	case Initial:
1454 	{
1455 		parser_state to = { sAny1, Initial };
1456 		parser_state subsequent = { sSelector, AfterAny1 };
1457 
1458 		discard_tokens(parser);
1459 
1460 		return transition(parser, to, subsequent);
1461 	}
1462 	case AfterAny1:
1463 		break;
1464 	}
1465 
1466 	return done(parser);
1467 }
1468 
parseDeclaration(css_parser * parser)1469 css_error parseDeclaration(css_parser *parser)
1470 {
1471 	enum { Initial = 0, Colon = 1, WS = 2, AfterValue1 = 3 };
1472 	parser_state *state = parserutils_stack_get_current(parser->states);
1473 	const css_token *token;
1474 	css_error error;
1475 
1476 	/* declaration -> property ':' ws value1 */
1477 
1478 	switch (state->substate) {
1479 	case Initial:
1480 	{
1481 		parser_state to = { sProperty, Initial };
1482 		parser_state subsequent = { sDeclaration, Colon };
1483 
1484 		discard_tokens(parser);
1485 
1486 		return transition(parser, to, subsequent);
1487 	}
1488 	case Colon:
1489 		if (parser->parseError) {
1490 			parser_state to = { sMalformedDecl, Initial };
1491 
1492 			parser->parseError = false;
1493 
1494 			return transitionNoRet(parser, to);
1495 		}
1496 
1497 		error = getToken(parser, &token);
1498 		if (error != CSS_OK)
1499 			return error;
1500 
1501 		if (token->type == CSS_TOKEN_EOF) {
1502 			error = pushBack(parser, token);
1503 			if (error != CSS_OK)
1504 				return error;
1505 
1506 			return done(parser);
1507 		}
1508 
1509 		if (token->type != CSS_TOKEN_CHAR ||
1510 				lwc_string_length(token->idata) != 1 ||
1511 				lwc_string_data(token->idata)[0] != ':') {
1512 			/* parse error -- expected : */
1513 			parser_state to = { sMalformedDecl, Initial };
1514 
1515 			error = pushBack(parser, token);
1516 			if (error != CSS_OK)
1517 				return error;
1518 
1519 			return transitionNoRet(parser, to);
1520 		}
1521 
1522 		state->substate = WS;
1523 		/* Fall through */
1524 	case WS:
1525 	{
1526 		parser_state to = { sValue1, Initial };
1527 		parser_state subsequent = { sDeclaration, AfterValue1 };
1528 
1529 		error = eatWS(parser);
1530 		if (error != CSS_OK)
1531 			return error;
1532 
1533 		return transition(parser, to, subsequent);
1534 	}
1535 	case AfterValue1:
1536 		if (parser->parseError) {
1537 			parser_state to = { sMalformedDecl, Initial };
1538 
1539 			parser->parseError = false;
1540 
1541 			return transitionNoRet(parser, to);
1542 		}
1543 
1544 #if !defined(NDEBUG) && defined(DEBUG_EVENTS)
1545 		parserutils_vector_dump(parser->tokens, __func__, tprinter);
1546 #endif
1547 		if (parser->event != NULL) {
1548 			parser->event(CSS_PARSER_DECLARATION, parser->tokens,
1549 					parser->event_pw);
1550 		}
1551 		break;
1552 	}
1553 
1554 	return done(parser);
1555 }
1556 
parseDeclList(css_parser * parser)1557 css_error parseDeclList(css_parser *parser)
1558 {
1559 	enum { Initial = 0, WS = 1 };
1560 	parser_state *state = parserutils_stack_get_current(parser->states);
1561 	parser_state to = { sDeclListEnd, Initial };
1562 	const css_token *token;
1563 	css_error error;
1564 
1565 	/* decl-list -> ';' ws decl-list-end
1566 	 *           ->
1567 	 */
1568 
1569 	switch (state->substate) {
1570 	case Initial:
1571 		error = getToken(parser, &token);
1572 		if (error != CSS_OK)
1573 			return error;
1574 
1575 		if (token->type == CSS_TOKEN_EOF) {
1576 			error = pushBack(parser, token);
1577 			if (error != CSS_OK)
1578 				return error;
1579 
1580 			return done(parser);
1581 		}
1582 
1583 		if (token->type != CSS_TOKEN_CHAR ||
1584 				lwc_string_length(token->idata) != 1 ||
1585 				(lwc_string_data(token->idata)[0] != '}' &&
1586 				lwc_string_data(token->idata)[0] != ';')) {
1587 			/* Should never happen */
1588 			assert(0 && "Expected ; or  }");
1589 		}
1590 
1591 		if (lwc_string_data(token->idata)[0] == '}') {
1592 			error = pushBack(parser, token);
1593 			if (error != CSS_OK)
1594 				return error;
1595 
1596 			return done(parser);
1597 		} else {
1598 			/* ; */
1599 			state->substate = WS;
1600 		}
1601 
1602 		/* Fall through */
1603 	case WS:
1604 		error = eatWS(parser);
1605 		if (error != CSS_OK)
1606 			return error;
1607 
1608 		break;
1609 	}
1610 
1611 	return transitionNoRet(parser, to);
1612 }
1613 
parseDeclListEnd(css_parser * parser)1614 css_error parseDeclListEnd(css_parser *parser)
1615 {
1616 	enum { Initial = 0, AfterDeclaration = 1 };
1617 	parser_state *state = parserutils_stack_get_current(parser->states);
1618 	parser_state to = { sDeclList, Initial };
1619 	const css_token *token;
1620 	css_error error;
1621 
1622 	/* decl-list-end -> declaration decl-list
1623 	 *               -> decl-list
1624 	 */
1625 
1626 	switch (state->substate) {
1627 	case Initial:
1628 		error = getToken(parser, &token);
1629 		if (error != CSS_OK)
1630 			return error;
1631 
1632 		if (token->type != CSS_TOKEN_CHAR ||
1633 				lwc_string_length(token->idata) != 1 ||
1634 				(lwc_string_data(token->idata)[0] != ';' &&
1635 				lwc_string_data(token->idata)[0] != '}')) {
1636 			parser_state to = { sDeclaration, Initial };
1637 			parser_state subsequent =
1638 					{ sDeclListEnd, AfterDeclaration };
1639 
1640 			error = pushBack(parser, token);
1641 			if (error != CSS_OK)
1642 				return error;
1643 
1644 			return transition(parser, to, subsequent);
1645 		} else {
1646 			error = pushBack(parser, token);
1647 			if (error != CSS_OK)
1648 				return error;
1649 		}
1650 
1651 		state->substate = AfterDeclaration;
1652 		/* Fall through */
1653 	case AfterDeclaration:
1654 		break;
1655 	}
1656 
1657 	return transitionNoRet(parser, to);
1658 }
1659 
parseProperty(css_parser * parser)1660 css_error parseProperty(css_parser *parser)
1661 {
1662 	enum { Initial = 0, WS = 1 };
1663 	parser_state *state = parserutils_stack_get_current(parser->states);
1664 	const css_token *token;
1665 	css_error error;
1666 
1667 	/* property -> IDENT ws */
1668 
1669 	switch (state->substate) {
1670 	case Initial:
1671 		error = getToken(parser, &token);
1672 		if (error != CSS_OK)
1673 			return error;
1674 
1675 		if (token->type == CSS_TOKEN_EOF) {
1676 			error = pushBack(parser, token);
1677 			if (error != CSS_OK)
1678 				return error;
1679 
1680 			return done(parser);
1681 		}
1682 
1683 		if (token->type != CSS_TOKEN_IDENT) {
1684 			/* parse error */
1685 			parser->parseError = true;
1686 
1687 			return done(parser);
1688 		}
1689 
1690 		state->substate = WS;
1691 		/* Fall through */
1692 	case WS:
1693 		error = eatWS(parser);
1694 		if (error != CSS_OK)
1695 			return error;
1696 
1697 		break;
1698 	}
1699 
1700 	return done(parser);
1701 }
1702 
parseValue1(css_parser * parser)1703 css_error parseValue1(css_parser *parser)
1704 {
1705 	enum { Initial = 0, AfterValue = 1 };
1706 	parser_state *state = parserutils_stack_get_current(parser->states);
1707 	parser_state to = { sValue0, Initial };
1708 	const css_token *token;
1709 	css_error error;
1710 
1711 	/* value1 -> value value0 */
1712 
1713 	switch (state->substate) {
1714 	case Initial:
1715 	{
1716 		parser_state to = { sValue, Initial };
1717 		parser_state subsequent = { sValue1, AfterValue };
1718 
1719 		error = getToken(parser, &token);
1720 		if (error != CSS_OK)
1721 			return error;
1722 
1723 		error = pushBack(parser, token);
1724 		if (error != CSS_OK)
1725 			return error;
1726 
1727 		/* Grammar ambiguity -- assume ';' or '}' mark end */
1728 		if (token->type == CSS_TOKEN_CHAR &&
1729 				lwc_string_length(token->idata) == 1 &&
1730 				(lwc_string_data(token->idata)[0] == ';' ||
1731 				lwc_string_data(token->idata)[0] == '}')) {
1732 			/* Parse error */
1733 			parser->parseError = true;
1734 
1735 			return done(parser);
1736 		}
1737 
1738 		return transition(parser, to, subsequent);
1739 	}
1740 	case AfterValue:
1741 		if (parser->parseError)
1742 			return done(parser);
1743 		break;
1744 	}
1745 
1746 	return transitionNoRet(parser, to);
1747 }
1748 
parseValue0(css_parser * parser)1749 css_error parseValue0(css_parser *parser)
1750 {
1751 	enum { Initial = 0, AfterValue = 1 };
1752 	parser_state *state = parserutils_stack_get_current(parser->states);
1753 	const css_token *token;
1754 	css_error error;
1755 
1756 	/* value0 -> value value0
1757 	 *        ->
1758 	 */
1759 
1760 	while (1) {
1761 		switch (state->substate) {
1762 		case Initial:
1763 		{
1764 			parser_state to = { sValue, Initial };
1765 			parser_state subsequent = { sValue0, AfterValue };
1766 
1767 			error = getToken(parser, &token);
1768 			if (error != CSS_OK)
1769 				return error;
1770 
1771 			error = pushBack(parser, token);
1772 			if (error != CSS_OK)
1773 				return error;
1774 
1775 			if (token->type == CSS_TOKEN_EOF)
1776 				return done(parser);
1777 
1778 			/* Grammar ambiguity -- assume ';' or '}' mark end */
1779 			if (token->type == CSS_TOKEN_CHAR &&
1780 					lwc_string_length(
1781 						token->idata) == 1 &&
1782 					(lwc_string_data(
1783 						token->idata)[0] == ';' ||
1784 					lwc_string_data(
1785 						token->idata)[0] == '}')) {
1786 				return done(parser);
1787 			}
1788 
1789 			return transition(parser, to, subsequent);
1790 		}
1791 		case AfterValue:
1792 			if (parser->parseError)
1793 				return done(parser);
1794 
1795 			state->substate = Initial;
1796 
1797 			break;
1798 		}
1799 	}
1800 
1801 	return done(parser);
1802 }
1803 
parseValue(css_parser * parser)1804 css_error parseValue(css_parser *parser)
1805 {
1806 	enum { Initial = 0, WS = 1 };
1807 	parser_state *state = parserutils_stack_get_current(parser->states);
1808 	const css_token *token;
1809 	css_error error;
1810 
1811 	/* value  -> any
1812 	 *        -> block
1813 	 *        -> ATKEYWORD ws
1814 	 */
1815 
1816 	switch (state->substate) {
1817 	case Initial:
1818 		error = getToken(parser, &token);
1819 		if (error != CSS_OK)
1820 			return error;
1821 
1822 		if (token->type == CSS_TOKEN_ATKEYWORD) {
1823 			state->substate = WS;
1824 		} else if (token->type == CSS_TOKEN_CHAR &&
1825 				lwc_string_length(token->idata) == 1 &&
1826 				lwc_string_data(token->idata)[0] == '{') {
1827 			/* Grammar ambiguity. Assume block. */
1828 			parser_state to = { sBlock, Initial };
1829 
1830 			error = pushBack(parser, token);
1831 			if (error != CSS_OK)
1832 				return error;
1833 
1834 			return transitionNoRet(parser, to);
1835 		} else {
1836 			parser_state to = { sAny, Initial };
1837 
1838 			error = pushBack(parser, token);
1839 			if (error != CSS_OK)
1840 				return error;
1841 
1842 			return transitionNoRet(parser, to);
1843 		}
1844 
1845 		/* Fall through */
1846 	case WS:
1847 		error = eatWS(parser);
1848 		if (error != CSS_OK)
1849 			return error;
1850 
1851 		break;
1852 	}
1853 
1854 	return done(parser);
1855 }
1856 
parseAny0(css_parser * parser)1857 css_error parseAny0(css_parser *parser)
1858 {
1859 	enum { Initial = 0, AfterAny = 1 };
1860 	parser_state *state = parserutils_stack_get_current(parser->states);
1861 	const css_token *token;
1862 	css_error error;
1863 
1864 	/* any0 -> any any0
1865 	 *      ->
1866 	 */
1867 
1868 	while (1) {
1869 		switch (state->substate) {
1870 		case Initial:
1871 		{
1872 			parser_state to = { sAny, Initial };
1873 			parser_state subsequent = { sAny0, AfterAny };
1874 
1875 			error = getToken(parser, &token);
1876 			if (error != CSS_OK)
1877 				return error;
1878 
1879 			error = pushBack(parser, token);
1880 			if (error != CSS_OK)
1881 				return error;
1882 
1883 			if (token->type == CSS_TOKEN_EOF)
1884 				return done(parser);
1885 
1886 			/* Grammar ambiguity:
1887 			 * assume '{', ';', ')', ']' mark end */
1888 			if (token->type == CSS_TOKEN_CHAR &&
1889 					lwc_string_length(
1890 						token->idata) == 1 &&
1891 					(lwc_string_data(
1892 						token->idata)[0] == '{' ||
1893 					lwc_string_data(
1894 						token->idata)[0] == ';' ||
1895 					lwc_string_data(
1896 						token->idata)[0] == ')' ||
1897 					lwc_string_data(
1898 						token->idata)[0] == ']')) {
1899 				return done(parser);
1900 			}
1901 
1902 			return transition(parser, to, subsequent);
1903 		}
1904 		case AfterAny:
1905 			if (parser->parseError)
1906 				return done(parser);
1907 
1908 			state->substate = Initial;
1909 
1910 			break;
1911 		}
1912 	}
1913 
1914 	return done(parser);
1915 }
1916 
parseAny1(css_parser * parser)1917 css_error parseAny1(css_parser *parser)
1918 {
1919 	enum { Initial = 0, AfterAny = 1, AfterAny0 = 2 };
1920 	parser_state *state = parserutils_stack_get_current(parser->states);
1921 	const css_token *token;
1922 	css_error error;
1923 
1924 	/* any1 -> any any0 */
1925 
1926 	switch (state->substate) {
1927 	case Initial:
1928 	{
1929 		parser_state to = { sAny, Initial };
1930 		parser_state subsequent = { sAny1, AfterAny };
1931 
1932 		return transition(parser, to, subsequent);
1933 	}
1934 	case AfterAny:
1935 	{
1936 		parser_state to = { sAny0, Initial };
1937 		parser_state subsequent = { sAny1, AfterAny0 };
1938 
1939 		if (parser->parseError)
1940 			return done(parser);
1941 
1942 		return transition(parser, to, subsequent);
1943 	}
1944 	case AfterAny0:
1945 		if (parser->parseError)
1946 			return done(parser);
1947 
1948 		error = getToken(parser, &token);
1949 		if (error != CSS_OK)
1950 			return error;
1951 
1952 		error = pushBack(parser, token);
1953 		if (error != CSS_OK)
1954 			return error;
1955 
1956 		if (token->type == CSS_TOKEN_EOF)
1957 			return done(parser);
1958 
1959 		/* Grammar ambiguity: any0 can be followed by
1960 		 * '{', ';', ')', ']'. any1 can only be followed by '{'. */
1961 		if (token->type == CSS_TOKEN_CHAR &&
1962 				lwc_string_length(token->idata) == 1) {
1963 			if (lwc_string_data(token->idata)[0] == ';' ||
1964 					lwc_string_data(
1965 						token->idata)[0] == ')' ||
1966 					lwc_string_data(
1967 						token->idata)[0] == ']') {
1968 				parser_state to = { sAny, Initial };
1969 				parser_state subsequent = { sAny1, AfterAny };
1970 
1971 				return transition(parser, to, subsequent);
1972 			} else if (lwc_string_data(token->idata)[0] != '{') {
1973 				/* parse error */
1974 				parser->parseError = true;
1975 			}
1976 		} else {
1977 			/* parse error */
1978 			parser->parseError = true;
1979 		}
1980 	}
1981 
1982 	return done(parser);
1983 }
1984 
parseAny(css_parser * parser)1985 css_error parseAny(css_parser *parser)
1986 {
1987 	enum { Initial = 0, WS = 1, AfterAny0 = 2, WS2 = 3 };
1988 	parser_state *state = parserutils_stack_get_current(parser->states);
1989 	const css_token *token;
1990 	css_error error;
1991 
1992 	/* any -> IDENT ws
1993 	 *     -> NUMBER ws
1994 	 *     -> PERCENTAGE ws
1995 	 *     -> DIMENSION ws
1996 	 *     -> STRING ws
1997 	 *     -> CHAR ws
1998 	 *     -> URI ws
1999 	 *     -> HASH ws
2000 	 *     -> UNICODE-RANGE ws
2001 	 *     -> INCLUDES ws
2002 	 *     -> DASHMATCH ws
2003 	 *     -> PREFIXMATCH ws
2004 	 *     -> SUFFIXMATCH ws
2005 	 *     -> SUBSTRINGMATCH ws
2006 	 *     -> FUNCTION ws any0 ')' ws
2007 	 *     -> '(' ws any0 ')' ws
2008 	 *     -> '[' ws any0 ']' ws
2009 	 */
2010 
2011 	switch (state->substate) {
2012 	case Initial:
2013 		error = getToken(parser, &token);
2014 		if (error != CSS_OK)
2015 			return error;
2016 
2017 		if (token->type != CSS_TOKEN_IDENT &&
2018 				token->type != CSS_TOKEN_NUMBER &&
2019 				token->type != CSS_TOKEN_PERCENTAGE &&
2020 				token->type != CSS_TOKEN_DIMENSION &&
2021 				token->type != CSS_TOKEN_STRING &&
2022 				token->type != CSS_TOKEN_CHAR &&
2023 				token->type != CSS_TOKEN_URI &&
2024 				token->type != CSS_TOKEN_HASH &&
2025 				token->type != CSS_TOKEN_UNICODE_RANGE &&
2026 				token->type != CSS_TOKEN_INCLUDES &&
2027 				token->type != CSS_TOKEN_DASHMATCH &&
2028 				token->type != CSS_TOKEN_PREFIXMATCH &&
2029 				token->type != CSS_TOKEN_SUFFIXMATCH &&
2030 				token->type != CSS_TOKEN_SUBSTRINGMATCH &&
2031 				token->type != CSS_TOKEN_FUNCTION) {
2032 			/* parse error */
2033 			parser->parseError = true;
2034 
2035 			return done(parser);
2036 		}
2037 
2038 		if (token->type == CSS_TOKEN_FUNCTION) {
2039 			parserutils_stack_push(parser->open_items, &")"[0]);
2040 			state->substate = WS;
2041 		} else if (token->type == CSS_TOKEN_CHAR &&
2042 				lwc_string_length(token->idata) == 1 &&
2043 				(lwc_string_data(token->idata)[0] == '(' ||
2044 				lwc_string_data(token->idata)[0] == '[')) {
2045 			parserutils_stack_push(parser->open_items,
2046 					&(lwc_string_data(
2047 					token->idata)[0] == '(' ? ")" : "]")[0]);
2048 			state->substate = WS;
2049 		} else {
2050 			state->substate = WS2;
2051 		}
2052 		/* Fall through */
2053 	case WS:
2054 	case WS2:
2055 	ws2:
2056 	{
2057 		parser_state to = { sAny0, Initial };
2058 		parser_state subsequent = { sAny, AfterAny0 };
2059 
2060 		error = eatWS(parser);
2061 		if (error != CSS_OK)
2062 			return error;
2063 
2064 		if (state->substate == WS2)
2065 			break;
2066 
2067 		return transition(parser, to, subsequent);
2068 	}
2069 	case AfterAny0:
2070 	{
2071 		parser_state to = { sAny0, Initial };
2072 		parser_state subsequent = { sAny, AfterAny0 };
2073 
2074 		error = getToken(parser, &token);
2075 		if (error != CSS_OK)
2076 			return error;
2077 
2078 		if (token->type == CSS_TOKEN_EOF) {
2079 			error = pushBack(parser, token);
2080 			if (error != CSS_OK)
2081 				return error;
2082 
2083 			/* parse error */
2084 			parser->parseError = true;
2085 
2086 			return done(parser);
2087 		}
2088 
2089 		/* Match correct close bracket (grammar ambiguity) */
2090 		if (token->type == CSS_TOKEN_CHAR &&
2091 				lwc_string_length(token->idata) == 1 &&
2092 				lwc_string_data(token->idata)[0] ==
2093 				((uint8_t *) parserutils_stack_get_current(
2094 						parser->open_items))[0]) {
2095 			parserutils_stack_pop(parser->open_items, NULL);
2096 			state->substate = WS2;
2097 			goto ws2;
2098 		}
2099 
2100 		return transition(parser, to, subsequent);
2101 	}
2102 	}
2103 
2104 	return done(parser);
2105 }
2106 
parseMalformedDeclaration(css_parser * parser)2107 css_error parseMalformedDeclaration(css_parser *parser)
2108 {
2109 	enum { Initial = 0, Go = 1 };
2110 	parser_state *state = parserutils_stack_get_current(parser->states);
2111 	const css_token *token = NULL;
2112 	css_error error;
2113 
2114 	/* Malformed declaration: read everything up to the next ; or }
2115 	 * We must ensure that pairs of {}, (), [], are balanced */
2116 
2117 	switch (state->substate) {
2118 	case Initial:
2119 	{
2120 		/* Clear stack of open items */
2121 		while (parserutils_stack_pop(parser->open_items, NULL) ==
2122 				PARSERUTILS_OK)
2123 			;
2124 
2125 		state->substate = Go;
2126 	}
2127 	/* Fall through */
2128 	case Go:
2129 		while (1) {
2130 			char want;
2131 			char *match;
2132 			const char *data;
2133 			size_t len;
2134 
2135 			error = getToken(parser, &token);
2136 			if (error != CSS_OK)
2137 				return error;
2138 
2139 			if (token->type == CSS_TOKEN_EOF)
2140 				break;
2141 
2142 			if (token->type != CSS_TOKEN_CHAR)
2143 				continue;
2144 
2145 			len = lwc_string_length(token->idata);
2146 			data = lwc_string_data(token->idata);
2147 
2148 			if (len != 1 || (data[0] != '{' && data[0] != '}' &&
2149 					data[0] != '[' && data[0] != ']' &&
2150 					data[0] != '(' && data[0] != ')' &&
2151 					data[0] != ';'))
2152 				continue;
2153 
2154 			match = parserutils_stack_get_current(
2155 					parser->open_items);
2156 
2157 			/* If the stack is empty, then we're done if we've got
2158 			 * either a ';' or '}' */
2159 			if (match == NULL) {
2160 				if (data[0] == ';' || data[0] == '}')
2161 					break;
2162 			}
2163 
2164 			/* Regardless, if we've got a semicolon, ignore it */
2165 			if (data[0] == ';')
2166 				continue;
2167 
2168 			/* Get corresponding start tokens for end tokens */
2169 			switch (data[0]) {
2170 			case '}':
2171 				want = '{';
2172 				break;
2173 			case ']':
2174 				want = '[';
2175 				break;
2176 			case ')':
2177 				want = '(';
2178 				break;
2179 			default:
2180 				want = 0;
2181 				break;
2182 			}
2183 
2184 			/* Either pop off the stack, if we've matched the
2185 			 * current item, or push the start token on */
2186 			if (match != NULL && *match == want) {
2187 				parserutils_stack_pop(
2188 					parser->open_items, NULL);
2189 			} else if (want == 0) {
2190 				parserutils_stack_push(parser->open_items,
2191 						&data[0]);
2192 			}
2193 		}
2194 	}
2195 
2196 	/* Push the last token (';', '}' or EOF) back */
2197 	error = pushBack(parser, token);
2198 	if (error != CSS_OK)
2199 		return error;
2200 
2201 	/* Discard the tokens we've read */
2202 	discard_tokens(parser);
2203 
2204 	return done(parser);
2205 }
2206 
parseMalformedSelector(css_parser * parser)2207 css_error parseMalformedSelector(css_parser *parser)
2208 {
2209 	enum { Initial = 0, Go = 1 };
2210 	parser_state *state = parserutils_stack_get_current(parser->states);
2211 	const css_token *token;
2212 	css_error error;
2213 
2214 	/* Malformed selector: discard the entirety of the next block,
2215 	 * ensuring we correctly match pairs of {}, [], and (). */
2216 
2217 	switch (state->substate) {
2218 	case Initial:
2219 		/* Clear the stack of open items */
2220 		while (parserutils_stack_pop(parser->open_items, NULL) ==
2221 				PARSERUTILS_OK)
2222 			;
2223 
2224 		state->substate = Go;
2225 		/* Fall through */
2226 	case Go:
2227 		while (1) {
2228 			char want;
2229 			char *match;
2230 			const char *data;
2231 			size_t len;
2232 
2233 			error = getToken(parser, &token);
2234 			if (error != CSS_OK)
2235 				return error;
2236 
2237 			if (token->type == CSS_TOKEN_EOF)
2238 				break;
2239 
2240 			if (token->type != CSS_TOKEN_CHAR)
2241 				continue;
2242 
2243 			len = lwc_string_length(token->idata);
2244 			data = lwc_string_data(token->idata);
2245 
2246 			if (len != 1 || (data[0] != '{' && data[0] != '}' &&
2247 					data[0] != '[' && data[0] != ']' &&
2248 					data[0] != '(' && data[0] != ')'))
2249 				continue;
2250 
2251 			match = parserutils_stack_get_current(
2252 					parser->open_items);
2253 
2254 			/* Get corresponding start tokens for end tokens */
2255 			switch (data[0]) {
2256 			case '}':
2257 				want = '{';
2258 				break;
2259 			case ']':
2260 				want = '[';
2261 				break;
2262 			case ')':
2263 				want = '(';
2264 				break;
2265 			default:
2266 				want = 0;
2267 				break;
2268 			}
2269 
2270 			/* Either pop off the stack, if we've matched the
2271 			 * current item, or push the start token on */
2272 			if (match != NULL && *match == want) {
2273 				parserutils_stack_pop(
2274 					parser->open_items, NULL);
2275 			} else if (want == 0) {
2276 				parserutils_stack_push(parser->open_items,
2277 						&data[0]);
2278 			}
2279 
2280 			/* If we encountered a '}', there was data on the stack
2281 			 * before, and the stack's now empty, then we've popped
2282 			 * a '{' off the stack. That means we've found the end
2283 			 * of the block, so we're done here. */
2284 			if (want == '{' && match != NULL &&
2285 					parserutils_stack_get_current(
2286 					parser->open_items) == NULL)
2287 				break;
2288 		}
2289 	}
2290 
2291 	/* Consume any trailing whitespace after the ruleset */
2292 	error = eatWS(parser);
2293 	if (error != CSS_OK)
2294 		return error;
2295 
2296 	/* Discard the tokens we've read */
2297 	discard_tokens(parser);
2298 
2299 	return done(parser);
2300 }
2301 
parseMalformedAtRule(css_parser * parser)2302 css_error parseMalformedAtRule(css_parser *parser)
2303 {
2304 	enum { Initial = 0, Go = 1 };
2305 	parser_state *state = parserutils_stack_get_current(parser->states);
2306 	const css_token *token = NULL;
2307 	css_error error;
2308 
2309 	/* Malformed at-rule: read everything up to the next ; or the next
2310 	 * block, whichever is first.
2311 	 * We must ensure that pairs of {}, (), [], are balanced */
2312 
2313 	switch (state->substate) {
2314 	case Initial:
2315 	{
2316 		/* Clear stack of open items */
2317 		while (parserutils_stack_pop(parser->open_items, NULL) ==
2318 				PARSERUTILS_OK)
2319 			;
2320 
2321 		state->substate = Go;
2322 	}
2323 	/* Fall through */
2324 	case Go:
2325 		while (1) {
2326 			char want;
2327 			char *match;
2328 			const char *data;
2329 			size_t len;
2330 
2331 			error = getToken(parser, &token);
2332 			if (error != CSS_OK)
2333 				return error;
2334 
2335 			if (token->type == CSS_TOKEN_EOF)
2336 				break;
2337 
2338 			if (token->type != CSS_TOKEN_CHAR)
2339 				continue;
2340 
2341 			len = lwc_string_length(token->idata);
2342 			data = lwc_string_data(token->idata);
2343 
2344 			if (len != 1 || (data[0] != '{' && data[0] != '}' &&
2345 					data[0] != '[' && data[0] != ']' &&
2346 					data[0] != '(' && data[0] != ')' &&
2347 					data[0] != ';'))
2348 				continue;
2349 
2350 			match = parserutils_stack_get_current(
2351 					parser->open_items);
2352 
2353 			/* If we have a semicolon, then we're either done or
2354 			 * need to ignore it */
2355 			if (data[0] == ';') {
2356 				if (match == NULL)
2357 					break;
2358 				else
2359 					continue;
2360 			}
2361 
2362 			/* Get corresponding start tokens for end tokens */
2363 			switch (data[0]) {
2364 			case '}':
2365 				want = '{';
2366 				break;
2367 			case ']':
2368 				want = '[';
2369 				break;
2370 			case ')':
2371 				want = '(';
2372 				break;
2373 			default:
2374 				want = 0;
2375 				break;
2376 			}
2377 
2378 			/* Either pop off the stack, if we've matched the
2379 			 * current item, or push the start token on */
2380 			if (match != NULL && *match == want) {
2381 				parserutils_stack_pop(
2382 					parser->open_items, NULL);
2383 			} else if (want == 0) {
2384 				parserutils_stack_push(parser->open_items,
2385 						&data[0]);
2386 			}
2387 
2388 			/* If we encountered a '}', there was data on the stack
2389 			 * before, and the stack's now empty, then we've popped
2390 			 * a '{' off the stack. That means we've found the end
2391 			 * of the block, so we're done here. */
2392 			if (want == '{' && match != NULL &&
2393 					parserutils_stack_get_current(
2394 					parser->open_items) == NULL)
2395 				break;
2396 		}
2397 	}
2398 
2399 	/* Consume any trailing whitespace after the at-rule */
2400 	error = eatWS(parser);
2401 	if (error != CSS_OK)
2402 		return error;
2403 
2404 	/* Discard the tokens we've read */
2405 	discard_tokens(parser);
2406 
2407 	return done(parser);
2408 }
2409 
parseInlineStyle(css_parser * parser)2410 css_error parseInlineStyle(css_parser *parser)
2411 {
2412 	enum { Initial = 0, WS = 1, AfterISBody0 = 2 };
2413 	parser_state *state = parserutils_stack_get_current(parser->states);
2414 	css_error error;
2415 
2416 	/* inline-style = ws is-body0 */
2417 
2418 	switch (state->substate) {
2419 	case Initial:
2420 		/* Emit fake events such that the language parser knows
2421 		 * no different from a normal parse. */
2422 
2423 		if (parser->event != NULL) {
2424 			/* 1) begin stylesheet */
2425 			parser->event(CSS_PARSER_START_STYLESHEET, NULL,
2426 					parser->event_pw);
2427 
2428 			/* 2) begin ruleset */
2429 			parser->event(CSS_PARSER_START_RULESET, NULL,
2430 					parser->event_pw);
2431 		}
2432 
2433 		/* Fall through */
2434 	case WS:
2435 	{
2436 		parser_state to = { sISBody0, Initial };
2437 		parser_state subsequent = { sInlineStyle, AfterISBody0 };
2438 
2439 		state->substate = WS;
2440 
2441 		/* Consume leading whitespace */
2442 		error = eatWS(parser);
2443 		if (error != CSS_OK)
2444 			return error;
2445 
2446 		return transition(parser, to, subsequent);
2447 	}
2448 	case AfterISBody0:
2449 		/* Clean up any remaining tokens */
2450 		discard_tokens(parser);
2451 
2452 		/* Emit remaining fake events to end the parse */
2453 		if (parser->event != NULL) {
2454 			/* 1) end ruleset */
2455 			parser->event(CSS_PARSER_END_RULESET, NULL,
2456 					parser->event_pw);
2457 
2458 			/* 2) end stylesheet */
2459 			parser->event(CSS_PARSER_END_STYLESHEET, NULL,
2460 					parser->event_pw);
2461 		}
2462 
2463 		break;
2464 	}
2465 
2466 	return done(parser);
2467 }
2468 
parseISBody0(css_parser * parser)2469 css_error parseISBody0(css_parser *parser)
2470 {
2471 	enum { Initial = 0, AfterISBody = 1 };
2472 	parser_state *state = parserutils_stack_get_current(parser->states);
2473 	const css_token *token;
2474 	css_error error;
2475 
2476 	/* is-body0     -> is-body is-body0
2477 	 *              ->
2478 	 */
2479 
2480 	while (1) {
2481 		switch (state->substate) {
2482 		case Initial:
2483 		{
2484 			parser_state to = { sISBody, Initial };
2485 			parser_state subsequent = { sISBody0, AfterISBody };
2486 
2487 			error = getToken(parser, &token);
2488 			if (error != CSS_OK)
2489 				return error;
2490 
2491 			error = pushBack(parser, token);
2492 			if (error != CSS_OK)
2493 				return error;
2494 
2495 			/* EOF is the only way out */
2496 			if (token->type == CSS_TOKEN_EOF)
2497 				return done(parser);
2498 
2499 			return transition(parser, to, subsequent);
2500 		}
2501 		case AfterISBody:
2502 			/* Unless there's a parse error */
2503 			if (parser->parseError)
2504 				return done(parser);
2505 
2506 			state->substate = Initial;
2507 
2508 			break;
2509 		}
2510 	}
2511 
2512 	return done(parser);
2513 }
2514 
parseISBody(css_parser * parser)2515 css_error parseISBody(css_parser *parser)
2516 {
2517 	enum { Initial = 0, DeclList = 1, Brace = 2, WS = 3 };
2518 	parser_state *state = parserutils_stack_get_current(parser->states);
2519 	const css_token *token;
2520 	css_error error;
2521 
2522 	/* is-body      -> declaration decl-list '}' ws
2523 	 *              -> decl-list '}' ws
2524 	 *
2525 	 * Note that this looks suspiciously similar to ruleset-end.
2526 	 * The difference here, however, is that we never end the ruleset.
2527 	 */
2528 
2529 	switch (state->substate) {
2530 	case Initial:
2531 		error = getToken(parser, &token);
2532 		if (error != CSS_OK)
2533 			return error;
2534 
2535 		error = pushBack(parser, token);
2536 		if (error != CSS_OK)
2537 			return error;
2538 
2539 		/* If this can't possibly be the start of a decl-list, then
2540 		 * attempt to parse a declaration. This will catch any invalid
2541 		 * input at this point and read to the start of the next
2542 		 * declaration. FIRST(decl-list) = (';', '}') */
2543 		if (token->type != CSS_TOKEN_CHAR ||
2544 				lwc_string_length(token->idata) != 1 ||
2545 				(lwc_string_data(token->idata)[0] != '}' &&
2546 				lwc_string_data(token->idata)[0] != ';')) {
2547 			parser_state to = { sDeclaration, Initial };
2548 			parser_state subsequent = { sISBody, DeclList };
2549 
2550 			return transition(parser, to, subsequent);
2551 		}
2552 
2553 		state->substate = DeclList;
2554 		/* Fall through */
2555 	case DeclList:
2556 	{
2557 		parser_state to = { sDeclList, Initial };
2558 		parser_state subsequent = { sISBody, Brace };
2559 
2560 		return transition(parser, to, subsequent);
2561 	}
2562 	case Brace:
2563 		error = getToken(parser, &token);
2564 		if (error != CSS_OK)
2565 			return error;
2566 
2567 		if (token->type == CSS_TOKEN_EOF) {
2568 			error = pushBack(parser, token);
2569 			if (error != CSS_OK)
2570 				return error;
2571 
2572 			return done(parser);
2573 		}
2574 
2575 		if (token->type != CSS_TOKEN_CHAR ||
2576 				lwc_string_length(token->idata) != 1 ||
2577 				lwc_string_data(token->idata)[0] != '}') {
2578 			/* This should never happen, as FOLLOW(decl-list)
2579 			 * contains only '}' */
2580 			assert(0 && "Expected }");
2581 		}
2582 
2583 		state->substate = WS;
2584 		/* Fall through */
2585 	case WS:
2586 		error = eatWS(parser);
2587 		if (error != CSS_OK)
2588 			return error;
2589 
2590 		break;
2591 	}
2592 
2593 	return done(parser);
2594 }
2595 
parseMediaQuery(css_parser * parser)2596 css_error parseMediaQuery(css_parser *parser)
2597 {
2598 	enum { Initial = 0, AfterAtRule = 1 };
2599 	parser_state *state = parserutils_stack_get_current(parser->states);
2600 
2601 	/* media-query = at-rule */
2602 
2603 	switch (state->substate) {
2604 	case Initial:
2605 	{
2606 		parser_state to = { sAtRule, Initial };
2607 		parser_state subsequent = { sMediaQuery, AfterAtRule };
2608 
2609 		return transition(parser, to, subsequent);
2610 	}
2611 	case AfterAtRule:
2612 		/* Clean up any remaining tokens */
2613 		discard_tokens(parser);
2614 		break;
2615 	}
2616 
2617 	return done(parser);
2618 }
2619 
2620 /**
2621  * Discard the contents of the token vector
2622  *
2623  * \param parser The parser whose tokens we are cleaning up.
2624  */
discard_tokens(css_parser * parser)2625 void discard_tokens(css_parser *parser)
2626 {
2627 	int32_t ctx = 0;
2628 	const css_token *tok;
2629 
2630 	while ((tok = parserutils_vector_iterate(
2631 			parser->tokens, &ctx)) != NULL) {
2632 		if (tok->idata != NULL) {
2633 			lwc_string_unref(tok->idata);
2634 		}
2635 	}
2636 
2637 	parserutils_vector_clear(parser->tokens);
2638 }
2639 
2640 #ifndef NDEBUG
2641 #ifdef DEBUG_STACK
printer(void * item)2642 static void printer(void *item)
2643 {
2644 	parser_state *s = item;
2645 
2646 	printf("[%d %d]", s->state, s->substate);
2647 }
2648 #endif
2649 
2650 #ifdef DEBUG_EVENTS
tprinter(void * token)2651 static void tprinter(void *token)
2652 {
2653 	css_token *t = token;
2654 
2655 	if (t->idata) {
2656 		printf("%d: %.*s", t->type,
2657 				(int) lwc_string_length(t->idata),
2658 				lwc_string_data(t->idata));
2659 	} else if (t->data.data)
2660 		printf("%d: %.*s", t->type, (int) t->data.len, t->data.data);
2661 	else
2662 		printf("%d", t->type);
2663 }
2664 #endif
2665 #endif
2666 
2667