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