/peek-build/src/netdepends/libcss/src/parse/language.c
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, ¶ms); 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