Reviewed by Maciej.
[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, 2007 Apple 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 <stdlib.h>
41 #include <string.h>
42
43 #if ENABLE(SVG)
44 #include "ksvgcssproperties.h"
45 #include "ksvgcssvalues.h"
46 #endif
47
48 using namespace WebCore;
49 using namespace HTMLNames;
50
51 // The following file defines the function
52 //     const struct props *findProp(const char *word, int len)
53 //
54 // with 'props->id' a CSS property in the range from CSS_PROP_MIN to
55 // (and including) CSS_PROP_TOTAL-1
56
57 #include "CSSPropertyNames.c"
58 #include "CSSValueKeywords.c"
59
60 namespace WebCore {
61
62 int getPropertyID(const char* tagStr, int len)
63 {
64     DeprecatedString prop;
65
66     if (len && tagStr[0] == '-') {
67         prop = DeprecatedString(tagStr, len);
68         if (prop.startsWith("-apple-")) {
69             prop = "-webkit-" + prop.mid(7);
70             tagStr = prop.ascii();
71             len++;
72         } else if (prop.startsWith("-khtml-")) {
73             prop = "-webkit-" + prop.mid(7);
74             len++;
75             tagStr = prop.ascii();
76         }
77
78         // Honor the use of old-style opacity (for Safari 1.1).
79         if (prop == "-webkit-opacity") {
80             const char * const opacity = "opacity";
81             tagStr = opacity;
82             len = strlen(opacity);
83         }
84     }
85
86     const struct props* propsPtr = findProp(tagStr, len);
87     if (!propsPtr)
88         return 0;
89
90     return propsPtr->id;
91 }
92
93 } // namespace WebCore
94
95 static inline int getValueID(const char* tagStr, int len)
96 {
97     DeprecatedString prop;
98     if (len && tagStr[0] == '-') {
99         prop = DeprecatedString(tagStr, len);
100         if (prop.startsWith("-apple-")) {
101             prop = "-webkit-" + prop.mid(7);
102             tagStr = prop.ascii();
103             len++;
104         } else if (prop.startsWith("-khtml-")) {
105             prop = "-webkit-" + prop.mid(7);
106             len++;
107             tagStr = prop.ascii();
108         }
109     }
110
111     const struct css_value* val = findValue(tagStr, len);
112     if (!val)
113         return 0;
114
115     return val->id;
116 }
117
118 #define YYENABLE_NLS 0
119 #define YYLTYPE_IS_TRIVIAL 1
120 #define YYMAXDEPTH 10000
121 #define YYDEBUG 0
122 #define YYPARSE_PARAM parser
123
124 %}
125
126 %pure_parser
127
128 %union {
129     CSSRule* rule;
130     CSSSelector* selector;
131     bool ok;
132     MediaList *mediaList;
133     CSSMediaRule* mediaRule;
134     CSSRuleList* ruleList;
135     ParseString string;
136     float val;
137     int prop_id;
138     int attribute;
139     CSSSelector::Relation relation;
140     bool b;
141     int i;
142     char tok;
143     Value value;
144     ValueList* valueList;
145
146     MediaQuery* mediaQuery;
147     MediaQueryExp* mediaQueryExp;
148     Vector<MediaQueryExp*>* mediaQueryExpList;
149     MediaQuery::Restrictor mediaQueryRestrictor;
150 }
151
152 %{
153
154 static inline int cssyyerror(const char*) { return 1; }
155 static int cssyylex(YYSTYPE* yylval) { return CSSParser::current()->lex(yylval); }
156
157 %}
158
159 //%expect 37
160
161 %left UNIMPORTANT_TOK
162
163 %token WHITESPACE SGML_CD
164
165 %token INCLUDES
166 %token DASHMATCH
167 %token BEGINSWITH
168 %token ENDSWITH
169 %token CONTAINS
170
171 %token <string> STRING
172
173 %right <string> IDENT
174
175 %nonassoc <string> HEX_OR_IDENT
176 %nonassoc <string> IDSEL
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     HEX_OR_IDENT {
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   | IDSEL {
754         CSSParser* p = static_cast<CSSParser*>(parser);
755         $$ = p->createFloatingSelector();
756         $$->m_match = CSSSelector::Id;
757         if (!p->strict)
758             $1.lower();
759         $$->m_attr = idAttr;
760         $$->m_value = atomicString($1);
761     }
762   | class
763   | attrib
764   | pseudo
765     ;
766
767 class:
768     '.' IDENT {
769         CSSParser* p = static_cast<CSSParser*>(parser);
770         $$ = p->createFloatingSelector();
771         $$->m_match = CSSSelector::Class;
772         if (!p->strict)
773             $2.lower();
774         $$->m_attr = classAttr;
775         $$->m_value = atomicString($2);
776     }
777   ;
778
779 attr_name:
780     IDENT maybe_space {
781         ParseString& str = $1;
782         CSSParser* p = static_cast<CSSParser*>(parser);
783         Document* doc = p->document();
784         if (doc && doc->isHTMLDocument())
785             str.lower();
786         $$ = str;
787     }
788     ;
789
790 attrib:
791     '[' maybe_space attr_name ']' {
792         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
793         $$->m_attr = QualifiedName(nullAtom, atomicString($3), nullAtom);
794         $$->m_match = CSSSelector::Set;
795     }
796     | '[' maybe_space attr_name match maybe_space ident_or_string maybe_space ']' {
797         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
798         $$->m_attr = QualifiedName(nullAtom, atomicString($3), nullAtom);
799         $$->m_match = (CSSSelector::Match)$4;
800         $$->m_value = atomicString($6);
801     }
802     | '[' maybe_space namespace_selector attr_name ']' {
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::Set;
810     }
811     | '[' maybe_space namespace_selector attr_name match maybe_space ident_or_string maybe_space ']' {
812         AtomicString namespacePrefix = atomicString($3);
813         CSSParser* p = static_cast<CSSParser*>(parser);
814         $$ = p->createFloatingSelector();
815         $$->m_attr = QualifiedName(namespacePrefix,
816                                    atomicString($4),
817                                    static_cast<CSSStyleSheet*>(p->styleElement)->determineNamespace(namespacePrefix));
818         $$->m_match = (CSSSelector::Match)$5;
819         $$->m_value = atomicString($7);
820     }
821   ;
822
823 match:
824     '=' {
825         $$ = CSSSelector::Exact;
826     }
827     | INCLUDES {
828         $$ = CSSSelector::List;
829     }
830     | DASHMATCH {
831         $$ = CSSSelector::Hyphen;
832     }
833     | BEGINSWITH {
834         $$ = CSSSelector::Begin;
835     }
836     | ENDSWITH {
837         $$ = CSSSelector::End;
838     }
839     | CONTAINS {
840         $$ = CSSSelector::Contain;
841     }
842     ;
843
844 ident_or_string:
845     IDENT
846   | STRING
847     ;
848
849 pseudo:
850     ':' IDENT {
851         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
852         $$->m_match = CSSSelector::PseudoClass;
853         $2.lower();
854         $$->m_value = atomicString($2);
855         CSSSelector::PseudoType type = $$->pseudoType();
856         if (type == CSSSelector::PseudoUnknown)
857             $$ = 0;
858         else if (type == CSSSelector::PseudoEmpty ||
859                  type == CSSSelector::PseudoFirstChild) {
860             CSSParser* p = static_cast<CSSParser*>(parser);
861             Document* doc = p->document();
862             if (doc)
863                 doc->setUsesSiblingRules(true);
864         } else if (type == CSSSelector::PseudoFirstLine) {
865             CSSParser* p = static_cast<CSSParser*>(parser);
866             if (Document* doc = p->document())
867                 doc->setUsesFirstLineRules(true);
868         }
869     }
870     | ':' ':' IDENT {
871         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
872         $$->m_match = CSSSelector::PseudoElement;
873         $3.lower();
874         $$->m_value = atomicString($3);
875         CSSSelector::PseudoType type = $$->pseudoType();
876         if (type == CSSSelector::PseudoUnknown)
877             $$ = 0;
878         else if (type == CSSSelector::PseudoFirstLine) {
879             CSSParser* p = static_cast<CSSParser*>(parser);
880             if (Document* doc = p->document())
881                 doc->setUsesFirstLineRules(true);
882         }
883     }
884     // used by :lang
885     | ':' FUNCTION IDENT ')' {
886         $$ = static_cast<CSSParser*>(parser)->createFloatingSelector();
887         $$->m_match = CSSSelector::PseudoClass;
888         $$->m_argument = atomicString($3);
889         $2.lower();
890         $$->m_value = atomicString($2);
891         if ($$->pseudoType() == CSSSelector::PseudoUnknown)
892             $$ = 0;
893     }
894     // used by :not
895     | ':' NOTFUNCTION maybe_space simple_selector ')' {
896         if (!$4)
897             $$ = 0;
898         else {
899             CSSParser* p = static_cast<CSSParser*>(parser);
900             $$ = p->createFloatingSelector();
901             $$->m_match = CSSSelector::PseudoClass;
902             $$->m_simpleSelector = p->sinkFloatingSelector($4);
903             $2.lower();
904             $$->m_value = atomicString($2);
905         }
906     }
907   ;
908
909 declaration_list:
910     declaration {
911         $$ = $1;
912     }
913     | decl_list declaration {
914         $$ = $1;
915         if ( $2 )
916             $$ = $2;
917     }
918     | decl_list {
919         $$ = $1;
920     }
921     | error invalid_block_list error {
922         $$ = false;
923     }
924     | error {
925         $$ = false;
926     }
927     | decl_list error {
928         $$ = $1;
929     }
930     ;
931
932 decl_list:
933     declaration ';' maybe_space {
934         $$ = $1;
935     }
936     | declaration invalid_block_list ';' maybe_space {
937         $$ = false;
938     }
939     | error ';' maybe_space {
940         $$ = false;
941     }
942     | error invalid_block_list error ';' maybe_space {
943         $$ = false;
944     }
945     | decl_list declaration ';' maybe_space {
946         $$ = $1;
947         if ($2)
948             $$ = $2;
949     }
950     | decl_list error ';' maybe_space {
951         $$ = $1;
952     }
953     | decl_list error invalid_block_list error ';' maybe_space {
954         $$ = $1;
955     }
956     ;
957
958 declaration:
959     property ':' maybe_space expr prio {
960         $$ = false;
961         CSSParser* p = static_cast<CSSParser*>(parser);
962         if ($1 && $4) {
963             p->valueList = p->sinkFloatingValueList($4);
964             int oldParsedProperties = p->numParsedProperties;
965             $$ = p->parseValue($1, $5);
966             if (!$$)
967                 p->rollbackLastProperties(p->numParsedProperties - oldParsedProperties);
968             delete p->valueList;
969             p->valueList = 0;
970         }
971     }
972     |
973     property error {
974         $$ = false;
975     }
976     |
977     property ':' maybe_space error expr prio {
978         /* The default movable type template has letter-spacing: .none;  Handle this by looking for
979         error tokens at the start of an expr, recover the expr and then treat as an error, cleaning
980         up and deleting the shifted expr.  */
981         $$ = false;
982     }
983     |
984     IMPORTANT_SYM maybe_space {
985         /* Handle this case: div { text-align: center; !important } Just reduce away the stray !important. */
986         $$ = false;
987     }
988     |
989     property ':' maybe_space {
990         /* div { font-family: } Just reduce away this property with no value. */
991         $$ = false;
992     }
993   ;
994
995 property:
996     IDENT maybe_space {
997         $1.lower();
998         DeprecatedString str = deprecatedString($1);
999         const char* s = str.ascii();
1000         int l = str.length();
1001         $$ = getPropertyID(s, l);
1002 #if ENABLE(SVG)
1003         if ($$ == 0)
1004             $$ = SVG::getSVGCSSPropertyID(s, l);
1005 #endif
1006     }
1007   ;
1008
1009 prio:
1010     IMPORTANT_SYM maybe_space { $$ = true; }
1011     | /* empty */ { $$ = false; }
1012   ;
1013
1014 expr:
1015     term {
1016         CSSParser* p = static_cast<CSSParser*>(parser);
1017         $$ = p->createFloatingValueList();
1018         $$->addValue(p->sinkFloatingValue($1));
1019     }
1020     | expr operator term {
1021         CSSParser* p = static_cast<CSSParser*>(parser);
1022         $$ = $1;
1023         if ($$) {
1024             if ($2) {
1025                 Value v;
1026                 v.id = 0;
1027                 v.unit = Value::Operator;
1028                 v.iValue = $2;
1029                 $$->addValue(v);
1030             }
1031             $$->addValue(p->sinkFloatingValue($3));
1032         }
1033     }
1034     | expr error {
1035         $$ = 0;
1036     }
1037   ;
1038
1039 operator:
1040     '/' maybe_space {
1041         $$ = '/';
1042     }
1043   | ',' maybe_space {
1044         $$ = ',';
1045     }
1046   | /* empty */ {
1047         $$ = 0;
1048   }
1049   ;
1050
1051 term:
1052   unary_term { $$ = $1; }
1053   | unary_operator unary_term { $$ = $2; $$.fValue *= $1; }
1054   | STRING maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_STRING; }
1055   | IDENT maybe_space {
1056       DeprecatedString str = deprecatedString($1);
1057       $$.id = getValueID(str.lower().latin1(), str.length());
1058 #if ENABLE(SVG)
1059       if ($$.id == 0)
1060           $$.id = SVG::getSVGCSSValueID(str.lower().latin1(), str.length());
1061 #endif
1062       $$.unit = CSSPrimitiveValue::CSS_IDENT;
1063       $$.string = $1;
1064   }
1065   /* We might need to actually parse the number from a dimension, but we can't just put something that uses $$.string into unary_term. */
1066   | DIMEN maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1067   | unary_operator DIMEN maybe_space { $$.id = 0; $$.string = $2; $$.unit = CSSPrimitiveValue::CSS_DIMENSION }
1068   | URI maybe_space { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_URI; }
1069   | UNICODERANGE maybe_space { $$.id = 0; $$.iValue = 0; $$.unit = CSSPrimitiveValue::CSS_UNKNOWN;/* ### */ }
1070   | hexcolor { $$.id = 0; $$.string = $1; $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; }
1071   | '#' maybe_space { $$.id = 0; $$.string = ParseString(); $$.unit = CSSPrimitiveValue::CSS_RGBCOLOR; } /* Handle error case: "color: #;" */
1072 /* FIXME: according to the specs a function can have a unary_operator in front. I know no case where this makes sense */
1073   | function {
1074       $$ = $1;
1075   }
1076   ;
1077
1078 unary_term:
1079   INTEGER maybe_space { $$.id = 0; $$.isInt = true; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1080   | FLOAT maybe_space { $$.id = 0; $$.isInt = false; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_NUMBER; }
1081   | PERCENTAGE maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PERCENTAGE; }
1082   | PXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PX; }
1083   | CMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_CM; }
1084   | MMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MM; }
1085   | INS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_IN; }
1086   | PTS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PT; }
1087   | PCS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_PC; }
1088   | DEGS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_DEG; }
1089   | RADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_RAD; }
1090   | GRADS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_GRAD; }
1091   | MSECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_MS; }
1092   | SECS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_S; }
1093   | HERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_HZ; }
1094   | KHERZ maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_KHZ; }
1095   | EMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EMS; }
1096   | QEMS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = Value::Q_EMS; }
1097   | EXS maybe_space { $$.id = 0; $$.fValue = $1; $$.unit = CSSPrimitiveValue::CSS_EXS; }
1098     ;
1099
1100
1101 function:
1102     FUNCTION maybe_space expr ')' maybe_space {
1103         CSSParser* p = static_cast<CSSParser*>(parser);
1104         Function* f = p->createFloatingFunction();
1105         f->name = $1;
1106         f->args = p->sinkFloatingValueList($3);
1107         $$.id = 0;
1108         $$.unit = Value::Function;
1109         $$.function = f;
1110     } |
1111     FUNCTION maybe_space error {
1112         CSSParser* p = static_cast<CSSParser*>(parser);
1113         Function* f = p->createFloatingFunction();
1114         f->name = $1;
1115         f->args = 0;
1116         $$.id = 0;
1117         $$.unit = Value::Function;
1118         $$.function = f;
1119   }
1120   ;
1121 /*
1122  * There is a constraint on the color that it must
1123  * have either 3 or 6 hex-digits (i.e., [0-9a-fA-F])
1124  * after the "#"; e.g., "#000" is OK, but "#abcd" is not.
1125  */
1126 hexcolor:
1127   HEX_OR_IDENT maybe_space { $$ = $1; }
1128   ;
1129
1130
1131 /* error handling rules */
1132
1133 invalid_at:
1134     '@' error invalid_block {
1135         $$ = 0;
1136     }
1137   | '@' error ';' {
1138         $$ = 0;
1139     }
1140     ;
1141
1142 invalid_import:
1143     import {
1144         $$ = 0;
1145     }
1146     ;
1147
1148 invalid_rule:
1149     error invalid_block {
1150         $$ = 0;
1151     }
1152 /*
1153   Seems like the two rules below are trying too much and violating
1154   http://www.hixie.ch/tests/evil/mixed/csserrorhandling.html
1155
1156   | error ';' {
1157         $$ = 0;
1158     }
1159   | error '}' {
1160         $$ = 0;
1161     }
1162 */
1163     ;
1164
1165 invalid_block:
1166     '{' error invalid_block_list error '}'
1167   | '{' error '}'
1168     ;
1169
1170 invalid_block_list:
1171     invalid_block
1172   | invalid_block_list error invalid_block
1173 ;
1174
1175 %%