PageRenderTime 105ms CodeModel.GetById 2ms app.highlight 95ms RepoModel.GetById 1ms app.codeStats 0ms

/peek-build/src/netdepends/libcss/src/parse/language.c

https://bitbucket.org/C0deMaver1ck/peeklinux
C | 1301 lines | 914 code | 242 blank | 145 comment | 436 complexity | daf8d671d66ca1bf4a5e5bc95d36f4ea MD5 | raw file
   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 <string.h>
  10
  11#include <parserutils/utils/stack.h>
  12
  13#include "stylesheet.h"
  14#include "lex/lex.h"
  15#include "parse/important.h"
  16#include "parse/language.h"
  17#include "parse/parse.h"
  18#include "parse/propstrings.h"
  19#include "parse/properties/properties.h"
  20#include "parse/properties/utils.h"
  21
  22#include "utils/parserutilserror.h"
  23#include "utils/utils.h"
  24
  25typedef struct context_entry {
  26	css_parser_event type;		/**< Type of entry */
  27	void *data;			/**< Data for context */
  28} context_entry;
  29
  30/* Event handlers */
  31static css_error language_handle_event(css_parser_event type, 
  32		const parserutils_vector *tokens, void *pw);
  33static css_error handleStartStylesheet(css_language *c, 
  34		const parserutils_vector *vector);
  35static css_error handleEndStylesheet(css_language *c, 
  36		const parserutils_vector *vector);
  37static css_error handleStartRuleset(css_language *c, 
  38		const parserutils_vector *vector);
  39static css_error handleEndRuleset(css_language *c, 
  40		const parserutils_vector *vector);
  41static css_error handleStartAtRule(css_language *c, 
  42		const parserutils_vector *vector);
  43static css_error handleEndAtRule(css_language *c, 
  44		const parserutils_vector *vector);
  45static css_error handleStartBlock(css_language *c, 
  46		const parserutils_vector *vector);
  47static css_error handleEndBlock(css_language *c, 
  48		const parserutils_vector *vector);
  49static css_error handleBlockContent(css_language *c, 
  50		const parserutils_vector *vector);
  51static css_error handleDeclaration(css_language *c, 
  52		const parserutils_vector *vector);
  53
  54/* At-rule parsing */
  55static css_error parseMediaList(css_language *c,
  56		const parserutils_vector *vector, int *ctx,
  57		uint64_t *media);
  58
  59/* Selector list parsing */
  60static css_error parseClass(css_language *c,
  61		const parserutils_vector *vector, int *ctx,
  62		css_selector_detail *specific);
  63static css_error parseAttrib(css_language *c, 
  64		const parserutils_vector *vector, int *ctx,
  65		css_selector_detail *specific);
  66static css_error parsePseudo(css_language *c,
  67		const parserutils_vector *vector, int *ctx,
  68		css_selector_detail *specific);
  69static css_error parseSpecific(css_language *c,
  70		const parserutils_vector *vector, int *ctx,
  71		css_selector **parent);
  72static css_error parseSelectorSpecifics(css_language *c,
  73		const parserutils_vector *vector, int *ctx,
  74		css_selector **parent);
  75static css_error parseSimpleSelector(css_language *c, 
  76		const parserutils_vector *vector, int *ctx, 
  77		css_selector **result);
  78static css_error parseCombinator(css_language *c, 
  79		const parserutils_vector *vector, int *ctx, 
  80		css_combinator *result);
  81static css_error parseSelector(css_language *c, 
  82		const parserutils_vector *vector, int *ctx, 
  83		css_selector **result);
  84static css_error parseSelectorList(css_language *c, 
  85		const parserutils_vector *vector, css_rule *rule);
  86
  87/* Declaration parsing */
  88static css_error parseProperty(css_language *c,
  89		const css_token *property, const parserutils_vector *vector,
  90		int *ctx, css_rule *rule);
  91
  92/**
  93 * Create a CSS language parser
  94 *
  95 * \param sheet	    The stylesheet object to parse for
  96 * \param parser    The core parser object to use
  97 * \param alloc	    Memory (de)allocation function
  98 * \param pw	    Pointer to client-specific private data
  99 * \param language  Pointer to location to receive parser object
 100 * \return CSS_OK on success,
 101 *	   CSS_BADPARM on bad parameters,
 102 *	   CSS_NOMEM on memory exhaustion
 103 */
 104css_error css_language_create(css_stylesheet *sheet, css_parser *parser,
 105		css_allocator_fn alloc, void *pw, void **language)
 106{
 107	css_language *c;
 108	css_parser_optparams params;
 109	parserutils_error perror;
 110	lwc_error lerror;
 111	css_error error;
 112	int i;
 113
 114	if (sheet == NULL || parser == NULL || alloc == NULL || 
 115			language == NULL)
 116		return CSS_BADPARM;
 117
 118	c = alloc(NULL, sizeof(css_language), pw);
 119	if (c == NULL)
 120		return CSS_NOMEM;
 121
 122	perror = parserutils_stack_create(sizeof(context_entry), 
 123			STACK_CHUNK, (parserutils_alloc) alloc, pw, 
 124			&c->context);
 125	if (perror != PARSERUTILS_OK) {
 126		alloc(c, 0, pw);
 127		return css_error_from_parserutils_error(perror);
 128	}
 129
 130	/* Intern all known strings */
 131	for (i = 0; i < LAST_KNOWN; i++) {
 132		lerror = lwc_intern_string(stringmap[i].data,
 133					    stringmap[i].len,
 134					    &(c->strings[i]));
 135		if (lerror != lwc_error_ok) {
 136			parserutils_stack_destroy(c->context);
 137			alloc(c, 0, pw);
 138			return CSS_NOMEM;
 139		}
 140	}
 141
 142	params.event_handler.handler = language_handle_event;
 143	params.event_handler.pw = c;
 144	error = css_parser_setopt(parser, CSS_PARSER_EVENT_HANDLER, &params);
 145	if (error != CSS_OK) {
 146		parserutils_stack_destroy(c->context);
 147		alloc(c, 0, pw);
 148		return error;
 149	}
 150
 151	c->sheet = sheet;
 152	c->state = BEFORE_CHARSET;
 153	c->alloc = alloc;
 154	c->pw = pw;
 155
 156	*language = c;
 157
 158	return CSS_OK;
 159}
 160
 161/**
 162 * Destroy a CSS language parser
 163 *
 164 * \param language  The parser to destroy
 165 * \return CSS_OK on success, appropriate error otherwise
 166 */
 167css_error css_language_destroy(css_language *language)
 168{
 169	int i;
 170	
 171	if (language == NULL)
 172		return CSS_BADPARM;
 173
 174	parserutils_stack_destroy(language->context);
 175	
 176	for (i = 0; i < LAST_KNOWN; ++i) {
 177		lwc_string_unref(language->strings[i]);
 178	}
 179	
 180	language->alloc(language, 0, language->pw);
 181
 182	return CSS_OK;
 183}
 184
 185/**
 186 * Handler for core parser events
 187 *
 188 * \param type	  The event type
 189 * \param tokens  Vector of tokens read since last event, or NULL
 190 * \param pw	  Pointer to handler context
 191 * \return CSS_OK on success, CSS_INVALID to indicate parse error, 
 192 *	   appropriate error otherwise.
 193 */
 194css_error language_handle_event(css_parser_event type, 
 195		const parserutils_vector *tokens, void *pw)
 196{
 197	css_language *language = (css_language *) pw;
 198
 199	switch (type) {
 200	case CSS_PARSER_START_STYLESHEET:
 201		return handleStartStylesheet(language, tokens);
 202	case CSS_PARSER_END_STYLESHEET:
 203		return handleEndStylesheet(language, tokens);
 204	case CSS_PARSER_START_RULESET:
 205		return handleStartRuleset(language, tokens);
 206	case CSS_PARSER_END_RULESET:
 207		return handleEndRuleset(language, tokens);
 208	case CSS_PARSER_START_ATRULE:
 209		return handleStartAtRule(language, tokens);
 210	case CSS_PARSER_END_ATRULE:
 211		return handleEndAtRule(language, tokens);
 212	case CSS_PARSER_START_BLOCK:
 213		return handleStartBlock(language, tokens);
 214	case CSS_PARSER_END_BLOCK:
 215		return handleEndBlock(language, tokens);
 216	case CSS_PARSER_BLOCK_CONTENT:
 217		return handleBlockContent(language, tokens);
 218	case CSS_PARSER_DECLARATION:
 219		return handleDeclaration(language, tokens);
 220	}
 221
 222	return CSS_OK;
 223}
 224
 225/******************************************************************************
 226 * Parser stages							      *
 227 ******************************************************************************/
 228
 229css_error handleStartStylesheet(css_language *c, 
 230		const parserutils_vector *vector)
 231{
 232	parserutils_error perror;
 233	context_entry entry = { CSS_PARSER_START_STYLESHEET, NULL };
 234
 235	UNUSED(vector);
 236
 237	assert(c != NULL);
 238
 239	perror = parserutils_stack_push(c->context, (void *) &entry);
 240	if (perror != PARSERUTILS_OK) {
 241		return css_error_from_parserutils_error(perror);
 242	}
 243
 244	return CSS_OK;
 245}
 246
 247css_error handleEndStylesheet(css_language *c, const parserutils_vector *vector)
 248{
 249	parserutils_error perror;
 250	context_entry *entry;
 251
 252	UNUSED(vector);
 253
 254	assert(c != NULL);
 255
 256	entry = parserutils_stack_get_current(c->context);
 257	if (entry == NULL || entry->type != CSS_PARSER_START_STYLESHEET)
 258		return CSS_INVALID;
 259
 260	perror = parserutils_stack_pop(c->context, NULL);
 261	if (perror != PARSERUTILS_OK) {
 262		return css_error_from_parserutils_error(perror);
 263	}
 264
 265	return CSS_OK;
 266}
 267
 268css_error handleStartRuleset(css_language *c, const parserutils_vector *vector)
 269{
 270	parserutils_error perror;
 271	css_error error;
 272	context_entry entry = { CSS_PARSER_START_RULESET, NULL };
 273	context_entry *cur;
 274	css_rule *parent_rule = NULL;
 275	css_rule *rule = NULL;
 276
 277	assert(c != NULL);
 278
 279	/* Retrieve parent rule from stack, if any */
 280	cur = parserutils_stack_get_current(c->context);
 281	if (cur != NULL && cur->type != CSS_PARSER_START_STYLESHEET)
 282		parent_rule = cur->data;
 283
 284	error = css_stylesheet_rule_create(c->sheet, CSS_RULE_SELECTOR, &rule);
 285	if (error != CSS_OK)
 286		return error;
 287
 288	if (vector != NULL) {
 289		/* Parse selectors, if there are any */
 290		error = parseSelectorList(c, vector, rule);
 291		if (error != CSS_OK) {
 292			css_stylesheet_rule_destroy(c->sheet, rule);
 293			return error;
 294		}
 295	}
 296
 297	entry.data = rule;
 298
 299	perror = parserutils_stack_push(c->context, (void *) &entry);
 300	if (perror != PARSERUTILS_OK) {
 301		css_stylesheet_rule_destroy(c->sheet, rule);
 302		return css_error_from_parserutils_error(perror);
 303	}
 304
 305	error = css_stylesheet_add_rule(c->sheet, rule, parent_rule);
 306	if (error != CSS_OK) {
 307		parserutils_stack_pop(c->context, NULL);
 308		css_stylesheet_rule_destroy(c->sheet, rule);
 309		return error;
 310	}
 311
 312	/* Flag that we've had a valid rule, so @import/@charset have 
 313	 * no effect. */
 314	c->state = HAD_RULE;
 315
 316	/* Rule is now owned by the sheet, so no need to destroy it */
 317
 318	return CSS_OK;
 319}
 320
 321css_error handleEndRuleset(css_language *c, const parserutils_vector *vector)
 322{
 323	parserutils_error perror;
 324	context_entry *entry;
 325
 326	UNUSED(vector);
 327
 328	assert(c != NULL);
 329
 330	entry = parserutils_stack_get_current(c->context);
 331	if (entry == NULL || entry->type != CSS_PARSER_START_RULESET)
 332		return CSS_INVALID;
 333
 334	perror = parserutils_stack_pop(c->context, NULL);
 335	if (perror != PARSERUTILS_OK) {
 336		return css_error_from_parserutils_error(perror);
 337	}
 338
 339	return CSS_OK;
 340}
 341
 342css_error handleStartAtRule(css_language *c, const parserutils_vector *vector)
 343{
 344	parserutils_error perror;
 345	context_entry entry = { CSS_PARSER_START_ATRULE, NULL };
 346	const css_token *token = NULL;
 347	const css_token *atkeyword = NULL;
 348	int32_t ctx = 0;
 349	bool match = false;
 350	css_rule *rule;
 351	css_error error;
 352
 353	/* vector contains: ATKEYWORD ws any0 */
 354
 355	assert(c != NULL);
 356
 357	atkeyword = parserutils_vector_iterate(vector, &ctx);
 358
 359	consumeWhitespace(vector, &ctx);
 360
 361	/* We now have an ATKEYWORD and the context for the start of any0, if 
 362	 * there is one */
 363	assert(atkeyword != NULL && atkeyword->type == CSS_TOKEN_ATKEYWORD);
 364
 365	if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[CHARSET], 
 366			&match) == lwc_error_ok && match) {
 367		if (c->state == BEFORE_CHARSET) {
 368			const css_token *charset;
 369
 370			/* any0 = STRING */
 371			if (ctx == 0)
 372				return CSS_INVALID;
 373
 374			charset = parserutils_vector_iterate(vector, &ctx);
 375			if (charset == NULL || 
 376					charset->type != CSS_TOKEN_STRING)
 377				return CSS_INVALID;
 378
 379			token = parserutils_vector_iterate(vector, &ctx);
 380			if (token != NULL)
 381				return CSS_INVALID;
 382
 383			error = css_stylesheet_rule_create(c->sheet, 
 384					CSS_RULE_CHARSET, &rule);
 385			if (error != CSS_OK)
 386				return error;
 387
 388			error = css_stylesheet_rule_set_charset(c->sheet, rule,
 389					charset->idata);
 390			if (error != CSS_OK) {
 391				css_stylesheet_rule_destroy(c->sheet, rule);
 392				return error;
 393			}
 394
 395			error = css_stylesheet_add_rule(c->sheet, rule, NULL);
 396			if (error != CSS_OK) {
 397				css_stylesheet_rule_destroy(c->sheet, rule);
 398				return error;
 399			}
 400
 401			/* Rule is now owned by the sheet, 
 402			 * so no need to destroy it */
 403
 404			c->state = BEFORE_RULES;
 405		} else {
 406			return CSS_INVALID;
 407		}
 408	} else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[IMPORT], 
 409			&match) == lwc_error_ok && match) {
 410		if (c->state != HAD_RULE) {
 411			lwc_string *url;
 412			uint64_t media = 0;
 413
 414			/* any0 = (STRING | URI) ws 
 415			 *	  (IDENT ws (',' ws IDENT ws)* )? */
 416			const css_token *uri = 
 417				parserutils_vector_iterate(vector, &ctx);
 418			if (uri == NULL || (uri->type != CSS_TOKEN_STRING && 
 419					uri->type != CSS_TOKEN_URI))
 420				return CSS_INVALID;
 421
 422			consumeWhitespace(vector, &ctx);
 423
 424			/* Parse media list */
 425			error = parseMediaList(c, vector, &ctx, &media);
 426			if (error != CSS_OK)
 427				return error;
 428
 429			/* Create rule */
 430			error = css_stylesheet_rule_create(c->sheet, 
 431					CSS_RULE_IMPORT, &rule);
 432			if (error != CSS_OK)
 433				return error;
 434
 435			/* Resolve import URI */
 436			error = c->sheet->resolve(c->sheet->resolve_pw,
 437					c->sheet->url,
 438					uri->idata, &url);
 439			if (error != CSS_OK) {
 440				css_stylesheet_rule_destroy(c->sheet, rule);
 441				return error;
 442			}
 443
 444			/* Inform rule of it */
 445			error = css_stylesheet_rule_set_nascent_import(c->sheet,
 446					rule, url, media);
 447			if (error != CSS_OK) {
 448				lwc_string_unref(url);
 449				css_stylesheet_rule_destroy(c->sheet, rule);
 450				return error;
 451			}
 452
 453			/* No longer care about url */
 454			lwc_string_unref(url);
 455
 456			/* Add rule to sheet */
 457			error = css_stylesheet_add_rule(c->sheet, rule, NULL);
 458			if (error != CSS_OK) {
 459				css_stylesheet_rule_destroy(c->sheet, rule);
 460				return error;
 461			}
 462
 463			/* Rule is now owned by the sheet, 
 464			 * so no need to destroy it */
 465
 466			c->state = BEFORE_RULES;
 467		} else {
 468			return CSS_INVALID;
 469		}
 470	} else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[MEDIA], 
 471			&match) == lwc_error_ok && match) {
 472		uint64_t media = 0;
 473
 474		/* any0 = IDENT ws (',' ws IDENT ws)* */
 475
 476		error = parseMediaList(c, vector, &ctx, &media);
 477		if (error != CSS_OK)
 478			return error;
 479
 480		error = css_stylesheet_rule_create(c->sheet, 
 481				CSS_RULE_MEDIA, &rule);
 482		if (error != CSS_OK)
 483			return error;
 484
 485		error = css_stylesheet_rule_set_media(c->sheet, rule, media);
 486		if (error != CSS_OK) {
 487			css_stylesheet_rule_destroy(c->sheet, rule);
 488			return error;
 489		}
 490
 491		error = css_stylesheet_add_rule(c->sheet, rule, NULL);
 492		if (error != CSS_OK) {
 493			css_stylesheet_rule_destroy(c->sheet, rule);
 494			return error;
 495		}
 496
 497		/* Rule is now owned by the sheet, 
 498		 * so no need to destroy it */
 499
 500		c->state = HAD_RULE;
 501	} else if (lwc_string_caseless_isequal(atkeyword->idata, c->strings[PAGE], 
 502			&match) == lwc_error_ok && match) {
 503		const css_token *token;
 504
 505		/* any0 = (':' IDENT)? ws */
 506
 507		error = css_stylesheet_rule_create(c->sheet,
 508				CSS_RULE_PAGE, &rule);
 509		if (error != CSS_OK)
 510			return error;
 511
 512		consumeWhitespace(vector, &ctx);
 513
 514		token = parserutils_vector_peek(vector, ctx);
 515		if (token != NULL) {
 516			css_selector *sel = NULL;
 517
 518			error = parseSelector(c, vector, &ctx, &sel);
 519			if (error != CSS_OK) {
 520				css_stylesheet_rule_destroy(c->sheet, rule);
 521				return error;
 522			}
 523
 524			error = css_stylesheet_rule_set_page_selector(c->sheet,
 525					rule, sel);
 526			if (error != CSS_OK) {
 527				css_stylesheet_selector_destroy(c->sheet, sel);
 528				css_stylesheet_rule_destroy(c->sheet, rule);
 529				return error;
 530			}
 531		}
 532
 533		error = css_stylesheet_add_rule(c->sheet, rule, NULL);
 534		if (error != CSS_OK) {
 535			css_stylesheet_rule_destroy(c->sheet, rule);
 536			return error;
 537		}
 538
 539		/* Rule is now owned by the sheet, 
 540		 * so no need to destroy it */
 541
 542		c->state = HAD_RULE;
 543	} else {
 544		return CSS_INVALID;
 545	}
 546
 547	entry.data = rule;
 548
 549	perror = parserutils_stack_push(c->context, (void *) &entry);
 550	if (perror != PARSERUTILS_OK) {
 551		return css_error_from_parserutils_error(perror);
 552	}
 553
 554	return CSS_OK;
 555}
 556
 557css_error handleEndAtRule(css_language *c, const parserutils_vector *vector)
 558{
 559	parserutils_error perror;
 560	context_entry *entry;
 561
 562	UNUSED(vector);
 563
 564	assert(c != NULL);
 565
 566	entry = parserutils_stack_get_current(c->context);
 567	if (entry == NULL || entry->type != CSS_PARSER_START_ATRULE)
 568		return CSS_INVALID;
 569
 570	perror = parserutils_stack_pop(c->context, NULL);
 571	if (perror != PARSERUTILS_OK) {
 572		return css_error_from_parserutils_error(perror);
 573	}
 574
 575	return CSS_OK;
 576}
 577
 578css_error handleStartBlock(css_language *c, const parserutils_vector *vector)
 579{
 580	parserutils_error perror;
 581	context_entry entry = { CSS_PARSER_START_BLOCK, NULL };
 582	context_entry *cur;
 583
 584	UNUSED(vector);
 585
 586	/* If the current item on the stack isn't a block, 
 587	 * then clone its data field. This ensures that the relevant rule
 588	 * is available when parsing the block contents. */
 589	cur = parserutils_stack_get_current(c->context);
 590	if (cur != NULL && cur->type != CSS_PARSER_START_BLOCK)
 591		entry.data = cur->data;
 592
 593	perror = parserutils_stack_push(c->context, (void *) &entry);
 594	if (perror != PARSERUTILS_OK) {
 595		return css_error_from_parserutils_error(perror);
 596	}
 597
 598	return CSS_OK;
 599}
 600
 601css_error handleEndBlock(css_language *c, const parserutils_vector *vector)
 602{
 603	parserutils_error perror;
 604	context_entry *entry;
 605	css_rule *rule;
 606
 607	UNUSED(vector);
 608
 609	entry = parserutils_stack_get_current(c->context);
 610	if (entry == NULL || entry->type != CSS_PARSER_START_BLOCK)
 611		return CSS_INVALID;
 612
 613	rule = entry->data;
 614
 615	perror = parserutils_stack_pop(c->context, NULL);
 616	if (perror != PARSERUTILS_OK) {
 617		return css_error_from_parserutils_error(perror);
 618	}
 619
 620	/* If the block we just popped off the stack was associated with a 
 621	 * non-block stack entry, and that entry is not a top-level statement,
 622	 * then report the end of that entry, too. */
 623	if (rule != NULL && rule->ptype != CSS_RULE_PARENT_STYLESHEET) {
 624		if (rule->type == CSS_RULE_SELECTOR)
 625			return handleEndRuleset(c, vector);
 626	}
 627
 628	return CSS_OK;
 629}
 630
 631css_error handleBlockContent(css_language *c, const parserutils_vector *vector)
 632{
 633	context_entry *entry;
 634	css_rule *rule;
 635
 636	/* In CSS 2.1, block content comprises either declarations (if the 
 637	 * current block is associated with @page or a selector), or rulesets 
 638	 * (if the current block is associated with @media). */
 639
 640	entry = parserutils_stack_get_current(c->context);
 641	if (entry == NULL || entry->data == NULL)
 642		return CSS_INVALID;
 643
 644	rule = entry->data;
 645	if (rule == NULL || (rule->type != CSS_RULE_SELECTOR && 
 646			rule->type != CSS_RULE_PAGE &&
 647			rule->type != CSS_RULE_MEDIA))
 648		return CSS_INVALID;
 649
 650	if (rule->type == CSS_RULE_MEDIA) {
 651		/* Expect rulesets */
 652		return handleStartRuleset(c, vector);
 653	} else {
 654		/* Expect declarations */
 655		return handleDeclaration(c, vector);
 656	}
 657
 658	return CSS_OK;
 659}
 660
 661css_error handleDeclaration(css_language *c, const parserutils_vector *vector)
 662{
 663	css_error error;
 664	const css_token *token, *ident;
 665	int ctx = 0;
 666	context_entry *entry;
 667	css_rule *rule;
 668
 669	/* Locations where declarations are permitted:
 670	 *
 671	 * + In @page
 672	 * + In ruleset
 673	 */
 674	entry = parserutils_stack_get_current(c->context);
 675	if (entry == NULL || entry->data == NULL)
 676		return CSS_INVALID;
 677
 678	rule = entry->data;
 679	if (rule == NULL || (rule->type != CSS_RULE_SELECTOR && 
 680			rule->type != CSS_RULE_PAGE))
 681		return CSS_INVALID;
 682
 683	/* Strip any leading whitespace (can happen if in nested block) */
 684	consumeWhitespace(vector, &ctx);
 685
 686	/* IDENT ws ':' ws value 
 687	 * 
 688	 * In CSS 2.1, value is any1, so '{' or ATKEYWORD => parse error
 689	 */
 690	ident = parserutils_vector_iterate(vector, &ctx);
 691	if (ident == NULL || ident->type != CSS_TOKEN_IDENT)
 692		return CSS_INVALID;
 693
 694	consumeWhitespace(vector, &ctx);
 695
 696	token = parserutils_vector_iterate(vector, &ctx);
 697	if (token == NULL || tokenIsChar(token, ':') == false)
 698		return CSS_INVALID;
 699
 700	consumeWhitespace(vector, &ctx);
 701
 702	error = parseProperty(c, ident, vector, &ctx, rule);
 703	if (error != CSS_OK)
 704		return error;
 705
 706	return CSS_OK;
 707}
 708
 709/******************************************************************************
 710 * At-rule parsing functions						      *
 711 ******************************************************************************/
 712css_error parseMediaList(css_language *c,
 713		const parserutils_vector *vector, int *ctx,
 714		uint64_t *media)
 715{
 716	uint64_t ret = 0;
 717	bool match = false;
 718	const css_token *token;
 719
 720	token = parserutils_vector_iterate(vector, ctx);
 721
 722	while (token != NULL) {
 723		if (token->type != CSS_TOKEN_IDENT)
 724			return CSS_INVALID;
 725
 726		if (lwc_string_caseless_isequal(token->idata, c->strings[AURAL], 
 727				&match) == lwc_error_ok && match) {
 728			ret |= CSS_MEDIA_AURAL;
 729		} else if (lwc_string_caseless_isequal(
 730				token->idata, c->strings[BRAILLE], 
 731				&match) == lwc_error_ok && match) {
 732			ret |= CSS_MEDIA_BRAILLE;
 733		} else if (lwc_string_caseless_isequal(
 734				token->idata, c->strings[EMBOSSED], 
 735				&match) == lwc_error_ok && match) {
 736			ret |= CSS_MEDIA_EMBOSSED;
 737		} else if (lwc_string_caseless_isequal(
 738				token->idata, c->strings[HANDHELD], 
 739				&match) == lwc_error_ok && match) {
 740			ret |= CSS_MEDIA_HANDHELD;
 741		} else if (lwc_string_caseless_isequal(
 742				token->idata, c->strings[PRINT], 
 743				&match) == lwc_error_ok && match) {
 744			ret |= CSS_MEDIA_PRINT;
 745		} else if (lwc_string_caseless_isequal(
 746				token->idata, c->strings[PROJECTION], 
 747				&match) == lwc_error_ok && match) {
 748			ret |= CSS_MEDIA_PROJECTION;
 749		} else if (lwc_string_caseless_isequal(
 750				token->idata, c->strings[SCREEN], 
 751				&match) == lwc_error_ok && match) {
 752			ret |= CSS_MEDIA_SCREEN;
 753		} else if (lwc_string_caseless_isequal(
 754				token->idata, c->strings[SPEECH], 
 755				&match) == lwc_error_ok && match) {
 756			ret |= CSS_MEDIA_SPEECH;
 757		} else if (lwc_string_caseless_isequal(
 758				token->idata, c->strings[TTY], 
 759				&match) == lwc_error_ok && match) {
 760			ret |= CSS_MEDIA_TTY;
 761		} else if (lwc_string_caseless_isequal(
 762				token->idata, c->strings[TV], 
 763				&match) == lwc_error_ok && match) {
 764			ret |= CSS_MEDIA_TV;
 765		} else if (lwc_string_caseless_isequal(
 766				token->idata, c->strings[ALL], 
 767				&match) == lwc_error_ok && match) {
 768			ret |= CSS_MEDIA_ALL;
 769		} else
 770			return CSS_INVALID;
 771
 772		consumeWhitespace(vector, ctx);
 773
 774		token = parserutils_vector_iterate(vector, ctx);
 775		if (token != NULL && tokenIsChar(token, ',') == false)
 776			return CSS_INVALID;
 777
 778		consumeWhitespace(vector, ctx);
 779	}
 780
 781	/* If, after parsing the media list, we still have no media, 
 782	 * then it must be ALL. */
 783	if (ret == 0)
 784		ret = CSS_MEDIA_ALL;
 785
 786	*media = ret;
 787
 788	return CSS_OK;
 789}
 790
 791/******************************************************************************
 792 * Selector list parsing functions					      *
 793 ******************************************************************************/
 794
 795css_error parseClass(css_language *c, const parserutils_vector *vector, 
 796		int *ctx, css_selector_detail *specific)
 797{
 798	const css_token *token;
 799
 800	/* class     -> '.' IDENT */
 801	token = parserutils_vector_iterate(vector, ctx);
 802	if (token == NULL || tokenIsChar(token, '.') == false)
 803		return CSS_INVALID;
 804
 805	token = parserutils_vector_iterate(vector, ctx);
 806	if (token == NULL || token->type != CSS_TOKEN_IDENT)
 807		return CSS_INVALID;
 808
 809	return css_stylesheet_selector_detail_init(c->sheet, 
 810			CSS_SELECTOR_CLASS, token->idata, NULL, specific);
 811}
 812
 813css_error parseAttrib(css_language *c, const parserutils_vector *vector, 
 814		int *ctx, css_selector_detail *specific)
 815{
 816	const css_token *token, *name, *value = NULL;
 817	css_selector_type type = CSS_SELECTOR_ATTRIBUTE;
 818
 819	/* attrib    -> '[' ws IDENT ws [
 820	 *		       [ '=' | INCLUDES | DASHMATCH ] ws
 821	 *		       [ IDENT | STRING ] ws ]? ']'
 822	 */
 823	token = parserutils_vector_iterate(vector, ctx);
 824	if (token == NULL || tokenIsChar(token, '[') == false)
 825		return CSS_INVALID;
 826
 827	consumeWhitespace(vector, ctx);
 828
 829	token = parserutils_vector_iterate(vector, ctx);
 830	if (token == NULL || token->type != CSS_TOKEN_IDENT)
 831		return CSS_INVALID;
 832
 833	name = token;
 834
 835	consumeWhitespace(vector, ctx);
 836
 837	token = parserutils_vector_iterate(vector, ctx);
 838	if (token == NULL)
 839		return CSS_INVALID;
 840
 841	if (tokenIsChar(token, ']') == false) {
 842		if (tokenIsChar(token, '='))
 843			type = CSS_SELECTOR_ATTRIBUTE_EQUAL;
 844		else if (token->type == CSS_TOKEN_INCLUDES)
 845			type = CSS_SELECTOR_ATTRIBUTE_INCLUDES;
 846		else if (token->type == CSS_TOKEN_DASHMATCH)
 847			type = CSS_SELECTOR_ATTRIBUTE_DASHMATCH;
 848		else
 849			return CSS_INVALID;
 850
 851		consumeWhitespace(vector, ctx);
 852
 853		token = parserutils_vector_iterate(vector, ctx);
 854		if (token == NULL || (token->type != CSS_TOKEN_IDENT &&
 855				token->type != CSS_TOKEN_STRING))
 856			return CSS_INVALID;
 857
 858		value = token;
 859
 860		consumeWhitespace(vector, ctx);
 861
 862		token = parserutils_vector_iterate(vector, ctx);
 863		if (token == NULL || tokenIsChar(token, ']') == false)
 864			return CSS_INVALID;
 865	}
 866
 867	return css_stylesheet_selector_detail_init(c->sheet, type, 
 868			name->idata, value != NULL ? value->idata : NULL,
 869			specific);
 870}
 871
 872css_error parsePseudo(css_language *c, const parserutils_vector *vector, 
 873		int *ctx, css_selector_detail *specific)
 874{
 875	const css_token *token, *name, *value = NULL;
 876	bool match = false;
 877	css_selector_type type;
 878
 879	/* pseudo    -> ':' [ IDENT | FUNCTION ws IDENT? ws ')' ] */
 880
 881	token = parserutils_vector_iterate(vector, ctx);
 882	if (token == NULL || tokenIsChar(token, ':') == false)
 883		return CSS_INVALID;
 884
 885	token = parserutils_vector_iterate(vector, ctx);
 886	if (token == NULL || (token->type != CSS_TOKEN_IDENT && 
 887			token->type != CSS_TOKEN_FUNCTION))
 888		return CSS_INVALID;
 889
 890	name = token;
 891
 892	if (token->type == CSS_TOKEN_FUNCTION) {
 893		consumeWhitespace(vector, ctx);
 894
 895		token = parserutils_vector_iterate(vector, ctx);
 896
 897		if (token != NULL && token->type == CSS_TOKEN_IDENT) {
 898			value = token;
 899
 900			consumeWhitespace(vector, ctx);
 901
 902			token = parserutils_vector_iterate(vector, ctx);
 903		}
 904
 905		if (token == NULL || tokenIsChar(token, ')') == false)
 906			return CSS_INVALID;
 907	}
 908
 909	if ((lwc_string_caseless_isequal(
 910				name->idata, c->strings[FIRST_CHILD], 
 911				&match) == lwc_error_ok && match) ||
 912			(lwc_string_caseless_isequal(
 913				name->idata, c->strings[LINK], 
 914				&match) == lwc_error_ok && match) ||
 915			(lwc_string_caseless_isequal(
 916				name->idata, c->strings[VISITED], 
 917				&match) == lwc_error_ok && match) ||
 918			(lwc_string_caseless_isequal(
 919				name->idata, c->strings[HOVER], 
 920				&match) == lwc_error_ok && match) ||
 921			(lwc_string_caseless_isequal(
 922				name->idata, c->strings[ACTIVE], 
 923				&match) == lwc_error_ok && match) ||
 924			(lwc_string_caseless_isequal(
 925				name->idata, c->strings[FOCUS], 
 926				&match) == lwc_error_ok && match) ||
 927			(lwc_string_caseless_isequal(
 928				name->idata, c->strings[LANG], 
 929				&match) == lwc_error_ok && match) ||
 930			(lwc_string_caseless_isequal(
 931				name->idata, c->strings[LEFT], 
 932				&match) == lwc_error_ok && match) ||
 933			(lwc_string_caseless_isequal(
 934				name->idata, c->strings[RIGHT], 
 935				&match) == lwc_error_ok && match) ||
 936			(lwc_string_caseless_isequal(
 937				name->idata, c->strings[FIRST], 
 938				&match) == lwc_error_ok && match))
 939		type = CSS_SELECTOR_PSEUDO_CLASS;
 940	else if ((lwc_string_caseless_isequal(
 941				name->idata, c->strings[FIRST_LINE], 
 942				&match) == lwc_error_ok && match) ||
 943			(lwc_string_caseless_isequal(
 944				name->idata, c->strings[FIRST_LETTER], 
 945				&match) == lwc_error_ok && match) ||
 946			(lwc_string_caseless_isequal(
 947				name->idata, c->strings[BEFORE], 
 948				&match) == lwc_error_ok && match) ||
 949			(lwc_string_caseless_isequal(
 950				name->idata, c->strings[AFTER], 
 951				&match) == lwc_error_ok && match))
 952		type = CSS_SELECTOR_PSEUDO_ELEMENT;
 953	else
 954		return CSS_INVALID;
 955
 956	return css_stylesheet_selector_detail_init(c->sheet, 
 957			type, name->idata, value != NULL ? value->idata : NULL,
 958			specific);
 959}
 960
 961css_error parseSpecific(css_language *c, 
 962		const parserutils_vector *vector, int *ctx,
 963		css_selector **parent)
 964{
 965	css_error error;
 966	const css_token *token;
 967	css_selector_detail specific;
 968
 969	/* specific  -> [ HASH | class | attrib | pseudo ] */
 970
 971	token = parserutils_vector_peek(vector, *ctx);
 972	if (token == NULL)
 973		return CSS_INVALID;
 974
 975	if (token->type == CSS_TOKEN_HASH) {
 976		error = css_stylesheet_selector_detail_init(c->sheet,
 977				CSS_SELECTOR_ID, token->idata, NULL, &specific);
 978		if (error != CSS_OK)
 979			return error;
 980
 981		parserutils_vector_iterate(vector, ctx);
 982	} else if (tokenIsChar(token, '.')) {
 983		error = parseClass(c, vector, ctx, &specific);
 984		if (error != CSS_OK)
 985			return error;
 986	} else if (tokenIsChar(token, '[')) {
 987		error = parseAttrib(c, vector, ctx, &specific);
 988		if (error != CSS_OK)
 989			return error;
 990	} else if (tokenIsChar(token, ':')) {
 991		error = parsePseudo(c, vector, ctx, &specific);
 992		if (error != CSS_OK)
 993			return error;
 994	} else {
 995		return CSS_INVALID;
 996	}
 997
 998	return css_stylesheet_selector_append_specific(c->sheet, parent, 
 999			&specific);
1000}
1001
1002css_error parseSelectorSpecifics(css_language *c,
1003		const parserutils_vector *vector, int *ctx,
1004		css_selector **parent)
1005{
1006	css_error error;
1007	const css_token *token;
1008
1009	/* specifics -> specific* */
1010	while ((token = parserutils_vector_peek(vector, *ctx)) != NULL &&
1011			token->type != CSS_TOKEN_S && 
1012			tokenIsChar(token, '+') == false &&
1013			tokenIsChar(token, '>') == false &&
1014			tokenIsChar(token, ',') == false) {
1015		error = parseSpecific(c, vector, ctx, parent);
1016		if (error != CSS_OK)
1017			return error;
1018	}
1019
1020	return CSS_OK;
1021}
1022
1023css_error parseSimpleSelector(css_language *c, 
1024		const parserutils_vector *vector, int *ctx, 
1025		css_selector **result)
1026{
1027	css_error error;
1028	const css_token *token;
1029	css_selector *selector;
1030
1031	/* simple_selector -> element_name specifics
1032	 *		   -> specific specifics
1033	 * element_name	   -> IDENT | '*'
1034	 */
1035
1036	token = parserutils_vector_peek(vector, *ctx);
1037	if (token == NULL)
1038		return CSS_INVALID;
1039
1040	if (token->type == CSS_TOKEN_IDENT || tokenIsChar(token, '*')) {
1041		/* Have element name */
1042		error = css_stylesheet_selector_create(c->sheet,
1043				token->idata, &selector);
1044		if (error != CSS_OK)
1045			return error;
1046
1047		parserutils_vector_iterate(vector, ctx);
1048	} else {
1049		/* Universal selector */
1050		error = css_stylesheet_selector_create(c->sheet,
1051				c->strings[UNIVERSAL], &selector);
1052		if (error != CSS_OK)
1053			return error;
1054
1055		/* Ensure we have at least one specific selector */
1056		error = parseSpecific(c, vector, ctx, &selector);
1057		if (error != CSS_OK) {
1058			css_stylesheet_selector_destroy(c->sheet, selector);
1059			return error;
1060		}
1061	}
1062
1063	error = parseSelectorSpecifics(c, vector, ctx, &selector);
1064	if (error != CSS_OK) {
1065		css_stylesheet_selector_destroy(c->sheet, selector);
1066		return error;
1067	}
1068
1069	*result = selector;
1070
1071	return CSS_OK;
1072}
1073
1074css_error parseCombinator(css_language *c, const parserutils_vector *vector,
1075		int *ctx, css_combinator *result)
1076{
1077	const css_token *token;
1078	css_combinator comb = CSS_COMBINATOR_NONE;
1079
1080	/* combinator	   -> ws '+' ws | ws '>' ws | ws1 */
1081
1082	UNUSED(c);
1083
1084	while ((token = parserutils_vector_peek(vector, *ctx)) != NULL) {
1085		if (tokenIsChar(token, '+'))
1086			comb = CSS_COMBINATOR_SIBLING;
1087		else if (tokenIsChar(token, '>'))
1088			comb = CSS_COMBINATOR_PARENT;
1089		else if (token->type == CSS_TOKEN_S)
1090			comb = CSS_COMBINATOR_ANCESTOR;
1091		else
1092			break;
1093
1094		parserutils_vector_iterate(vector, ctx);
1095
1096		/* If we've seen a '+' or '>', we're done. */
1097		if (comb != CSS_COMBINATOR_ANCESTOR)
1098			break;
1099	}
1100
1101	/* No valid combinator found */
1102	if (comb == CSS_COMBINATOR_NONE)
1103		return CSS_INVALID;
1104
1105	/* Consume any trailing whitespace */
1106	consumeWhitespace(vector, ctx);
1107
1108	*result = comb;
1109
1110	return CSS_OK;
1111}
1112
1113css_error parseSelector(css_language *c, const parserutils_vector *vector, 
1114		int *ctx, css_selector **result)
1115{
1116	css_error error;
1117	const css_token *token = NULL;
1118	css_selector *selector = NULL;
1119
1120	/* selector -> simple_selector [ combinator simple_selector ]* ws
1121	 * 
1122	 * Note, however, that, as combinator can be wholly whitespace,
1123	 * there's an ambiguity as to whether "ws" has been reached. We 
1124	 * resolve this by attempting to extract a combinator, then 
1125	 * recovering when we detect that we've reached the end of the
1126	 * selector.
1127	 */
1128
1129	error = parseSimpleSelector(c, vector, ctx, &selector);
1130	if (error != CSS_OK)
1131		return error;
1132	*result = selector;
1133
1134	while ((token = parserutils_vector_peek(vector, *ctx)) != NULL &&
1135			tokenIsChar(token, ',') == false) {
1136		css_combinator comb = CSS_COMBINATOR_NONE;
1137		css_selector *other = NULL;
1138
1139		error = parseCombinator(c, vector, ctx, &comb);
1140		if (error != CSS_OK)
1141			return error;
1142
1143		/* In the case of "html , body { ... }", the whitespace after
1144		 * "html" and "body" will be considered an ancestor combinator.
1145		 * This clearly is not the case, however. Therefore, as a 
1146		 * special case, if we've got an ancestor combinator and there 
1147		 * are no further tokens, or if the next token is a comma,
1148		 * we ignore the supposed combinator and continue. */
1149		if (comb == CSS_COMBINATOR_ANCESTOR && 
1150				((token = parserutils_vector_peek(vector, 
1151					*ctx)) == NULL || 
1152				tokenIsChar(token, ',')))
1153			continue;
1154
1155		error = parseSimpleSelector(c, vector, ctx, &other);
1156		if (error != CSS_OK)
1157			return error;
1158
1159		*result = other;
1160
1161		error = css_stylesheet_selector_combine(c->sheet,
1162				comb, selector, other);
1163		if (error != CSS_OK) {
1164			css_stylesheet_selector_destroy(c->sheet, selector);
1165			return error;
1166		}
1167
1168		selector = other;
1169	}
1170
1171	return CSS_OK;
1172}
1173
1174css_error parseSelectorList(css_language *c, const parserutils_vector *vector,
1175		css_rule *rule)
1176{
1177	css_error error;
1178	const css_token *token = NULL;
1179	css_selector *selector = NULL;
1180	int ctx = 0;
1181
1182	/* Strip any leading whitespace (can happen if in nested block) */
1183	consumeWhitespace(vector, &ctx);
1184
1185	/* selector_list   -> selector [ ',' ws selector ]* */
1186
1187	error = parseSelector(c, vector, &ctx, &selector);
1188	if (error != CSS_OK) {
1189		if (selector != NULL)
1190			css_stylesheet_selector_destroy(c->sheet, selector);
1191		return error;
1192	}
1193
1194	assert(selector != NULL);
1195
1196	error = css_stylesheet_rule_add_selector(c->sheet, rule, selector);
1197	if (error != CSS_OK) {
1198		css_stylesheet_selector_destroy(c->sheet, selector);
1199		return error;
1200	}
1201
1202	while (parserutils_vector_peek(vector, ctx) != NULL) {
1203		token = parserutils_vector_iterate(vector, &ctx);
1204		if (tokenIsChar(token, ',') == false)
1205			return CSS_INVALID;
1206
1207		consumeWhitespace(vector, &ctx);
1208
1209		selector = NULL;
1210
1211		error = parseSelector(c, vector, &ctx, &selector);
1212		if (error != CSS_OK) {
1213			if (selector != NULL) {
1214				css_stylesheet_selector_destroy(c->sheet, 
1215						selector);
1216			}
1217			return error;
1218		}
1219
1220		assert(selector != NULL);
1221
1222		error = css_stylesheet_rule_add_selector(c->sheet, rule, 
1223				selector);
1224		if (error != CSS_OK) {
1225			css_stylesheet_selector_destroy(c->sheet, selector);
1226			return error;
1227		}
1228	}
1229
1230	return CSS_OK;
1231}
1232
1233/******************************************************************************
1234 * Property parsing functions						      *
1235 ******************************************************************************/
1236
1237css_error parseProperty(css_language *c, const css_token *property, 
1238		const parserutils_vector *vector, int *ctx, css_rule *rule)
1239{
1240	css_error error;
1241	css_prop_handler handler = NULL;
1242	int i = 0;
1243	uint8_t flags = 0;
1244	css_style *style = NULL;
1245	const css_token *token;
1246
1247	/* Find property index */
1248	/** \todo improve on this linear search */
1249	for (i = FIRST_PROP; i <= LAST_PROP; i++) {
1250		bool match = false;
1251
1252		if (lwc_string_caseless_isequal(property->idata, c->strings[i],
1253				&match) == lwc_error_ok && match)
1254			break;
1255	}
1256	if (i == LAST_PROP + 1)
1257		return CSS_INVALID;
1258
1259	/* Get handler */
1260	handler = property_handlers[i - FIRST_PROP];
1261	assert(handler != NULL);
1262
1263	/* Call it */
1264	error = handler(c, vector, ctx, &style);
1265	if (error != CSS_OK)
1266		return error;
1267
1268	assert (style != NULL);
1269
1270	/* Determine if this declaration is important or not */
1271	error = parse_important(c, vector, ctx, &flags);
1272	if (error != CSS_OK) {
1273	  css_stylesheet_style_destroy(c->sheet, style, false);
1274		return error;
1275	}
1276
1277	/* Ensure that we've exhausted all the input */
1278	consumeWhitespace(vector, ctx);
1279	token = parserutils_vector_iterate(vector, ctx);
1280	if (token != NULL) {
1281		/* Trailing junk, so discard declaration */
1282                css_stylesheet_style_destroy(c->sheet, style, false);
1283		return CSS_INVALID;
1284	}
1285
1286	/* If it's important, then mark the style appropriately */
1287	if (flags != 0)
1288		make_style_important(style);
1289
1290	/* Append style to rule */
1291	error = css_stylesheet_rule_append_style(c->sheet, rule, style);
1292	if (error != CSS_OK) {
1293                css_stylesheet_style_destroy(c->sheet, style, false);
1294		return error;
1295	}
1296
1297	/* Style owned or destroyed by stylesheet, so forget about it */
1298
1299	return CSS_OK;
1300}
1301