7d70b31d22ceaab9cdf06aaa3b38d7a080ac979b
[WebKit-https.git] / WebCore / css / CSSGrammar.y
1 %{
2
3 /*
4  *  Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org)
5  *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
6  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
7  *  Copyright (C) 2008 Eric Seidel <eric@webkit.org>
8  *
9  *  This library is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU Lesser General Public
11  *  License as published by the Free Software Foundation; either
12  *  version 2 of the License, or (at your option) any later version.
13  *
14  *  This library is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  Lesser General Public License for more details.
18  *
19  *  You should have received a copy of the GNU Lesser General Public
20  *  License along with this library; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24
25 #include "config.h"
26
27 #include "CSSMediaRule.h"
28 #include "CSSParser.h"
29 #include "CSSRuleList.h"
30 #include "CSSSelector.h"
31 #include "CSSStyleSheet.h"
32 #include "Document.h"
33 #include "HTMLNames.h"
34 #include "MediaList.h"
35 #include <stdlib.h>
36 #include <string.h>
37
38 using namespace WebCore;
39 using namespace HTMLNames;
40
41 #define YYENABLE_NLS 0
42 #define YYLTYPE_IS_TRIVIAL 1
43 #define YYMAXDEPTH 10000
44 #define YYDEBUG 0
45
46 // FIXME: Replace with %parse-param { CSSParser* parser } once we can depend on bison 2.x
47 #define YYPARSE_PARAM parser
48 #define YYLEX_PARAM parser
49
50 %}
51
52 %pure_parser
53
54 %union {
55     bool boolean;
56     char character;
57     int integer;
58     double number;
59     CSSParserString string;
60
61     CSSRule* rule;
62     CSSRuleList* ruleList;
63     CSSSelector* selector;
64     CSSSelector::Relation relation;
65     MediaList* mediaList;
66     MediaQuery* mediaQuery;
67     MediaQuery::Restrictor mediaQueryRestrictor;
68     MediaQueryExp* mediaQueryExp;
69     CSSParserValue value;
70     CSSParserValueList* valueList;
71     Vector<MediaQueryExp*>* mediaQueryExpList;
72 }
73
74 %{
75
76 static inline int cssyyerror(const char*)
77 {
78     return 1;
79 }
80
81 static int cssyylex(YYSTYPE* yylval, void* parser)
82 {
83     return static_cast<CSSParser*>(parser)->lex(yylval);
84 }
85
86 %}
87
88 %expect 46
89
90 %left UNIMPORTANT_TOK
91
92 %token WHITESPACE SGML_CD
93
94 %token INCLUDES
95 %token DASHMATCH
96 %token BEGINSWITH
97 %token ENDSWITH
98 %token CONTAINS
99
100 %token <string> STRING
101 %right <string> IDENT
102 %token <string> NTH
103
104 %nonassoc <string> HEX
105 %nonassoc <string> IDSEL
106 %nonassoc ':'
107 %nonassoc '.'
108 %nonassoc '['
109 %nonassoc <string> '*'
110 %nonassoc error
111 %left '|'
112
113 %token IMPORT_SYM
114 %token PAGE_SYM
115 %token MEDIA_SYM
116 %token FONT_FACE_SYM
117 %token CHARSET_SYM
118 %token NAMESPACE_SYM
119 %token WEBKIT_RULE_SYM
120 %token WEBKIT_DECLS_SYM
121 %token WEBKIT_VALUE_SYM
122 %token WEBKIT_MEDIAQUERY_SYM
123 %token WEBKIT_SELECTOR_SYM
124 %token WEBKIT_VARIABLES_SYM
125 %token WEBKIT_DEFINE_SYM
126 %token VARIABLES_FOR
127 %token WEBKIT_VARIABLES_DECLS_SYM
128 %token ATKEYWORD
129
130 %token IMPORTANT_SYM
131 %token MEDIA_ONLY
132 %token MEDIA_NOT
133 %token MEDIA_AND
134
135 %token <number> QEMS
136 %token <number> EMS
137 %token <number> EXS
138 %token <number> PXS
139 %token <number> CMS
140 %token <number> MMS
141 %token <number> INS
142 %token <number> PTS
143 %token <number> PCS
144 %token <number> DEGS
145 %token <number> RADS
146 %token <number> GRADS
147 %token <number> MSECS
148 %token <number> SECS
149 %token <number> HERZ
150 %token <number> KHERZ
151 %token <string> DIMEN
152 %token <number> PERCENTAGE
153 %token <number> FLOATTOKEN
154 %token <number> INTEGER
155
156 %token <string> URI
157 %token <string> FUNCTION
158 %token <string> NOTFUNCTION
159
160 %token <string> UNICODERANGE
161
162 %token <string> VARCALL
163
164 %type <relation> combinator
165
166 %type <rule> charset
167 %type <rule> ruleset
168 %type <rule> valid_rule_or_import
169 %type <rule> media
170 %type <rule> import
171 %type <rule> page
172 %type <rule> font_face
173 %type <rule> invalid_rule
174 %type <rule> invalid_at
175 %type <rule> invalid_import
176 %type <rule> rule
177 %type <rule> valid_rule
178 %type <rule> variables_rule
179 %type <mediaList> variables_media_list
180
181 %type <string> maybe_ns_prefix
182
183 %type <string> namespace_selector
184
185 %type <string> string_or_uri
186 %type <string> ident_or_string
187 %type <string> medium
188 %type <string> hexcolor
189
190 %type <string> media_feature
191 %type <mediaList> media_list
192 %type <mediaList> maybe_media_list
193 %type <mediaQuery> media_query
194 %type <mediaQueryRestrictor> maybe_media_restrictor
195 %type <valueList> maybe_media_value
196 %type <mediaQueryExp> media_query_exp
197 %type <mediaQueryExpList> media_query_exp_list
198 %type <mediaQueryExpList> maybe_and_media_query_exp_list
199
200 %type <ruleList> ruleset_list
201
202 %type <integer> property
203
204 %type <selector> specifier
205 %type <selector> specifier_list
206 %type <selector> simple_selector
207 %type <selector> selector
208 %type <selector> selector_list
209 %type <selector> selector_with_trailing_whitespace
210 %type <selector> class
211 %type <selector> attrib
212 %type <selector> pseudo
213
214 %type <boolean> declaration_list
215 %type <boolean> decl_list
216 %type <boolean> declaration
217
218 %type <boolean> prio
219
220 %type <integer> match
221 %type <integer> unary_operator
222 %type <character> operator
223
224 %type <valueList> expr
225 %type <value> term
226 %type <value> unary_term
227 %type <value> function
228
229 %type <string> element_name
230 %type <string> attr_name
231
232 %type <string> variable_name
233 %type <boolean> variables_declaration_list
234 %type <boolean> variables_decl_list
235 %type <boolean> variables_declaration
236
237 %%
238
239 stylesheet:
240     maybe_charset maybe_sgml import_list variables_list namespace_list rule_list
241   | webkit_rule maybe_space
242   | webkit_decls maybe_space
243   | webkit_value maybe_space
244   | webkit_mediaquery maybe_space
245   | webkit_selector maybe_space
246   | webkit_variables_decls maybe_space
247   ;
248
249 valid_rule_or_import:
250     valid_rule
251   | import
252   ;
253
254 webkit_rule:
255     WEBKIT_RULE_SYM '{' maybe_space valid_rule_or_import maybe_space '}' {
256         static_cast<CSSParser*>(parser)->m_rule = $4;
257     }
258 ;
259
260 webkit_decls:
261     WEBKIT_DECLS_SYM '{' maybe_space declaration_list '}' {
262         /* can be empty */
263     }
264 ;
265
266 webkit_variables_decls:
267     WEBKIT_VARIABLES_DECLS_SYM '{' maybe_space variables_declaration_list '}' {
268         /* can be empty */
269     }
270 ;
271
272 webkit_value:
273     WEBKIT_VALUE_SYM '{' maybe_space expr '}' {
274         CSSParser* p = static_cast<CSSParser*>(parser);
275         if ($4) {
276             p->m_valueList = p->sinkFloatingValueList($4);
277             int oldParsedProperties = p->m_numParsedProperties;
278             if (!p->parseValue(p->m_id, p->m_important))
279                 p->rollbackLastProperties(p->m_numParsedProperties - oldParsedProperties);
280             delete p->m_valueList;
281             p->m_valueList = 0;
282         }
283     }
284 ;
285
286 webkit_mediaquery:
287      WEBKIT_MEDIAQUERY_SYM WHITESPACE maybe_space media_query '}' {
288          CSSParser* p = static_cast<CSSParser*>(parser);
289          p->m_mediaQuery = p->sinkFloatingMediaQuery($4);
290      }
291 ;
292
293 webkit_selector:
294     WEBKIT_SELECTOR_SYM '{' maybe_space selector_list '}' {
295         CSSParser* p = static_cast<CSSParser*>(parser);
296         p->m_floatingSelector = p->sinkFloatingSelector($4);
297     }
298 ;
299
300 maybe_space:
301     /* empty */ %prec UNIMPORTANT_TOK
302   | maybe_space WHITESPACE
303   ;
304
305 maybe_sgml:
306     /* empty */
307   | maybe_sgml SGML_CD
308   | maybe_sgml WHITESPACE
309   ;
310
311 maybe_charset:
312    /* empty */
313   | charset {
314   }
315 ;
316
317 charset:
318   CHARSET_SYM maybe_space STRING maybe_space ';' {
319      CSSParser* p = static_cast<CSSParser*>(parser);
320      $$ = static_cast<CSSParser*>(parser)->createCharsetRule($3);
321      if ($$ && p->m_styleSheet)
322          p->m_styleSheet->append($$);
323   }
324   | CHARSET_SYM error invalid_block {
325   }
326   | CHARSET_SYM error ';' {
327   }
328 ;
329
330 import_list:
331  /* empty */
332  | import_list import maybe_sgml {
333      CSSParser* p = static_cast<CSSParser*>(parser);
334      if ($2 && p->m_styleSheet)
335          p->m_styleSheet->append($2);
336  }
337  ;
338
339 variables_list:
340 /* empty */
341 | variables_list variables_rule maybe_sgml {
342     CSSParser* p = static_cast<CSSParser*>(parser);
343      if ($2 && p->m_styleSheet)
344          p->m_styleSheet->append($2);
345 }
346 ;
347
348 namespace_list:
349 /* empty */
350 | namespace_list namespace maybe_sgml
351 ;
352
353 rule_list:
354    /* empty */
355  | rule_list rule maybe_sgml {
356      CSSParser* p = static_cast<CSSParser*>(parser);
357      if ($2 && p->m_styleSheet)
358          p->m_styleSheet->append($2);
359  }
360  ;
361
362 valid_rule:
363     ruleset
364   | media
365   | page
366   | font_face
367   ;
368
369 rule:
370     valid_rule
371   | invalid_rule
372   | invalid_at
373   | invalid_import
374   ;
375
376 import:
377     IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list ';' {
378         $$ = static_cast<CSSParser*>(parser)->createImportRule($3, $5);
379     }
380   | IMPORT_SYM error invalid_block {
381         $$ = 0;
382     }
383   | IMPORT_SYM error ';' {
384         $$ = 0;
385     }
386   ;
387
388 variables_rule:
389     WEBKIT_VARIABLES_SYM maybe_space maybe_media_list '{' maybe_space variables_declaration_list '}' {
390         $$ = static_cast<CSSParser*>(parser)->createVariablesRule($3, true);
391     }
392     |
393     WEBKIT_DEFINE_SYM maybe_space variables_media_list '{' maybe_space variables_declaration_list '}' {
394         $$ = static_cast<CSSParser*>(parser)->createVariablesRule($3, false);
395     }
396     ;
397
398 variables_media_list:
399     /* empty */ {
400         $$ = static_cast<CSSParser*>(parser)->createMediaList();
401     }
402     |
403     VARIABLES_FOR WHITESPACE media_list {
404         $$ = $3;
405     }
406     ;
407
408 variables_declaration_list:
409     variables_declaration {
410         $$ = $1;
411     }
412     | variables_decl_list variables_declaration {
413         $$ = $1;
414         if ($2)
415             $$ = $2;
416     }
417     | variables_decl_list {
418         $$ = $1;
419     }
420     | error invalid_block_list error {
421         $$ = false;
422     }
423     | error {
424         $$ = false;
425     }
426     | variables_decl_list error {
427         $$ = $1;
428     }
429     ;
430
431 variables_decl_list:
432     variables_declaration ';' maybe_space {
433         $$ = $1;
434     }
435     | variables_declaration invalid_block_list ';' maybe_space {
436         $$ = false;
437     }
438     | error ';' maybe_space {
439         $$ = false;
440     }
441     | error invalid_block_list error ';' maybe_space {
442         $$ = false;
443     }
444     | variables_decl_list variables_declaration ';' maybe_space {
445         $$ = $1;
446         if ($2)
447             $$ = $2;
448     }
449     | variables_decl_list error ';' maybe_space {
450         $$ = $1;
451     }
452     | variables_decl_list error invalid_block_list error ';' maybe_space {
453         $$ = $1;
454     }
455     ;
456
457 variables_declaration:
458     variable_name ':' maybe_space expr {
459         $$ = static_cast<CSSParser*>(parser)->addVariable($1, $4);
460     }
461     |
462     variable_name error {
463         $$ = false;
464     }
465     |
466     variable_name ':' maybe_space error expr {
467         $$ = false;
468     }
469     |
470     variable_name ':' maybe_space {
471         /* @variables { varname: } Just reduce away this variable with no value. */
472         $$ = false;
473     }
474     |
475     variable_name ':' maybe_space error {
476         /* if we come across rules with invalid values like this case: @variables { varname: *; }, just discard the property/value pair */
477         $$ = false;
478     }
479     ;
480
481 variable_name:
482     IDENT maybe_space {
483         $$ = $1;
484     }
485     ;
486
487 namespace:
488 NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' {
489     CSSParser* p = static_cast<CSSParser*>(parser);
490     if (p->m_styleSheet)
491         p->m_styleSheet->addNamespace(p, $3, $4);
492 }
493 | NAMESPACE_SYM error invalid_block
494 | NAMESPACE_SYM error ';'
495 ;
496
497 maybe_ns_prefix:
498 /* empty */ { $$.characters = 0; }
499 | IDENT WHITESPACE { $$ = $1; }
500 ;
501
502 string_or_uri:
503 STRING
504 | URI
505 ;
506
507 media_feature:
508     IDENT maybe_space {
509         $$ = $1;
510     }
511     ;
512
513 maybe_media_value:
514     /*empty*/ {
515         $$ = 0;
516     }
517     | ':' maybe_space expr maybe_space {
518         $$ = $3;
519     }
520     ;
521
522 media_query_exp:
523     '(' maybe_space media_feature maybe_space maybe_media_value ')' maybe_space {
524         $3.lower();
525         $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExp($3, $5);
526     }
527     ;
528
529 media_query_exp_list:
530     media_query_exp {
531         CSSParser* p = static_cast<CSSParser*>(parser);
532         $$ = p->createFloatingMediaQueryExpList();
533         $$->append(p->sinkFloatingMediaQueryExp($1));
534     }
535     | media_query_exp_list maybe_space MEDIA_AND maybe_space media_query_exp {
536         $$ = $1;
537         $$->append(static_cast<CSSParser*>(parser)->sinkFloatingMediaQueryExp($5));
538     }
539     ;
540
541 maybe_and_media_query_exp_list:
542     /*empty*/ {
543         $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExpList();
544     }
545     | MEDIA_AND maybe_space media_query_exp_list {
546         $$ = $3;
547     }
548     ;
549
550 maybe_media_restrictor:
551     /*empty*/ {
552         $$ = MediaQuery::None;
553     }
554     | MEDIA_ONLY {
555         $$ = MediaQuery::Only;
556     }
557     | MEDIA_NOT {
558         $$ = MediaQuery::Not;
559     }
560     ;
561
562 media_query:
563     media_query_exp_list {
564         CSSParser* p = static_cast<CSSParser*>(parser);
565         $$ = p->createFloatingMediaQuery(p->sinkFloatingMediaQueryExpList($1));
566     }
567     |
568     maybe_media_restrictor maybe_space medium maybe_and_media_query_exp_list {
569         CSSParser* p = static_cast<CSSParser*>(parser);
570         $3.lower();
571         $$ = p->createFloatingMediaQuery($1, $3, p->sinkFloatingMediaQueryExpList($4));
572     }
573     ;
574
575 maybe_media_list:
576      /* empty */ {
577         $$ = static_cast<CSSParser*>(parser)->createMediaList();
578      }
579      | media_list
580      ;
581
582 media_list:
583     media_query {
584         CSSParser* p = static_cast<CSSParser*>(parser);
585         $$ = p->createMediaList();
586         $$->appendMediaQuery(p->sinkFloatingMediaQuery($1));
587     }
588     | media_list ',' maybe_space media_query {
589         $$ = $1;
590         if ($$)
591             $$->appendMediaQuery(static_cast<CSSParser*>(parser)->sinkFloatingMediaQuery($4));
592     }
593     | media_list error {
594         $$ = 0;
595     }
596     ;
597
598 media:
599     MEDIA_SYM maybe_space media_list '{' maybe_space ruleset_list '}' {
600         $$ = static_cast<CSSParser*>(parser)->createMediaRule($3, $6);
601     }
602     | MEDIA_SYM maybe_space '{' maybe_space ruleset_list '}' {
603         $$ = static_cast<CSSParser*>(parser)->createMediaRule(0, $5);
604     }
605     ;
606
607 ruleset_list:
608     /* empty */ { $$ = 0; }
609     | ruleset_list ruleset maybe_space {
610         $$ = $1;
611         if ($2) {
612             if (!$$)
613                 $$ = static_cast<CSSParser*>(parser)->createRuleList();
614             $$->append($2);
615         }
616     }
617     ;
618
619 medium:
620   IDENT maybe_space {
621       $$ = $1;
622   }
623   ;
624
625 /*
626 page:
627     PAGE_SYM maybe_space IDENT? pseudo_page? maybe_space
628     '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
629   ;
630
631 pseudo_page
632   : ':' IDENT
633   ;
634 */
635
636 page:
637     PAGE_SYM error invalid_block {
638       $$ = 0;
639     }
640   | PAGE_SYM error ';' {
641       $$ = 0;
642     }
643     ;
644
645 font_face:
646     FONT_FACE_SYM maybe_space
647     '{' maybe_space declaration_list '}'  maybe_space {
648         $$ = static_cast<CSSParser*>(parser)->createFontFaceRule();
649     }
650     | FONT_FACE_SYM error invalid_block {
651       $$ = 0;
652     }
653     | FONT_FACE_SYM error ';' {
654       $$ = 0;
655     }
656 ;
657
658 combinator:
659     '+' maybe_space { $$ = CSSSelector::DirectAdjacent; }
660   | '~' maybe_space { $$ = CSSSelector::IndirectAdjacent; }
661   | '>' maybe_space { $$ = CSSSelector::Child; }
662   ;
663
664 unary_operator:
665     '-' { $$ = -1; }
666   | '+' { $$ = 1; }
667   ;
668
669 ruleset:
670     selector_list '{' maybe_space declaration_list '}' {
671         $$ = static_cast<CSSParser*>(parser)->createStyleRule($1);
672     }
673   ;
674
675 selector_list:
676     selector %prec UNIMPORTANT_TOK {
677         $$ = $1;
678     }
679     | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
680         if ($1 && $4) {
681             CSSParser* p = static_cast<CSSParser*>(parser);
682             $$ = $1;
683             $$->append(p->sinkFloatingSelector($4));
684         } else
685             $$ = 0;
686     }
687   | selector_list error {
688         $$ = 0;
689     }
690    ;
691
692 selector_with_trailing_whitespace:
693     selector WHITESPACE {
694         $$ = $1;
695     }
696     ;
697
698 selector:
699     simple_selector {
700         $$ = $1;
701     }
702     | selector_with_trailing_whitespace
703     {
704         $$ = $1;
705     }
706     | selector_with_trailing_whitespace simple_selector
707     {
708         $$ = $2;
709         if (!$1)
710             $$ = 0;
711         else if ($$) {
712             CSSParser* p = static_cast<CSSParser*>(parser);
713             CSSSelector* end = $$;
714             while (end->m_tagHistory)
715                 end = end->m_tagHistory;
716             end->m_relation = CSSSelector::Descendant;
717             end->m_tagHistory = p->sinkFloatingSelector($1);
718             if (Document* doc = p->document())
719                 doc->setUsesDescendantRules(true);
720         }
721     }
722     | selector combinator simple_selector {
723         $$ = $3;
724         if (!$1)
725             $$ = 0;
726         else if ($$) {
727             CSSParser* p = static_cast<CSSParser*>(parser);
728             CSSSelector* end = $$;
729             while (end->m_tagHistory)
730                 end = end->m_tagHistory;
731             end->m_relation = $2;
732             end->m_tagHistory = p->sinkFloatingSelector($1);
733             if ($2 == CSSSelector::Child) {
734                 if (Document* doc = p->document())
735                     doc->setUsesDescendantRules(true);
736             } else if ($2 == CSSSelector::DirectAdjacent || $2 == CSSSelector::IndirectAdjacent) {
737                 if (Document* doc = p->document())
738                     doc->setUsesSiblingRules(true);
739             }
740         }
741     }
742     | selector error {
743         $$ = 0;
744     }
745     ;
746
747 namespace_selector:
748     /* empty */ '|' { $$.characters = 0; $$.length = 0; }
749     | '*' '|' { static UChar star = '*'; $$.characters = &star; $$.length = 1; }
750     | IDENT '|' { $$ = $1; }
751 ;
752     
753 simple_selector:
754     element_name {
755         CSSParser* p = static_cast<CSSParser*>(parser);
756         $$ = p->createFloatingSelector();
757         $$->m_tag = QualifiedName(nullAtom, $1, p->m_defaultNamespace);
758     }
759     | element_name specifier_list {
760         $$ = $2;
761         if ($$) {
762             CSSParser* p = static_cast<CSSParser*>(parser);
763             $$->m_tag = QualifiedName(nullAtom, $1, p->m_defaultNamespace);
764         }
765     }
766     | specifier_list {
767         $$ = $1;
768         CSSParser* p = static_cast<CSSParser*>(parser);
769         if ($$ && p->m_defaultNamespace != starAtom)
770             $$->m_tag = QualifiedName(nullAtom, starAtom, p->m_defaultNamespace);
771     }
772     | namespace_selector element_name {
773         AtomicString namespacePrefix = $1;
774         CSSParser* p = static_cast<CSSParser*>(parser);
775         $$ = p->createFloatingSelector();
776         if (p->m_styleSheet)
777             $$->m_tag = QualifiedName(namespacePrefix, $2,
778                                       p->m_styleSheet->determineNamespace(namespacePrefix));
779         else // FIXME: Shouldn't this case be an error?
780             $$->m_tag = QualifiedName(nullAtom, $2, p->m_defaultNamespace);
781     }
782     | namespace_selector element_name specifier_list {
783         $$ = $3;
784         if ($$) {
785             AtomicString namespacePrefix = $1;
786             CSSParser* p = static_cast<CSSParser*>(parser);
787             if (p->m_styleSheet)
788                 $$->m_tag = QualifiedName(namespacePrefix, $2,
789                                           p->m_styleSheet->determineNamespace(namespacePrefix));
790             else // FIXME: Shouldn't this case be an error?
791                 $$->m_tag = QualifiedName(nullAtom, $2, p->m_defaultNamespace);
792         }
793     }
794     | namespace_selector specifier_list {
795         $$ = $2;
796         if ($$) {
797             AtomicString namespacePrefix = $1;
798             CSSParser* p = static_cast<CSSParser*>(parser);
799             if (p->m_styleSheet)
800                 $$->m_tag = QualifiedName(namespacePrefix, starAtom,
801                                           p->m_styleSheet->determineNamespace(namespacePrefix));
802         }
803     }
804   ;
805
806 element_name:
807     IDENT {
808         CSSParserString& str = $1;
809         CSSParser* p = static_cast<CSSParser*>(parser);
810         Document* doc = p->document();
811         if (doc && doc->isHTMLDocument())
812             str.lower();
813         $$ = str;
814     }
815     | '*' {
816         static UChar star = '*';
817         $$.characters = &star;
818         $$.length = 1;
819     }
820   ;
821
822 specifier_list:
823     specifier {
824         $$ = $1;
825     }
826     | specifier_list specifier {
827         if (!$2)
828             $$ = 0;
829         else if ($1) {
830             $$ = $1;
831             CSSParser* p = static_cast<CSSParser*>(parser);
832             CSSSelector* end = $1;
833             while (end->m_tagHistory)
834                 end = end->m_tagHistory;
835             end->m_relation = CSSSelector::SubSelector;
836             end->m_tagHistory = p->sinkFloatingSelector($2);
837         }
838     }
839     | specifier_list error {
840         $$ = 0;
841     }
842 ;
843
844 specifier:
845     IDSEL {
846         CSSParser* p = static_cast<CSSParser*>(parser);
847         $$ = p->createFloatingSelector();
848         $$->m_match = CSSSelector::Id;
849         if (!p->m_strict)
850             $1.lower();
851         $$->m_attr = idAttr;
852         $$->m_value = $1;
853     }
854   | HEX {
855         if ($1.characters[0] >= '0' && $1.characters[0] <= '9') {
856             $$ = 0;
857         } else {
858             CSSParser* p = static_cast<CSSParser*>(parser);
859             $$ = p->createFloatingSelector();
860             $$->m_match = CSSSelector::Id;
861             if (!p->m_strict)
862                 $1.lower();
863             $$->m_attr = idAttr;
864             $$->m_value = $1;
865         }
866     }
867   | class
868   | attrib
869   | pseudo
870     ;
871
872 class:
873     '.' IDENT {
874         CSSParser* p = static_cast<CSSParser*>(parser);
875         $$ = p->createFloatingSelector();
876         $$->m_match = CSSSelector::Class;
877         if (!p->m_strict)
878             $2.lower();
879         $$->m_attr = classAttr;
880         $$->m_value = $2;
881     }
882   ;
883
884 attr_name:
885     IDENT maybe_space {
886         CSSParserString& str = $1;
887         CSSParser* p = static_cast<CSSParser*>(parser);
888         Document* doc = p->document();
889         if (doc && doc->isHTMLDocument())
890             str.lower();
891         $$ = str;
892     }
893     ;
894
895 attrib:
896     '[' maybe_space attr_name ']' {
897         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
898         $$->m_attr = QualifiedName(nullAtom, $3, nullAtom);
899         $$->m_match = CSSSelector::Set;
900     }
901     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
902         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
903         $$->m_attr = QualifiedName(nullAtom, $3, nullAtom);
904         $$->m_match = (CSSSelector::Match)$4;
905         $$->m_value = $6;
906     }
907     | '[' maybe_space namespace_selector attr_name ']' {
908         AtomicString namespacePrefix = $3;
909         CSSParser* p = static_cast<CSSParser*>(parser);
910         $$ = p->createFloatingSelector();
911         $$->m_attr = QualifiedName(namespacePrefix, $4,
912                                    p->m_styleSheet->determineNamespace(namespacePrefix));
913         $$->m_match = CSSSelector::Set;
914     }
915     | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
916         AtomicString namespacePrefix = $3;
917         CSSParser* p = static_cast<CSSParser*>(parser);
918         $$ = p->createFloatingSelector();
919         $$->m_attr = QualifiedName(namespacePrefix, $4,
920                                    p->m_styleSheet->determineNamespace(namespacePrefix));
921         $$->m_match = (CSSSelector::Match)$5;
922         $$->m_value = $7;
923     }
924   ;
925
926 match:
927     '=' {
928         $$ = CSSSelector::Exact;
929     }
930     | INCLUDES {
931         $$ = CSSSelector::List;
932     }
933     | DASHMATCH {
934         $$ = CSSSelector::Hyphen;
935     }
936     | BEGINSWITH {
937         $$ = CSSSelector::Begin;
938     }
939     | ENDSWITH {
940         $$ = CSSSelector::End;
941     }
942     | CONTAINS {
943         $$ = CSSSelector::Contain;
944     }
945     ;
946
947 ident_or_string:
948     IDENT
949   | STRING
950     ;
951
952 pseudo:
953     ':' IDENT {
954         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
955         $$->m_match = CSSSelector::PseudoClass;
956         $2.lower();
957         $$->m_value = $2;
958         CSSSelector::PseudoType type = $$->pseudoType();
959         if (type == CSSSelector::PseudoUnknown)
960             $$ = 0;
961         else if (type == CSSSelector::PseudoEmpty ||
962                  type == CSSSelector::PseudoFirstChild ||
963                  type == CSSSelector::PseudoFirstOfType ||
964                  type == CSSSelector::PseudoLastChild ||
965                  type == CSSSelector::PseudoLastOfType ||
966                  type == CSSSelector::PseudoOnlyChild ||
967                  type == CSSSelector::PseudoOnlyOfType) {
968             CSSParser* p = static_cast<CSSParser*>(parser);
969             Document* doc = p->document();
970             if (doc)
971                 doc->setUsesSiblingRules(true);
972         } else if (type == CSSSelector::PseudoFirstLine) {
973             CSSParser* p = static_cast<CSSParser*>(parser);
974             if (Document* doc = p->document())
975                 doc->setUsesFirstLineRules(true);
976         }
977     }
978     | ':' ':' IDENT {
979         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
980         $$->m_match = CSSSelector::PseudoElement;
981         $3.lower();
982         $$->m_value = $3;
983         CSSSelector::PseudoType type = $$->pseudoType();
984         if (type == CSSSelector::PseudoUnknown)
985             $$ = 0;
986         else if (type == CSSSelector::PseudoFirstLine) {
987             CSSParser* p = static_cast<CSSParser*>(parser);
988             if (Document* doc = p->document())
989                 doc->setUsesFirstLineRules(true);
990         }
991     }
992     // used by :nth-*(ax+b)
993     | ':' FUNCTION NTH ')' {
994         CSSParser *p = static_cast<CSSParser*>(parser);
995         $$ = p->createFloatingSelector();
996         $$->m_match = CSSSelector::PseudoClass;
997         $$->m_argument = $3;
998         $$->m_value = $2;
999         CSSSelector::PseudoType type = $$->pseudoType();
1000         if (type == CSSSelector::PseudoUnknown)
1001             $$ = 0;
1002         else if (type == CSSSelector::PseudoNthChild ||
1003                  type == CSSSelector::PseudoNthOfType ||
1004                  type == CSSSelector::PseudoNthLastChild ||
1005                  type == CSSSelector::PseudoNthLastOfType) {
1006             if (p->document())
1007                 p->document()->setUsesSiblingRules(true);
1008         }
1009     }
1010     // used by :nth-*
1011     | ':' FUNCTION INTEGER ')' {
1012         CSSParser *p = static_cast<CSSParser*>(parser);
1013         $$ = p->createFloatingSelector();
1014         $$->m_match = CSSSelector::PseudoClass;
1015         $$->m_argument = String::number($3);
1016         $$->m_value = $2;
1017         CSSSelector::PseudoType type = $$->pseudoType();
1018         if (type == CSSSelector::PseudoUnknown)
1019             $$ = 0;
1020         else if (type == CSSSelector::PseudoNthChild ||
1021                  type == CSSSelector::PseudoNthOfType ||
1022                  type == CSSSelector::PseudoNthLastChild ||
1023                  type == CSSSelector::PseudoNthLastOfType) {
1024             if (p->document())
1025                 p->document()->setUsesSiblingRules(true);
1026         }
1027     }
1028     // used by :nth-*(odd/even) and :lang
1029     | ':' FUNCTION IDENT ')' {
1030         CSSParser *p = static_cast<CSSParser*>(parser);
1031         $$ = p->createFloatingSelector();
1032         $$->m_match = CSSSelector::PseudoClass;
1033         $$->m_argument = $3;
1034         $2.lower();
1035         $$->m_value = $2;
1036         CSSSelector::PseudoType type = $$->pseudoType();
1037         if (type == CSSSelector::PseudoUnknown)
1038             $$ = 0;
1039         else if (type == CSSSelector::PseudoNthChild ||
1040                  type == CSSSelector::PseudoNthOfType ||
1041                  type == CSSSelector::PseudoNthLastChild ||
1042                  type == CSSSelector::PseudoNthLastOfType) {
1043             if (p->document())
1044                 p->document()->setUsesSiblingRules(true);
1045         }
1046     }
1047     // used by :not
1048     | ':' NOTFUNCTION maybe_space simple_selector maybe_space ')' {
1049         if (!$4)
1050             $$ = 0;
1051         else {
1052             CSSParser* p = static_cast<CSSParser*>(parser);
1053             $$ = p->createFloatingSelector();
1054             $$->m_match = CSSSelector::PseudoClass;
1055             $$->m_simpleSelector = p->sinkFloatingSelector($4);
1056             $2.lower();
1057             $$->m_value = $2;
1058         }
1059     }
1060   ;
1061
1062 declaration_list:
1063     declaration {
1064         $$ = $1;
1065     }
1066     | decl_list declaration {
1067         $$ = $1;
1068         if ( $2 )
1069             $$ = $2;
1070     }
1071     | decl_list {
1072         $$ = $1;
1073     }
1074     | error invalid_block_list error {
1075         $$ = false;
1076     }
1077     | error {
1078         $$ = false;
1079     }
1080     | decl_list error {
1081         $$ = $1;
1082     }
1083     ;
1084
1085 decl_list:
1086     declaration ';' maybe_space {
1087         $$ = $1;
1088     }
1089     | declaration invalid_block_list ';' maybe_space {
1090         $$ = false;
1091     }
1092     | error ';' maybe_space {
1093         $$ = false;
1094     }
1095     | error invalid_block_list error ';' maybe_space {
1096         $$ = false;
1097     }
1098     | decl_list declaration ';' maybe_space {
1099         $$ = $1;
1100         if ($2)
1101             $$ = $2;
1102     }
1103     | decl_list error ';' maybe_space {
1104         $$ = $1;
1105     }
1106     | decl_list error invalid_block_list error ';' maybe_space {
1107         $$ = $1;
1108     }
1109     ;
1110
1111 declaration:
1112     property ':' maybe_space expr prio {
1113         $$ = false;
1114         CSSParser* p = static_cast<CSSParser*>(parser);
1115         if ($1 && $4) {
1116             p->m_valueList = p->sinkFloatingValueList($4);
1117             int oldParsedProperties = p->m_numParsedProperties;
1118             $$ = p->parseValue($1, $5);
1119             if (!$$)
1120                 p->rollbackLastProperties(p->m_numParsedProperties - oldParsedProperties);
1121             delete p->m_valueList;
1122             p->m_valueList = 0;
1123         }
1124     }
1125     |
1126     property error {
1127         $$ = false;
1128     }
1129     |
1130     property ':' maybe_space error expr prio {
1131         /* The default movable type template has letter-spacing: .none;  Handle this by looking for
1132         error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
1133         up and deleting the shifted expr.  */
1134         $$ = false;
1135     }
1136     |
1137     IMPORTANT_SYM maybe_space {
1138         /* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
1139         $$ = false;
1140     }
1141     |
1142     property ':' maybe_space {
1143         /* div { font-family: } Just reduce away this property with no value. */
1144         $$ = false;
1145     }
1146     |
1147     property ':' maybe_space error {
1148         /* if we come across rules with invalid values like this case: p { weight: *; }, just discard the rule */
1149         $$ = false;
1150     }
1151   ;
1152
1153 property:
1154     IDENT maybe_space {
1155         $$ = cssPropertyID($1);
1156     }
1157   ;
1158
1159 prio:
1160     IMPORTANT_SYM maybe_space { $$ = true; }
1161     | /* empty */ { $$ = false; }
1162   ;
1163
1164 expr:
1165     term {
1166         CSSParser* p = static_cast<CSSParser*>(parser);
1167         $$ = p->createFloatingValueList();
1168         $$->addValue(p->sinkFloatingValue($1));
1169     }
1170     | expr operator term {
1171         CSSParser* p = static_cast<CSSParser*>(parser);
1172         $$ = $1;
1173         if ($$) {
1174             if ($2) {
1175                 CSSParserValue v;
1176                 v.id = 0;
1177                 v.unit = CSSParserValue::Operator;
1178                 v.iValue = $2;
1179                 $$->addValue(v);
1180             }
1181             $$->addValue(p->sinkFloatingValue($3));
1182         }
1183     }
1184     | expr error {
1185         $$ = 0;
1186     }
1187   ;
1188
1189 operator:
1190     '/' maybe_space {
1191         $$ = '/';
1192     }
1193   | ',' maybe_space {
1194         $$ = ',';
1195     }
1196   | /* empty */ {
1197         $$ = 0;
1198   }
1199   ;
1200
1201 term:
1202   unary_term { $$ = $1; }
1203   | unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
1204   | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; }
1205   | IDENT maybe_space {
1206       $$.id = cssValueKeywordID($1);
1207       $$.unit = CSSPrimitiveValue::CSS_IDENT;
1208       $$.string = $1;
1209   }
1210   /* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
1211   | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1212   | unary_operator DIMEN maybe_space { $$.id = 0; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1213   | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; }
1214   | UNICODERANGE maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_UNICODE_RANGE }
1215   | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_PARSER_HEXCOLOR; }
1216   | '#' maybe_space { $$.id = 0; $$.string = CSSParserString(); $$.unit = CSSPrimitiveValue::CSS_PARSER_HEXCOLOR; } /* Handle error case: "color: #;" */
1217   /* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
1218   | function {
1219       $$ = $1;
1220   }
1221   | VARCALL maybe_space {
1222       $$.id = 0;
1223       $$.string = $1;
1224       $$.unit = CSSPrimitiveValue::CSS_PARSER_VARIABLE_FUNCTION_SYNTAX;
1225   }
1226   | '=' IDENT '=' maybe_space {
1227       $$.id = 0;
1228       $$.string = $2;
1229       $$.unit = CSSPrimitiveValue::CSS_PARSER_VARIABLE_EQUALS_SYNTAX;
1230   }
1231   | '$' IDENT maybe_space {
1232       $$.id = 0;
1233       $$.string = $2;
1234       $$.unit = CSSPrimitiveValue::CSS_PARSER_VARIABLE_DOLLAR_SYNTAX;
1235   }
1236
1237   | '%' maybe_space {} /* Handle width: %; */
1238   ;
1239
1240 unary_term:
1241   INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1242   | FLOATTOKEN maybe_space { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1243   | PERCENTAGE maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; }
1244   | PXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; }
1245   | CMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; }
1246   | MMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; }
1247   | INS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; }
1248   | PTS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; }
1249   | PCS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; }
1250   | DEGS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; }
1251   | RADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; }
1252   | GRADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; }
1253   | MSECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; }
1254   | SECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; }
1255   | HERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; }
1256   | KHERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; }
1257   | EMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; }
1258   | QEMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSParserValue::Q_EMS; }
1259   | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; }
1260     ;
1261
1262
1263 function:
1264     FUNCTION maybe_space expr ')' maybe_space {
1265         CSSParser* p = static_cast<CSSParser*>(parser);
1266         CSSParserFunction* f = p->createFloatingFunction();
1267         f->name = $1;
1268         f->args = p->sinkFloatingValueList($3);
1269         $$.id = 0;
1270         $$.unit = CSSParserValue::Function;
1271         $$.function = f;
1272     } |
1273     FUNCTION maybe_space error {
1274         CSSParser* p = static_cast<CSSParser*>(parser);
1275         CSSParserFunction* f = p->createFloatingFunction();
1276         f->name = $1;
1277         f->args = 0;
1278         $$.id = 0;
1279         $$.unit = CSSParserValue::Function;
1280         $$.function = f;
1281   }
1282   ;
1283 /*
1284  * There is a constraint on the color that it must
1285  * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
1286  * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
1287  */
1288 hexcolor:
1289   HEX maybe_space { $$ = $1; }
1290   | IDSEL maybe_space { $$ = $1; }
1291   ;
1292
1293
1294 /* error handling rules */
1295
1296 invalid_at:
1297     ATKEYWORD error invalid_block {
1298         $$ = 0;
1299     }
1300   | ATKEYWORD error ';' {
1301         $$ = 0;
1302     }
1303     ;
1304
1305 invalid_import:
1306     import {
1307         $$ = 0;
1308     }
1309     ;
1310
1311 invalid_rule:
1312     error invalid_block {
1313         $$ = 0;
1314     }
1315 /*
1316   Seems like the two rules below are trying too much and violating
1317   http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html
1318
1319   | error ';' {
1320         $$ = 0;
1321     }
1322   | error '}' {
1323         $$ = 0;
1324     }
1325 */
1326     ;
1327
1328 invalid_block:
1329     '{' error invalid_block_list error '}'
1330   | '{' error '}'
1331     ;
1332
1333 invalid_block_list:
1334     invalid_block
1335   | invalid_block_list error invalid_block
1336 ;
1337
1338 %%