2007-02-09 Nicholas Shanks <webkit@nickshanks.com>
[WebKit-https.git] / WebCore / css / CSSGrammar.y
1 %{
2
3 /*
4  *  This file is part of the KDE libraries
5  *  Copyright (C) 2002-2003 Lars Knoll (knoll@kde.org)
6  *  Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
7  *  Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com)
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  *
23  */
24
25 #include "config.h"
26
27 #include "CSSMediaRule.h"
28 #include "CSSPrimitiveValue.h"
29 #include "CSSRule.h"
30 #include "CSSRuleList.h"
31 #include "CSSSelector.h"
32 #include "CSSStyleSheet.h"
33 #include "Document.h"
34 #include "HTMLNames.h"
35 #include "MediaList.h"
36 #include "MediaQuery.h"
37 #include "MediaQueryExp.h"
38 #include "PlatformString.h"
39 #include "cssparser.h"
40 #include <assert.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #ifdef SVG_SUPPORT
45 #include "ksvgcssproperties.h"
46 #include "ksvgcssvalues.h"
47 #endif
48
49 using namespace WebCore;
50 using namespace HTMLNames;
51
52 // The following file defines the function
53 //     const struct props *findProp(const char *word, int len)
54 //
55 // with 'props->id' a CSS property in the range from CSS_PROP_MIN to
56 // (and including) CSS_PROP_TOTAL-1
57
58 #include "CSSPropertyNames.c"
59 #include "CSSValueKeywords.c"
60
61 namespace WebCore {
62
63 int getPropertyID(const char* tagStr, int len)
64 {
65     DeprecatedString prop;
66
67     if (len && tagStr[0] == '-') {
68         prop = DeprecatedString(tagStr, len);
69         if (prop.startsWith("-apple-")) {
70             prop = "-webkit-" + prop.mid(7);
71             tagStr = prop.ascii();
72             len++;
73         } else if (prop.startsWith("-khtml-")) {
74             prop = "-webkit-" + prop.mid(7);
75             len++;
76             tagStr = prop.ascii();
77         }
78
79         // Honor the use of old-style opacity (for Safari 1.1).
80         if (prop == "-webkit-opacity") {
81             const char * const opacity = "opacity";
82             tagStr = opacity;
83             len = strlen(opacity);
84         }
85     }
86
87     const struct props* propsPtr = findProp(tagStr, len);
88     if (!propsPtr)
89         return 0;
90
91     return propsPtr->id;
92 }
93
94 } // namespace WebCore
95
96 static inline int getValueID(const char* tagStr, int len)
97 {
98     DeprecatedString prop;
99     if (len && tagStr[0] == '-') {
100         prop = DeprecatedString(tagStr, len);
101         if (prop.startsWith("-apple-")) {
102             prop = "-webkit-" + prop.mid(7);
103             tagStr = prop.ascii();
104             len++;
105         } else if (prop.startsWith("-khtml-")) {
106             prop = "-webkit-" + prop.mid(7);
107             len++;
108             tagStr = prop.ascii();
109         }
110     }
111
112     const struct css_value* val = findValue(tagStr, len);
113     if (!val)
114         return 0;
115
116     return val->id;
117 }
118
119 #define YYENABLE_NLS 0
120 #define YYLTYPE_IS_TRIVIAL 1
121 #define YYMAXDEPTH 10000
122 #define YYDEBUG 0
123 #define YYPARSE_PARAM parser
124
125 %}
126
127 %pure_parser
128
129 %union {
130     CSSRule* rule;
131     CSSSelector* selector;
132     bool ok;
133     MediaList *mediaList;
134     CSSMediaRule* mediaRule;
135     CSSRuleList* ruleList;
136     ParseString string;
137     float val;
138     int prop_id;
139     int attribute;
140     CSSSelector::Relation relation;
141     bool b;
142     int i;
143     char tok;
144     Value value;
145     ValueList* valueList;
146
147     MediaQuery* mediaQuery;
148     MediaQueryExp* mediaQueryExp;
149     Vector<MediaQueryExp*>* mediaQueryExpList;
150     MediaQuery::Restrictor mediaQueryRestrictor;
151 }
152
153 %{
154
155 static inline int cssyyerror(const char*) { return 1; }
156 static int cssyylex(YYSTYPE* yylval) { return CSSParser::current()->lex(yylval); }
157
158 %}
159
160 //%expect 37
161
162 %left UNIMPORTANT_TOK
163
164 %token WHITESPACE SGML_CD
165
166 %token INCLUDES
167 %token DASHMATCH
168 %token BEGINSWITH
169 %token ENDSWITH
170 %token CONTAINS
171
172 %token <string> STRING
173
174 %right <string> IDENT
175
176 %nonassoc <string> HASH
177 %nonassoc ':'
178 %nonassoc '.'
179 %nonassoc '['
180 %nonassoc <string> '*'
181 %nonassoc error
182 %left '|'
183
184 %token IMPORT_SYM
185 %token PAGE_SYM
186 %token MEDIA_SYM
187 %token FONT_FACE_SYM
188 %token CHARSET_SYM
189 %token NAMESPACE_SYM
190 %token WEBKIT_RULE_SYM
191 %token WEBKIT_DECLS_SYM
192 %token WEBKIT_VALUE_SYM
193 %token WEBKIT_MEDIAQUERY_SYM
194
195 %token IMPORTANT_SYM
196 %token MEDIA_ONLY
197 %token MEDIA_NOT
198 %token MEDIA_AND
199
200 %token <val> QEMS
201 %token <val> EMS
202 %token <val> EXS
203 %token <val> PXS
204 %token <val> CMS
205 %token <val> MMS
206 %token <val> INS
207 %token <val> PTS
208 %token <val> PCS
209 %token <val> DEGS
210 %token <val> RADS
211 %token <val> GRADS
212 %token <val> MSECS
213 %token <val> SECS
214 %token <val> HERZ
215 %token <val> KHERZ
216 %token <string> DIMEN
217 %token <val> PERCENTAGE
218 %token <val> FLOAT
219 %token <val> INTEGER
220
221 %token <string> URI
222 %token <string> FUNCTION
223 %token <string> NOTFUNCTION
224
225 %token <string> UNICODERANGE
226
227 %type <relation> combinator
228
229 %type <rule> charset
230 %type <rule> ruleset
231 %type <rule> ruleset_or_import
232 %type <rule> media
233 %type <rule> import
234 %type <rule> page
235 %type <rule> font_face
236 %type <rule> invalid_rule
237 %type <rule> invalid_at
238 %type <rule> invalid_import
239 %type <rule> rule
240
241 %type <string> maybe_ns_prefix
242
243 %type <string> namespace_selector
244
245 %type <string> string_or_uri
246 %type <string> ident_or_string
247 %type <string> medium
248 %type <string> hexcolor
249
250 %type <string> media_feature
251 %type <mediaList> media_list
252 %type <mediaList> maybe_media_list
253 %type <mediaQuery> media_query
254 %type <mediaQueryRestrictor> maybe_media_restrictor
255 %type <valueList> maybe_media_value
256 %type <mediaQueryExp> media_query_exp
257 %type <mediaQueryExpList> media_query_exp_list
258 %type <mediaQueryExpList> maybe_media_query_exp_list
259
260 %type <ruleList> ruleset_list
261
262 %type <prop_id> property
263
264 %type <selector> specifier
265 %type <selector> specifier_list
266 %type <selector> simple_selector
267 %type <selector> selector
268 %type <selector> selector_list
269 %type <selector> class
270 %type <selector> attrib
271 %type <selector> pseudo
272
273 %type <ok> declaration_list
274 %type <ok> decl_list
275 %type <ok> declaration
276
277 %type <b> prio
278
279 %type <i> match
280 %type <i> unary_operator
281 %type <tok> operator
282
283 %type <valueList> expr
284 %type <value> term
285 %type <value> unary_term
286 %type <value> function
287
288 %type <string> element_name
289 %type <string> attr_name
290
291 %%
292
293 stylesheet:
294     maybe_charset maybe_sgml import_list namespace_list rule_list
295   | webkit_rule maybe_space
296   | webkit_decls maybe_space
297   | webkit_value maybe_space
298   | webkit_mediaquery maybe_space
299   ;
300
301 ruleset_or_import:
302    ruleset |
303    import
304 ;
305
306 webkit_rule:
307     WEBKIT_RULE_SYM '{' maybe_space ruleset_or_import maybe_space '}' {
308         static_cast<CSSParser*>(parser)->rule = $4;
309     }
310 ;
311
312 webkit_decls:
313     WEBKIT_DECLS_SYM '{' maybe_space declaration_list '}' {
314         /* can be empty */
315     }
316 ;
317
318 webkit_value:
319     WEBKIT_VALUE_SYM '{' maybe_space expr '}' {
320         CSSParser* p = static_cast<CSSParser*>(parser);
321         if ($4) {
322             p->valueList = p->sinkFloatingValueList($4);
323             int oldParsedProperties = p->numParsedProperties;
324             if (!p->parseValue(p->id, p->important))
325                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
326             delete p->valueList;
327             p->valueList = 0;
328         }
329     }
330 ;
331
332 webkit_mediaquery:
333      WEBKIT_MEDIAQUERY_SYM WHITESPACE maybe_space media_query '}' {
334          CSSParser* p = static_cast<CSSParser*>(parser);
335          p->mediaQuery = p->sinkFloatingMediaQuery($4);
336      }
337 ;
338
339 maybe_space:
340     /* empty */ %prec UNIMPORTANT_TOK
341   | maybe_space WHITESPACE
342   ;
343
344 maybe_sgml:
345     /* empty */
346   | maybe_sgml SGML_CD
347   | maybe_sgml WHITESPACE
348   ;
349
350 maybe_charset:
351    /* empty */
352   | charset {
353   }
354 ;
355
356 charset:
357   CHARSET_SYM maybe_space STRING maybe_space ';' {
358      CSSParser* p = static_cast<CSSParser*>(parser);
359      $$ = static_cast<CSSParser*>(parser)->createCharsetRule($3);
360      if ($$ && p->styleElement && p->styleElement->isCSSStyleSheet())
361          p->styleElement->append($$);
362   }
363   | CHARSET_SYM error invalid_block {
364   }
365   | CHARSET_SYM error ';' {
366   }
367 ;
368
369 import_list:
370  /* empty */
371  | import_list import maybe_sgml {
372      CSSParser* p = static_cast<CSSParser*>(parser);
373      if ($2 && p->styleElement && p->styleElement->isCSSStyleSheet())
374          p->styleElement->append($2);
375  }
376  ;
377
378 namespace_list:
379 /* empty */
380 | namespace_list namespace maybe_sgml
381 ;
382
383 rule_list:
384    /* empty */
385  | rule_list rule maybe_sgml {
386      CSSParser* p = static_cast<CSSParser*>(parser);
387      if ($2 && p->styleElement && p->styleElement->isCSSStyleSheet())
388          p->styleElement->append($2);
389  }
390  ;
391
392 rule:
393     ruleset
394   | media
395   | page
396   | font_face
397   | invalid_rule
398   | invalid_at
399   | invalid_import
400     ;
401
402 import:
403     IMPORT_SYM maybe_space string_or_uri maybe_space maybe_media_list ';' {
404         $$ = static_cast<CSSParser*>(parser)->createImportRule($3, $5);
405     }
406   | IMPORT_SYM error invalid_block {
407         $$ = 0;
408     }
409   | IMPORT_SYM error ';' {
410         $$ = 0;
411     }
412   ;
413
414 namespace:
415 NAMESPACE_SYM maybe_space maybe_ns_prefix string_or_uri maybe_space ';' {
416     CSSParser* p = static_cast<CSSParser*>(parser);
417     if (p->styleElement && p->styleElement->isCSSStyleSheet())
418         static_cast<CSSStyleSheet*>(p->styleElement)->addNamespace(p, atomicString($3), atomicString($4));
419 }
420 | NAMESPACE_SYM error invalid_block
421 | NAMESPACE_SYM error ';'
422 ;
423
424 maybe_ns_prefix:
425 /* empty */ { $$.characters = 0; }
426 | IDENT WHITESPACE { $$ = $1; }
427 ;
428
429 string_or_uri:
430 STRING
431 | URI
432 ;
433
434 media_feature:
435     IDENT maybe_space {
436         $$ = $1;
437     }
438     ;
439
440 maybe_media_value:
441     /*empty*/ {
442         $$ = 0;
443     }
444     | ':' maybe_space expr maybe_space {
445         $$ = $3;
446     }
447     ;
448
449 media_query_exp:
450     MEDIA_AND maybe_space '(' maybe_space media_feature maybe_space maybe_media_value ')' maybe_space {
451         $5.lower();
452         $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExp(atomicString($5), $7);
453     }
454     ;
455
456 media_query_exp_list:
457     media_query_exp {
458       CSSParser* p = static_cast<CSSParser*>(parser);
459       $$ = p->createFloatingMediaQueryExpList();
460       $$->append(p->sinkFloatingMediaQueryExp($1));
461     }
462     | media_query_exp_list media_query_exp {
463       $$ = $1;
464       $$->append(static_cast<CSSParser*>(parser)->sinkFloatingMediaQueryExp($2));
465     }
466     ;
467
468 maybe_media_query_exp_list:
469     /*empty*/ {
470         $$ = static_cast<CSSParser*>(parser)->createFloatingMediaQueryExpList();
471     }
472     | media_query_exp_list
473     ;
474
475 maybe_media_restrictor:
476     /*empty*/ {
477         $$ = MediaQuery::None;
478     }
479     | MEDIA_ONLY {
480         $$ = MediaQuery::Only;
481     }
482     | MEDIA_NOT {
483         $$ = MediaQuery::Not;
484     }
485     ;
486
487 media_query:
488     maybe_media_restrictor maybe_space medium maybe_media_query_exp_list {
489         CSSParser* p = static_cast<CSSParser*>(parser);
490         $3.lower();
491         $$ = p->createFloatingMediaQuery($1, domString($3), p->sinkFloatingMediaQueryExpList($4));
492     }
493     ;
494
495 maybe_media_list:
496      /* empty */ {
497         $$ = static_cast<CSSParser*>(parser)->createMediaList();
498      }
499      | media_list
500      ;
501
502 media_list:
503     media_query {
504         CSSParser* p = static_cast<CSSParser*>(parser);
505         $$ = p->createMediaList();
506         $$->appendMediaQuery(p->sinkFloatingMediaQuery($1));
507     }
508     | media_list ',' maybe_space media_query {
509         $$ = $1;
510         if ($$)
511             $$->appendMediaQuery(static_cast<CSSParser*>(parser)->sinkFloatingMediaQuery($4));
512     }
513     | media_list error {
514         $$ = 0;
515     }
516     ;
517
518 media:
519     MEDIA_SYM maybe_space media_list '{' maybe_space ruleset_list '}' {
520         $$ = static_cast<CSSParser*>(parser)->createMediaRule($3, $6);
521     }
522     | MEDIA_SYM maybe_space '{' maybe_space ruleset_list '}' {
523         $$ = static_cast<CSSParser*>(parser)->createMediaRule(0, $5);
524     }
525     ;
526
527 ruleset_list:
528     /* empty */ { $$ = 0; }
529     | ruleset_list ruleset maybe_space {
530         $$ = $1;
531         if ($2) {
532             if (!$$)
533                 $$ = static_cast<CSSParser*>(parser)->createRuleList();
534             $$->append($2);
535         }
536     }
537     ;
538
539 medium:
540   IDENT maybe_space {
541       $$ = $1;
542   }
543   ;
544
545 /*
546 page:
547     PAGE_SYM maybe_space IDENT? pseudo_page? maybe_space
548     '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
549   ;
550
551 pseudo_page
552   : ':' IDENT
553   ;
554
555 font_face
556   : FONT_FACE_SYM maybe_space
557     '{' maybe_space declaration [ ';' maybe_space declaration ]* '}' maybe_space
558   ;
559 */
560
561 page:
562     PAGE_SYM error invalid_block {
563       $$ = 0;
564     }
565   | PAGE_SYM error ';' {
566       $$ = 0;
567     }
568     ;
569
570 font_face:
571     FONT_FACE_SYM error invalid_block {
572       $$ = 0;
573     }
574   | FONT_FACE_SYM error ';' {
575       $$ = 0;
576     }
577 ;
578
579 combinator:
580     '+' maybe_space { $$ = CSSSelector::DirectAdjacent; }
581   | '~' maybe_space { $$ = CSSSelector::IndirectAdjacent; }
582   | '>' maybe_space { $$ = CSSSelector::Child; }
583   | /* empty */ { $$ = CSSSelector::Descendant; }
584   ;
585
586 unary_operator:
587     '-' { $$ = -1; }
588   | '+' { $$ = 1; }
589   ;
590
591 ruleset:
592     selector_list '{' maybe_space declaration_list '}' {
593         $$ = static_cast<CSSParser*>(parser)->createStyleRule($1);
594     }
595   ;
596
597 selector_list:
598     selector %prec UNIMPORTANT_TOK {
599         $$ = $1;
600     }
601     | selector_list ',' maybe_space selector %prec UNIMPORTANT_TOK {
602         if ($1 && $4) {
603             CSSParser* p = static_cast<CSSParser*>(parser);
604             $$ = $1;
605             $$->append(p->sinkFloatingSelector($4));
606         } else
607             $$ = 0;
608     }
609   | selector_list error {
610         $$ = 0;
611     }
612    ;
613
614 selector:
615     simple_selector {
616         $$ = $1;
617     }
618     | selector combinator simple_selector {
619         $$ = $3;
620         if (!$1)
621             $$ = 0;
622         else if ($$) {
623             CSSParser* p = static_cast<CSSParser*>(parser);
624             CSSSelector* end = $$;
625             while (end->m_tagHistory)
626                 end = end->m_tagHistory;
627             end->m_relation = $2;
628             end->m_tagHistory = p->sinkFloatingSelector($1);
629             if ($2 == CSSSelector::Descendant || $2 == CSSSelector::Child) {
630                 if (Document* doc = p->document())
631                     doc->setUsesDescendantRules(true);
632             } else if ($2 == CSSSelector::DirectAdjacent || $2 == CSSSelector::IndirectAdjacent) {
633                 if (Document* doc = p->document())
634                     doc->setUsesSiblingRules(true);
635             }
636         }
637     }
638     | selector error {
639         $$ = 0;
640     }
641     ;
642
643 namespace_selector:
644     /* empty */ '|' { $$.characters = 0; $$.length = 0; }
645     | '*' '|' { static UChar star = '*'; $$.characters = &star; $$.length = 1; }
646     | IDENT '|' { $$ = $1; }
647 ;
648
649 simple_selector:
650     element_name maybe_space {
651         CSSParser* p = static_cast<CSSParser*>(parser);
652         $$ = p->createFloatingSelector();
653         $$->m_tag = QualifiedName(nullAtom, atomicString($1), p->defaultNamespace);
654     }
655     | element_name specifier_list maybe_space {
656         $$ = $2;
657         if ($$) {
658             CSSParser* p = static_cast<CSSParser*>(parser);
659             $$->m_tag = QualifiedName(nullAtom, atomicString($1), p->defaultNamespace);
660         }
661     }
662     | specifier_list maybe_space {
663         $$ = $1;
664         CSSParser* p = static_cast<CSSParser*>(parser);
665         if ($$ && p->defaultNamespace != starAtom)
666             $$->m_tag = QualifiedName(nullAtom, starAtom, p->defaultNamespace);
667     }
668     | namespace_selector element_name maybe_space {
669         AtomicString namespacePrefix = atomicString($1);
670         CSSParser* p = static_cast<CSSParser*>(parser);
671         $$ = p->createFloatingSelector();
672         if (p->styleElement && p->styleElement->isCSSStyleSheet())
673             $$->m_tag = QualifiedName(namespacePrefix,
674                                     atomicString($2),
675                                     static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
676         else // FIXME: Shouldn't this case be an error?
677             $$->m_tag = QualifiedName(nullAtom, atomicString($2), p->defaultNamespace);
678     }
679     | namespace_selector element_name specifier_list maybe_space {
680         $$ = $3;
681         if ($$) {
682             AtomicString namespacePrefix = atomicString($1);
683             CSSParser* p = static_cast<CSSParser*>(parser);
684             if (p->styleElement && p->styleElement->isCSSStyleSheet())
685                 $$->m_tag = QualifiedName(namespacePrefix,
686                                           atomicString($2),
687                                           static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
688             else // FIXME: Shouldn't this case be an error?
689                 $$->m_tag = QualifiedName(nullAtom, atomicString($2), p->defaultNamespace);
690         }
691     }
692     | namespace_selector specifier_list maybe_space {
693         $$ = $2;
694         if ($$) {
695             AtomicString namespacePrefix = atomicString($1);
696             CSSParser* p = static_cast<CSSParser*>(parser);
697             if (p->styleElement && p->styleElement->isCSSStyleSheet())
698                 $$->m_tag = QualifiedName(namespacePrefix,
699                                           starAtom,
700                                           static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
701         }
702     }
703   ;
704
705 element_name:
706     IDENT {
707         ParseString& str = $1;
708         CSSParser* p = static_cast<CSSParser*>(parser);
709         Document* doc = p->document();
710         if (doc && doc->isHTMLDocument())
711             str.lower();
712         $$ = str;
713     }
714     | '*' {
715         static UChar star = '*';
716         $$.characters = &star;
717         $$.length = 1;
718     }
719   ;
720
721 specifier_list:
722     specifier {
723         $$ = $1;
724     }
725     | specifier_list specifier {
726         if (!$2)
727             $$ = 0;
728         else if ($1) {
729             $$ = $1;
730             CSSParser* p = static_cast<CSSParser*>(parser);
731             CSSSelector* end = $1;
732             while (end->m_tagHistory)
733                 end = end->m_tagHistory;
734             end->m_relation = CSSSelector::SubSelector;
735             end->m_tagHistory = p->sinkFloatingSelector($2);
736         }
737     }
738     | specifier_list error {
739         $$ = 0;
740     }
741 ;
742
743 specifier:
744     HASH {
745         CSSParser* p = static_cast<CSSParser*>(parser);
746         $$ = p->createFloatingSelector();
747         $$->m_match = CSSSelector::Id;
748         if (!p->strict)
749             $1.lower();
750         $$->m_attr = idAttr;
751         $$->m_value = atomicString($1);
752     }
753   | class
754   | attrib
755   | pseudo
756     ;
757
758 class:
759     '.' IDENT {
760         CSSParser* p = static_cast<CSSParser*>(parser);
761         $$ = p->createFloatingSelector();
762         $$->m_match = CSSSelector::Class;
763         if (!p->strict)
764             $2.lower();
765         $$->m_attr = classAttr;
766         $$->m_value = atomicString($2);
767     }
768   ;
769
770 attr_name:
771     IDENT maybe_space {
772         ParseString& str = $1;
773         CSSParser* p = static_cast<CSSParser*>(parser);
774         Document* doc = p->document();
775         if (doc && doc->isHTMLDocument())
776             str.lower();
777         $$ = str;
778     }
779     ;
780
781 attrib:
782     '[' maybe_space attr_name ']' {
783         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
784         $$->m_attr = QualifiedName(nullAtom, atomicString($3), nullAtom);
785         $$->m_match = CSSSelector::Set;
786     }
787     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
788         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
789         $$->m_attr = QualifiedName(nullAtom, atomicString($3), nullAtom);
790         $$->m_match = (CSSSelector::Match)$4;
791         $$->m_value = atomicString($6);
792     }
793     | '[' maybe_space namespace_selector attr_name ']' {
794         AtomicString namespacePrefix = atomicString($3);
795         CSSParser* p = static_cast<CSSParser*>(parser);
796         $$ = p->createFloatingSelector();
797         $$->m_attr = QualifiedName(namespacePrefix,
798                                    atomicString($4),
799                                    static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
800         $$->m_match = CSSSelector::Set;
801     }
802     | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
803         AtomicString namespacePrefix = atomicString($3);
804         CSSParser* p = static_cast<CSSParser*>(parser);
805         $$ = p->createFloatingSelector();
806         $$->m_attr = QualifiedName(namespacePrefix,
807                                    atomicString($4),
808                                    static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
809         $$->m_match = (CSSSelector::Match)$5;
810         $$->m_value = atomicString($7);
811     }
812   ;
813
814 match:
815     '=' {
816         $$ = CSSSelector::Exact;
817     }
818     | INCLUDES {
819         $$ = CSSSelector::List;
820     }
821     | DASHMATCH {
822         $$ = CSSSelector::Hyphen;
823     }
824     | BEGINSWITH {
825         $$ = CSSSelector::Begin;
826     }
827     | ENDSWITH {
828         $$ = CSSSelector::End;
829     }
830     | CONTAINS {
831         $$ = CSSSelector::Contain;
832     }
833     ;
834
835 ident_or_string:
836     IDENT
837   | STRING
838     ;
839
840 pseudo:
841     ':' IDENT {
842         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
843         $$->m_match = CSSSelector::PseudoClass;
844         $2.lower();
845         $$->m_value = atomicString($2);
846         CSSSelector::PseudoType type = $$->pseudoType();
847         if (type == CSSSelector::PseudoUnknown)
848             $$ = 0;
849         else if (type == CSSSelector::PseudoEmpty ||
850                  type == CSSSelector::PseudoFirstChild) {
851             CSSParser* p = static_cast<CSSParser*>(parser);
852             Document* doc = p->document();
853             if (doc)
854                 doc->setUsesSiblingRules(true);
855         }
856     }
857     | ':' ':' IDENT {
858         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
859         $$->m_match = CSSSelector::PseudoElement;
860         $3.lower();
861         $$->m_value = atomicString($3);
862         if ($$->pseudoType() == CSSSelector::PseudoUnknown)
863             $$ = 0;
864     }
865     // used by :lang
866     | ':' FUNCTION IDENT ')' {
867         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
868         $$->m_match = CSSSelector::PseudoClass;
869         $$->m_argument = atomicString($3);
870         $2.lower();
871         $$->m_value = atomicString($2);
872         if ($$->pseudoType() == CSSSelector::PseudoUnknown)
873             $$ = 0;
874     }
875     // used by :not
876     | ':' NOTFUNCTION maybe_space simple_selector ')' {
877         if (!$4)
878             $$ = 0;
879         else {
880             CSSParser* p = static_cast<CSSParser*>(parser);
881             $$ = p->createFloatingSelector();
882             $$->m_match = CSSSelector::PseudoClass;
883             $$->m_simpleSelector = p->sinkFloatingSelector($4);
884             $2.lower();
885             $$->m_value = atomicString($2);
886         }
887     }
888   ;
889
890 declaration_list:
891     declaration {
892         $$ = $1;
893     }
894     | decl_list declaration {
895         $$ = $1;
896         if ( $2 )
897             $$ = $2;
898     }
899     | decl_list {
900         $$ = $1;
901     }
902     | error invalid_block_list error {
903         $$ = false;
904     }
905     | error {
906         $$ = false;
907     }
908     | decl_list error {
909         $$ = $1;
910     }
911     ;
912
913 decl_list:
914     declaration ';' maybe_space {
915         $$ = $1;
916     }
917     | declaration invalid_block_list ';' maybe_space {
918         $$ = false;
919     }
920     | error ';' maybe_space {
921         $$ = false;
922     }
923     | error invalid_block_list error ';' maybe_space {
924         $$ = false;
925     }
926     | decl_list declaration ';' maybe_space {
927         $$ = $1;
928         if ($2)
929             $$ = $2;
930     }
931     | decl_list error ';' maybe_space {
932         $$ = $1;
933     }
934     | decl_list error invalid_block_list error ';' maybe_space {
935         $$ = $1;
936     }
937     ;
938
939 declaration:
940     property ':' maybe_space expr prio {
941         $$ = false;
942         CSSParser* p = static_cast<CSSParser*>(parser);
943         if ($1 && $4) {
944             p->valueList = p->sinkFloatingValueList($4);
945             int oldParsedProperties = p->numParsedProperties;
946             $$ = p->parseValue($1, $5);
947             if (!$$)
948                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
949             delete p->valueList;
950             p->valueList = 0;
951         }
952     }
953     |
954     property error {
955         $$ = false;
956     }
957     |
958     property ':' maybe_space error expr prio {
959         /* The default movable type template has letter-spacing: .none;  Handle this by looking for
960         error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
961         up and deleting the shifted expr.  */
962         $$ = false;
963     }
964     |
965     IMPORTANT_SYM maybe_space {
966         /* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
967         $$ = false;
968     }
969     |
970     property ':' maybe_space {
971         /* div { font-family: } Just reduce away this property with no value. */
972         $$ = false;
973     }
974   ;
975
976 property:
977     IDENT maybe_space {
978         $1.lower();
979         DeprecatedString str = deprecatedString($1);
980         const char* s = str.ascii();
981         int l = str.length();
982         $$ = getPropertyID(s, l);
983 #ifdef SVG_SUPPORT
984         if ($$ == 0)
985             $$ = SVG::getSVGCSSPropertyID(s, l);
986 #endif
987     }
988   ;
989
990 prio:
991     IMPORTANT_SYM maybe_space { $$ = true; }
992     | /* empty */ { $$ = false; }
993   ;
994
995 expr:
996     term {
997         CSSParser* p = static_cast<CSSParser*>(parser);
998         $$ = p->createFloatingValueList();
999         $$->addValue(p->sinkFloatingValue($1));
1000     }
1001     | expr operator term {
1002         CSSParser* p = static_cast<CSSParser*>(parser);
1003         $$ = $1;
1004         if ($$) {
1005             if ($2) {
1006                 Value v;
1007                 v.id = 0;
1008                 v.unit = Value::Operator;
1009                 v.iValue = $2;
1010                 $$->addValue(v);
1011             }
1012             $$->addValue(p->sinkFloatingValue($3));
1013         }
1014     }
1015     | expr error {
1016         $$ = 0;
1017     }
1018   ;
1019
1020 operator:
1021     '/' maybe_space {
1022         $$ = '/';
1023     }
1024   | ',' maybe_space {
1025         $$ = ',';
1026     }
1027   | /* empty */ {
1028         $$ = 0;
1029   }
1030   ;
1031
1032 term:
1033   unary_term { $$ = $1; }
1034   | unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
1035   | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; }
1036   | IDENT maybe_space {
1037       DeprecatedString str = deprecatedString($1);
1038       $$.id = getValueID(str.lower().latin1(), str.length());
1039 #ifdef SVG_SUPPORT
1040       if ($$.id == 0)
1041           $$.id = SVG::getSVGCSSValueID(str.lower().latin1(), str.length());
1042 #endif
1043       $$.unit = CSSPrimitiveValue::CSS_IDENT;
1044       $$.string = $1;
1045   }
1046   /* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
1047   | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1048   | unary_operator DIMEN maybe_space { $$.id = 0; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1049   | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; }
1050   | UNICODERANGE maybe_space { $$.id = 0; $$.iValue = 0; $$.unit = CSSPrimitiveValue::CSS_UNKNOWN;/* ### */ }
1051   | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; }
1052   | '#' maybe_space { $$.id = 0; $$.string = ParseString(); $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; } /* Handle error case: "color: #;" */
1053 /* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
1054   | function {
1055       $$ = $1;
1056   }
1057   ;
1058
1059 unary_term:
1060   INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1061   | FLOAT maybe_space { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1062   | PERCENTAGE maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; }
1063   | PXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; }
1064   | CMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; }
1065   | MMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; }
1066   | INS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; }
1067   | PTS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; }
1068   | PCS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; }
1069   | DEGS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; }
1070   | RADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; }
1071   | GRADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; }
1072   | MSECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; }
1073   | SECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; }
1074   | HERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; }
1075   | KHERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; }
1076   | EMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; }
1077   | QEMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = Value::Q_EMS; }
1078   | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; }
1079     ;
1080
1081
1082 function:
1083     FUNCTION maybe_space expr ')' maybe_space {
1084         CSSParser* p = static_cast<CSSParser*>(parser);
1085         Function* f = p->createFloatingFunction();
1086         f->name = $1;
1087         f->args = p->sinkFloatingValueList($3);
1088         $$.id = 0;
1089         $$.unit = Value::Function;
1090         $$.function = f;
1091     } |
1092     FUNCTION maybe_space error {
1093         CSSParser* p = static_cast<CSSParser*>(parser);
1094         Function* f = p->createFloatingFunction();
1095         f->name = $1;
1096         f->args = 0;
1097         $$.id = 0;
1098         $$.unit = Value::Function;
1099         $$.function = f;
1100   }
1101   ;
1102 /*
1103  * There is a constraint on the color that it must
1104  * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
1105  * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
1106  */
1107 hexcolor:
1108   HASH maybe_space { $$ = $1; }
1109   ;
1110
1111
1112 /* error handling rules */
1113
1114 invalid_at:
1115     '@' error invalid_block {
1116         $$ = 0;
1117     }
1118   | '@' error ';' {
1119         $$ = 0;
1120     }
1121     ;
1122
1123 invalid_import:
1124     import {
1125         $$ = 0;
1126     }
1127     ;
1128
1129 invalid_rule:
1130     error invalid_block {
1131         $$ = 0;
1132     }
1133 /*
1134   Seems like the two rules below are trying too much and violating
1135   http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html
1136
1137   | error ';' {
1138         $$ = 0;
1139     }
1140   | error '}' {
1141         $$ = 0;
1142     }
1143 */
1144     ;
1145
1146 invalid_block:
1147     '{' error invalid_block_list error '}'
1148   | '{' error '}'
1149     ;
1150
1151 invalid_block_list:
1152     invalid_block
1153   | invalid_block_list error invalid_block
1154 ;
1155
1156 %%