/[suikacvs]/markup/html/whatpm/Whatpm/CSS/Parser.pm
Suika

Contents of /markup/html/whatpm/Whatpm/CSS/Parser.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.10 - (hide annotations) (download)
Tue Jan 1 07:39:05 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.9: +6 -4 lines
++ whatpm/Whatpm/CSS/ChangeLog	1 Jan 2008 07:38:58 -0000
	* Cascade.pm: The |use strict| line was missing!!!!!!!!!11
	(get_computed_value): Support for the |inherit| value.

	* Parser.pm: The |inherit| value is represented by new |INHERIT|
	value type, rather than |KEYWORD|.
	(position): The initial value was incorrect.

2008-01-01  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::CSS::Parser;
2     use strict;
3     use Whatpm::CSS::Tokenizer qw(:token);
4     require Whatpm::CSS::SelectorsParser;
5    
6     sub new ($) {
7 wakaba 1.3 my $self = bless {onerror => sub { }, must_level => 'm',
8 wakaba 1.5 message_level => 'w',
9 wakaba 1.3 unsupported_level => 'unsupported'}, shift;
10 wakaba 1.1
11     return $self;
12     } # new
13    
14     sub BEFORE_STATEMENT_STATE () { 0 }
15     sub BEFORE_DECLARATION_STATE () { 1 }
16     sub IGNORED_STATEMENT_STATE () { 2 }
17     sub IGNORED_DECLARATION_STATE () { 3 }
18    
19 wakaba 1.5 our $Prop; ## By CSS property name
20     our $Attr; ## By CSSOM attribute name
21     our $Key; ## By internal key
22    
23 wakaba 1.1 sub parse_char_string ($$) {
24     my $self = $_[0];
25    
26     my $s = $_[1];
27     pos ($s) = 0;
28 wakaba 1.2 my $line = 1;
29     my $column = 0;
30    
31     my $_onerror = $self->{onerror};
32     my $onerror = sub {
33     $_onerror->(@_, line => $line, column => $column);
34     };
35 wakaba 1.1
36     my $tt = Whatpm::CSS::Tokenizer->new;
37 wakaba 1.2 $tt->{onerror} = $onerror;
38 wakaba 1.1 $tt->{get_char} = sub {
39     if (pos $s < length $s) {
40 wakaba 1.2 my $c = ord substr $s, pos ($s)++, 1;
41     if ($c == 0x000A) {
42     $line++;
43     $column = 0;
44     } elsif ($c == 0x000D) {
45     unless (substr ($s, pos ($s), 1) eq "\x0A") {
46     $line++;
47     $column = 0;
48     } else {
49     $column++;
50     }
51     } else {
52     $column++;
53     }
54     return $c;
55 wakaba 1.1 } else {
56     return -1;
57     }
58     }; # $tt->{get_char}
59     $tt->init;
60    
61     my $sp = Whatpm::CSS::SelectorsParser->new;
62 wakaba 1.2 $sp->{onerror} = $onerror;
63 wakaba 1.1 $sp->{must_level} = $self->{must_level};
64 wakaba 1.2 $sp->{pseudo_element} = $self->{pseudo_element};
65     $sp->{pseudo_class} = $self->{pseudo_class};
66 wakaba 1.1
67 wakaba 1.4 my $nsmap = {};
68     $sp->{lookup_namespace_uri} = sub {
69     return $nsmap->{$_[0]}; # $_[0] is '' (default namespace) or prefix
70     }; # $sp->{lookup_namespace_uri}
71 wakaba 1.1
72     ## TODO: Supported pseudo classes and elements...
73    
74     require Message::DOM::CSSStyleSheet;
75     require Message::DOM::CSSRule;
76     require Message::DOM::CSSStyleDeclaration;
77    
78     my $state = BEFORE_STATEMENT_STATE;
79     my $t = $tt->get_next_token;
80    
81     my $open_rules = [[]];
82     my $current_rules = $open_rules->[-1];
83     my $current_decls;
84     my $closing_tokens = [];
85 wakaba 1.3 my $charset_allowed = 1;
86 wakaba 1.4 my $namespace_allowed = 1;
87 wakaba 1.1
88     S: {
89     if ($state == BEFORE_STATEMENT_STATE) {
90     $t = $tt->get_next_token
91     while $t->{type} == S_TOKEN or
92     $t->{type} == CDO_TOKEN or
93     $t->{type} == CDC_TOKEN;
94    
95     if ($t->{type} == ATKEYWORD_TOKEN) {
96 wakaba 1.5 if (lc $t->{value} eq 'namespace') { ## TODO: case folding
97 wakaba 1.4 $t = $tt->get_next_token;
98     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
99    
100     my $prefix;
101     if ($t->{type} == IDENT_TOKEN) {
102     $prefix = lc $t->{value};
103     ## TODO: Unicode lowercase
104    
105     $t = $tt->get_next_token;
106     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
107     }
108    
109     if ($t->{type} == STRING_TOKEN or $t->{type} == URI_TOKEN) {
110     my $uri = $t->{value};
111    
112     $t = $tt->get_next_token;
113     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
114    
115     ## ISSUE: On handling of empty namespace URI, Firefox 2 and
116     ## Opera 9 work differently (See SuikaWiki:namespace).
117     ## TODO: We need to check what we do once it is specced.
118    
119     if ($t->{type} == SEMICOLON_TOKEN) {
120     if ($namespace_allowed) {
121     $nsmap->{defined $prefix ? $prefix : ''} = $uri;
122     push @$current_rules,
123     Message::DOM::CSSNamespaceRule->____new ($prefix, $uri);
124     undef $charset_allowed;
125     } else {
126     $onerror->(type => 'at:namespace:not allowed',
127     level => $self->{must_level},
128     token => $t);
129     }
130    
131     $t = $tt->get_next_token;
132     ## Stay in the state.
133     redo S;
134     } else {
135     #
136     }
137     } else {
138     #
139     }
140    
141     $onerror->(type => 'syntax error:at:namespace',
142     level => $self->{must_level},
143     token => $t);
144     #
145 wakaba 1.5 } elsif (lc $t->{value} eq 'charset') { ## TODO: case folding
146 wakaba 1.3 $t = $tt->get_next_token;
147     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
148    
149     if ($t->{type} == STRING_TOKEN) {
150     my $encoding = $t->{value};
151    
152     $t = $tt->get_next_token;
153     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
154    
155     if ($t->{type} == SEMICOLON_TOKEN) {
156     if ($charset_allowed) {
157     push @$current_rules,
158     Message::DOM::CSSCharsetRule->____new ($encoding);
159     undef $charset_allowed;
160     } else {
161     $onerror->(type => 'at:charset:not allowed',
162     level => $self->{must_level},
163     token => $t);
164     }
165    
166     ## TODO: Detect the conformance errors for @charset...
167    
168     $t = $tt->get_next_token;
169     ## Stay in the state.
170     redo S;
171     } else {
172     #
173     }
174     } else {
175     #
176     }
177    
178     $onerror->(type => 'syntax error:at:charset',
179     level => $self->{must_level},
180     token => $t);
181 wakaba 1.4 #
182 wakaba 1.3 ## NOTE: When adding support for new at-rule, insert code
183 wakaba 1.4 ## "undef $charset_allowed" and "undef $namespace_token" as
184     ## appropriate.
185 wakaba 1.3 } else {
186     $onerror->(type => 'not supported:at:'.$t->{value},
187     level => $self->{unsupported_level},
188     token => $t);
189     }
190 wakaba 1.1
191     $t = $tt->get_next_token;
192     $state = IGNORED_STATEMENT_STATE;
193     redo S;
194     } elsif (@$open_rules > 1 and $t->{type} == RBRACE_TOKEN) {
195     pop @$open_rules;
196     ## Stay in the state.
197     $t = $tt->get_next_token;
198     redo S;
199     } elsif ($t->{type} == EOF_TOKEN) {
200     if (@$open_rules > 1) {
201 wakaba 1.2 $onerror->(type => 'syntax error:block not closed',
202     level => $self->{must_level},
203     token => $t);
204 wakaba 1.1 }
205    
206     last S;
207     } else {
208 wakaba 1.3 undef $charset_allowed;
209 wakaba 1.4 undef $namespace_allowed;
210 wakaba 1.3
211 wakaba 1.1 ($t, my $selectors) = $sp->_parse_selectors_with_tokenizer
212     ($tt, LBRACE_TOKEN, $t);
213    
214     $t = $tt->get_next_token
215     while $t->{type} != LBRACE_TOKEN and $t->{type} != EOF_TOKEN;
216    
217     if ($t->{type} == LBRACE_TOKEN) {
218     $current_decls = Message::DOM::CSSStyleDeclaration->____new;
219     my $rs = Message::DOM::CSSStyleRule->____new
220     ($selectors, $current_decls);
221     push @{$current_rules}, $rs if defined $selectors;
222    
223     $state = BEFORE_DECLARATION_STATE;
224     $t = $tt->get_next_token;
225     redo S;
226     } else {
227 wakaba 1.2 $onerror->(type => 'syntax error:after selectors',
228     level => $self->{must_level},
229     token => $t);
230 wakaba 1.1
231     ## Stay in the state.
232     $t = $tt->get_next_token;
233     redo S;
234     }
235     }
236     } elsif ($state == BEFORE_DECLARATION_STATE) {
237     ## NOTE: DELIM? in declaration will be removed:
238     ## <http://csswg.inkedblade.net/spec/css2.1?s=declaration%20delim#issue-2>.
239    
240 wakaba 1.5 my $prop_def;
241     my $prop_value;
242     my $prop_flag;
243 wakaba 1.1 $t = $tt->get_next_token while $t->{type} == S_TOKEN;
244     if ($t->{type} == IDENT_TOKEN) { # property
245 wakaba 1.5 my $prop_name = lc $t->{value}; ## TODO: case folding
246     $t = $tt->get_next_token;
247     if ($t->{type} == COLON_TOKEN) {
248     $t = $tt->get_next_token;
249     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
250    
251     $prop_def = $Prop->{$prop_name};
252 wakaba 1.6 if ($prop_def and $self->{prop}->{$prop_name}) {
253 wakaba 1.5 ($t, $prop_value)
254     = $prop_def->{parse}->($self, $prop_name, $tt, $t, $onerror);
255     if ($prop_value) {
256     ## NOTE: {parse} don't have to consume trailing spaces.
257     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
258    
259     if ($t->{type} == EXCLAMATION_TOKEN) {
260     $t = $tt->get_next_token;
261     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
262     if ($t->{type} == IDENT_TOKEN and
263     lc $t->{value} eq 'important') { ## TODO: case folding
264     $prop_flag = 'important';
265    
266     $t = $tt->get_next_token;
267     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
268    
269     #
270     } else {
271     $onerror->(type => 'syntax error:important',
272     level => $self->{must_level},
273     token => $t);
274    
275     ## Reprocess.
276     $state = IGNORED_DECLARATION_STATE;
277     redo S;
278     }
279     }
280    
281     #
282     } else {
283     ## Syntax error.
284    
285     ## Reprocess.
286     $state = IGNORED_DECLARATION_STATE;
287     redo S;
288     }
289     } else {
290     $onerror->(type => 'not supported:property',
291     level => $self->{unsupported_level},
292     token => $t, value => $prop_name);
293    
294     #
295     $state = IGNORED_DECLARATION_STATE;
296     redo S;
297     }
298     } else {
299     $onerror->(type => 'syntax error:property colon',
300     level => $self->{must_level},
301     token => $t);
302 wakaba 1.1
303 wakaba 1.5 #
304     $state = IGNORED_DECLARATION_STATE;
305     redo S;
306     }
307     }
308    
309     if ($t->{type} == RBRACE_TOKEN) {
310 wakaba 1.1 $t = $tt->get_next_token;
311 wakaba 1.5 $state = BEFORE_STATEMENT_STATE;
312     #redo S;
313     } elsif ($t->{type} == SEMICOLON_TOKEN) {
314 wakaba 1.1 $t = $tt->get_next_token;
315 wakaba 1.5 ## Stay in the state.
316     #redo S;
317 wakaba 1.1 } elsif ($t->{type} == EOF_TOKEN) {
318 wakaba 1.2 $onerror->(type => 'syntax error:ruleset not closed',
319     level => $self->{must_level},
320     token => $t);
321 wakaba 1.1 ## Reprocess.
322     $state = BEFORE_STATEMENT_STATE;
323 wakaba 1.5 #redo S;
324     } else {
325     if ($prop_value) {
326     $onerror->(type => 'syntax error:property semicolon',
327     level => $self->{must_level},
328     token => $t);
329     } else {
330     $onerror->(type => 'syntax error:property name',
331     level => $self->{must_level},
332     token => $t);
333     }
334    
335     #
336     $state = IGNORED_DECLARATION_STATE;
337 wakaba 1.1 redo S;
338     }
339    
340 wakaba 1.7 my $important = (defined $prop_flag and $prop_flag eq 'important');
341     for my $set_prop_name (keys %{$prop_value or {}}) {
342     my $set_prop_def = $Prop->{$set_prop_name};
343     $$current_decls->{$set_prop_def->{key}}
344     = [$prop_value->{$set_prop_name}, $prop_flag]
345     if $important or
346     not $$current_decls->{$set_prop_def->{key}} or
347     not defined $$current_decls->{$set_prop_def->{key}}->[1];
348 wakaba 1.5 }
349 wakaba 1.1 redo S;
350     } elsif ($state == IGNORED_STATEMENT_STATE or
351     $state == IGNORED_DECLARATION_STATE) {
352     if (@$closing_tokens) { ## Something is yet in opening state.
353     if ($t->{type} == EOF_TOKEN) {
354     @$closing_tokens = ();
355     ## Reprocess.
356     $state = $state == IGNORED_STATEMENT_STATE
357     ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;
358     redo S;
359     } elsif ($t->{type} == $closing_tokens->[-1]) {
360     pop @$closing_tokens;
361     if (@$closing_tokens == 0 and
362     $t->{type} == RBRACE_TOKEN and
363     $state == IGNORED_STATEMENT_STATE) {
364     $t = $tt->get_next_token;
365     $state = BEFORE_STATEMENT_STATE;
366     redo S;
367     } else {
368     $t = $tt->get_next_token;
369     ## Stay in the state.
370     redo S;
371     }
372     } else {
373     #
374     }
375     } else {
376     if ($t->{type} == SEMICOLON_TOKEN) {
377     $t = $tt->get_next_token;
378     $state = $state == IGNORED_STATEMENT_STATE
379     ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;
380     redo S;
381     } elsif ($state == IGNORED_DECLARATION_STATE and
382     $t->{type} == RBRACE_TOKEN) {
383     $t = $tt->get_next_token;
384     $state = BEFORE_STATEMENT_STATE;
385     redo S;
386     } elsif ($t->{type} == EOF_TOKEN) {
387     ## Reprocess.
388     $state = $state == IGNORED_STATEMENT_STATE
389     ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;
390     redo S;
391     } else {
392     #
393     }
394     }
395    
396     while (not {
397     EOF_TOKEN, 1,
398     RBRACE_TOKEN, 1,
399     RBRACKET_TOKEN, 1,
400     RPAREN_TOKEN, 1,
401     SEMICOLON_TOKEN, 1,
402     }->{$t->{type}}) {
403     if ($t->{type} == LBRACE_TOKEN) {
404     push @$closing_tokens, RBRACE_TOKEN;
405     } elsif ($t->{type} == LBRACKET_TOKEN) {
406     push @$closing_tokens, RBRACKET_TOKEN;
407     } elsif ($t->{type} == LPAREN_TOKEN or $t->{type} == FUNCTION_TOKEN) {
408     push @$closing_tokens, RPAREN_TOKEN;
409     }
410    
411     $t = $tt->get_next_token;
412     }
413    
414     #
415     ## Stay in the state.
416     redo S;
417     } else {
418     die "$0: parse_char_string: Unknown state: $state";
419     }
420     } # S
421    
422     my $ss = Message::DOM::CSSStyleSheet->____new
423     (css_rules => $open_rules->[0],
424     ## TODO: href
425     ## TODO: owner_node
426     ## TODO: media
427     type => 'text/css', ## TODO: OK?
428     _parser => $self);
429     return $ss;
430     } # parse_char_string
431    
432 wakaba 1.9 my $compute_as_specified = sub ($$$$) {
433     #my ($self, $element, $prop_name, $specified_value) = @_;
434     return $_[3];
435     }; # $compute_as_specified
436    
437 wakaba 1.5 $Prop->{color} = {
438     css => 'color',
439     dom => 'color',
440     key => 'color',
441     parse => sub {
442     my ($self, $prop_name, $tt, $t, $onerror) = @_;
443    
444     if ($t->{type} == IDENT_TOKEN) {
445     if (lc $t->{value} eq 'blue') { ## TODO: case folding
446     $t = $tt->get_next_token;
447 wakaba 1.7 return ($t, {$prop_name => ["RGBA", 0, 0, 255, 1]});
448 wakaba 1.5 } else {
449     #
450     }
451     } else {
452     #
453     }
454    
455     $onerror->(type => 'syntax error:color',
456     level => $self->{must_level},
457     token => $t);
458    
459     return ($t, undef);
460     },
461     serialize => sub {
462     my ($self, $prop_name, $value) = @_;
463     if ($value->[0] eq 'RGBA') { ## TODO: %d? %f?
464     return sprintf 'rgba(%d, %d, %d, %f)', @$value[1, 2, 3, 4];
465     } else {
466     return undef;
467     }
468     },
469 wakaba 1.9 initial => ["KEYWORD", "-manakai-initial-color"], ## NOTE: UA-dependent in CSS 2.1.
470     inherited => 1,
471     compute => $compute_as_specified,
472 wakaba 1.5 };
473     $Attr->{color} = $Prop->{color};
474     $Key->{color} = $Prop->{color};
475    
476 wakaba 1.6 my $one_keyword_parser = sub {
477     my ($self, $prop_name, $tt, $t, $onerror) = @_;
478    
479     if ($t->{type} == IDENT_TOKEN) {
480     my $prop_value = lc $t->{value}; ## TODO: case folding
481     $t = $tt->get_next_token;
482     if ($Prop->{$prop_name}->{keyword}->{$prop_value} and
483     $self->{prop_value}->{$prop_name}->{$prop_value}) {
484 wakaba 1.7 return ($t, {$prop_name => ["KEYWORD", $prop_value]});
485 wakaba 1.6 } elsif ($prop_value eq 'inherit') {
486 wakaba 1.10 return ($t, {$prop_name => ['INHERIT']});
487 wakaba 1.6 }
488     }
489    
490 wakaba 1.7 $onerror->(type => 'syntax error:keyword:'.$prop_name,
491 wakaba 1.6 level => $self->{must_level},
492     token => $t);
493     return ($t, undef);
494     };
495    
496     my $one_keyword_serializer = sub {
497     my ($self, $prop_name, $value) = @_;
498     if ($value->[0] eq 'KEYWORD') {
499     return $value->[1];
500 wakaba 1.10 } elsif ($value->[0] eq 'INHERIT') {
501     return 'inherit';
502 wakaba 1.6 } else {
503     return undef;
504     }
505     };
506    
507     $Prop->{display} = {
508     css => 'display',
509     dom => 'display',
510     key => 'display',
511     parse => $one_keyword_parser,
512     serialize => $one_keyword_serializer,
513     keyword => {
514     block => 1, inline => 1, 'inline-block' => 1, 'inline-table' => 1,
515     'list-item' => 1, none => 1,
516     table => 1, 'table-caption' => 1, 'table-cell' => 1, 'table-column' => 1,
517     'table-column-group' => 1, 'table-header-group' => 1,
518     'table-footer-group' => 1, 'table-row' => 1, 'table-row-group' => 1,
519     },
520 wakaba 1.9 initial => ["KEYWORD", "inline"],
521     #inherited => 0,
522     compute => sub {
523     my ($self, $element, $prop_name, $specified_value) = @_;
524     ## NOTE: CSS 2.1 Section 9.7.
525    
526     ## WARNING: |compute| for 'float' property invoke this CODE
527     ## in some case. Careless modification might cause a infinite loop.
528    
529     if ($specified_value->[0] eq 'KEYWORD') {
530     if ($specified_value->[1] eq 'none') {
531     ## Case 1 [CSS 2.1]
532     return $specified_value;
533     } else {
534     my $position = $self->get_computed_value ($element, 'position');
535     if ($position->[0] eq 'KEYWORD' and
536     ($position->[1] eq 'absolute' or
537     $position->[1] eq 'fixed')) {
538     ## Case 2 [CSS 2.1]
539     #
540     } else {
541     my $float = $self->get_computed_value ($element, 'float');
542     if ($float->[0] eq 'KEYWORD' and $float->[1] ne 'none') {
543     ## Caes 3 [CSS 2.1]
544     #
545     } elsif (not defined $element->manakai_parent_element) {
546     ## Case 4 [CSS 2.1]
547     #
548     } else {
549     ## Case 5 [CSS 2.1]
550     return $specified_value;
551     }
552     }
553    
554     return ["KEYWORD",
555     {
556     'inline-table' => 'table',
557     inline => 'block',
558     'run-in' => 'block',
559     'table-row-group' => 'block',
560     'table-column' => 'block',
561     'table-column-group' => 'block',
562     'table-header-group' => 'block',
563     'table-footer-group' => 'block',
564     'table-row' => 'block',
565     'table-cell' => 'block',
566     'table-caption' => 'block',
567     'inline-block' => 'block',
568     }->{$specified_value->[1]} || $specified_value->[1]];
569     }
570     } else {
571     return $specified_value; ## Maybe an error of the implementation.
572     }
573     },
574 wakaba 1.6 };
575     $Attr->{display} = $Prop->{display};
576     $Key->{display} = $Prop->{display};
577    
578     $Prop->{position} = {
579     css => 'position',
580     dom => 'position',
581     key => 'position',
582     parse => $one_keyword_parser,
583     serialize => $one_keyword_serializer,
584     keyword => {
585     static => 1, relative => 1, absolute => 1, fixed => 1,
586     },
587 wakaba 1.10 initial => ["KEYWORD", "static"],
588 wakaba 1.9 #inherited => 0,
589     compute => $compute_as_specified,
590 wakaba 1.6 };
591     $Attr->{position} = $Prop->{position};
592     $Key->{position} = $Prop->{position};
593    
594     $Prop->{float} = {
595     css => 'float',
596     dom => 'css_float',
597     key => 'float',
598     parse => $one_keyword_parser,
599     serialize => $one_keyword_serializer,
600     keyword => {
601     left => 1, right => 1, none => 1,
602     },
603 wakaba 1.9 initial => ["KEYWORD", "none"],
604     #inherited => 0,
605     compute => sub {
606     my ($self, $element, $prop_name, $specified_value) = @_;
607     ## NOTE: CSS 2.1 Section 9.7.
608    
609     ## WARNING: |compute| for 'display' property invoke this CODE
610     ## in some case. Careless modification might cause a infinite loop.
611    
612     if ($specified_value->[0] eq 'KEYWORD') {
613     if ($specified_value->[1] eq 'none') {
614     ## Case 1 [CSS 2.1]
615     return $specified_value;
616     } else {
617     my $position = $self->get_computed_value ($element, 'position');
618     if ($position->[0] eq 'KEYWORD' and
619     ($position->[1] eq 'absolute' or
620     $position->[1] eq 'fixed')) {
621     ## Case 2 [CSS 2.1]
622     return ["KEYWORD", "none"];
623     }
624     }
625     }
626    
627     ## ISSUE: CSS 2.1 section 9.7 and 9.5.1 ('float' definition) disagree
628     ## on computed value of 'float' property.
629    
630     ## Case 3, 4, and 5 [CSS 2.1]
631     return $specified_value;
632     },
633 wakaba 1.6 };
634     $Attr->{css_float} = $Prop->{float};
635     $Attr->{style_float} = $Prop->{float}; ## NOTE: IEism
636     $Key->{float} = $Prop->{float};
637    
638     $Prop->{clear} = {
639     css => 'clear',
640     dom => 'clear',
641     key => 'clear',
642     parse => $one_keyword_parser,
643     serialize => $one_keyword_serializer,
644     keyword => {
645     left => 1, right => 1, none => 1, both => 1,
646     },
647 wakaba 1.9 initial => ["KEYWORD", "none"],
648     #inherited => 0,
649     compute => $compute_as_specified,
650 wakaba 1.6 };
651     $Attr->{clear} = $Prop->{clear};
652     $Key->{clear} = $Prop->{clear};
653    
654     $Prop->{direction} = {
655     css => 'direction',
656     dom => 'direction',
657     key => 'direction',
658     parse => $one_keyword_parser,
659     serialize => $one_keyword_serializer,
660     keyword => {
661     ltr => 1, rtl => 1,
662     },
663 wakaba 1.9 initial => ["KEYWORD", "ltr"],
664     inherited => 1,
665     compute => $compute_as_specified,
666 wakaba 1.6 };
667     $Attr->{direction} = $Prop->{direction};
668     $Key->{direction} = $Prop->{direction};
669    
670     $Prop->{'unicode-bidi'} = {
671     css => 'unicode-bidi',
672     dom => 'unicode_bidi',
673     key => 'unicode_bidi',
674     parse => $one_keyword_parser,
675     serialize => $one_keyword_serializer,
676     keyword => {
677     normal => 1, embed => 1, 'bidi-override' => 1,
678     },
679 wakaba 1.9 initial => ["KEYWORD", "normal"],
680     #inherited => 0,
681     compute => $compute_as_specified,
682 wakaba 1.6 };
683     $Attr->{unicode_bidi} = $Prop->{'unicode-bidi'};
684     $Key->{unicode_bidi} = $Prop->{'unicode-bidi'};
685    
686 wakaba 1.7 my $border_style_keyword = {
687     none => 1, hidden => 1, dotted => 1, dashed => 1, solid => 1,
688     double => 1, groove => 1, ridge => 1, inset => 1, outset => 1,
689     };
690    
691     $Prop->{'border-top-style'} = {
692     css => 'border-top-style',
693     dom => 'border_top_style',
694     key => 'border_top_style',
695     parse => $one_keyword_parser,
696     serialize => $one_keyword_serializer,
697     keyword => $border_style_keyword,
698 wakaba 1.9 initial => ["KEYWORD", "none"],
699     #inherited => 0,
700     compute => $compute_as_specified,
701 wakaba 1.7 };
702     $Attr->{border_top_style} = $Prop->{'border-top-style'};
703     $Key->{border_top_style} = $Prop->{'border-top-style'};
704    
705     $Prop->{'border-right-style'} = {
706     css => 'border-right-style',
707     dom => 'border_right_style',
708     key => 'border_right_style',
709     parse => $one_keyword_parser,
710     serialize => $one_keyword_serializer,
711     keyword => $border_style_keyword,
712 wakaba 1.9 initial => ["KEYWORD", "none"],
713     #inherited => 0,
714     compute => $compute_as_specified,
715 wakaba 1.7 };
716     $Attr->{border_right_style} = $Prop->{'border-right-style'};
717     $Key->{border_right_style} = $Prop->{'border-right-style'};
718    
719     $Prop->{'border-bottom-style'} = {
720     css => 'border-bottom-style',
721     dom => 'border_bottom_style',
722     key => 'border_bottom_style',
723     parse => $one_keyword_parser,
724     serialize => $one_keyword_serializer,
725     keyword => $border_style_keyword,
726 wakaba 1.9 initial => ["KEYWORD", "none"],
727     #inherited => 0,
728     compute => $compute_as_specified,
729 wakaba 1.7 };
730     $Attr->{border_bottom_style} = $Prop->{'border-bottom-style'};
731     $Key->{border_bottom_style} = $Prop->{'border-bottom-style'};
732    
733     $Prop->{'border-left-style'} = {
734     css => 'border-left-style',
735     dom => 'border_left_style',
736     key => 'border_left_style',
737     parse => $one_keyword_parser,
738     serialize => $one_keyword_serializer,
739     keyword => $border_style_keyword,
740 wakaba 1.9 initial => ["KEYWORD", "none"],
741     #inherited => 0,
742     compute => $compute_as_specified,
743 wakaba 1.7 };
744     $Attr->{border_left_style} = $Prop->{'border-left-style'};
745     $Key->{border_left_style} = $Prop->{'border-left-style'};
746    
747     $Prop->{'border-style'} = {
748     css => 'border-style',
749     dom => 'border_style',
750     parse => sub {
751     my ($self, $prop_name, $tt, $t, $onerror) = @_;
752    
753     my %prop_value;
754     my $has_inherit;
755     if ($t->{type} == IDENT_TOKEN) {
756     my $prop_value = lc $t->{value}; ## TODO: case folding
757     $t = $tt->get_next_token;
758     if ($border_style_keyword->{$prop_value} and
759     $self->{prop_value}->{'border-top-style'}->{$prop_value}) {
760     $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];
761     } elsif ($prop_value eq 'inherit') {
762 wakaba 1.10 $prop_value{'border-top-style'} = ["INHERIT"];
763 wakaba 1.7 $has_inherit = 1;
764     } else {
765     $onerror->(type => 'syntax error:keyword:'.$prop_name,
766     level => $self->{must_level},
767     token => $t);
768     return ($t, undef);
769     }
770     $prop_value{'border-right-style'} = $prop_value{'border-top-style'};
771     $prop_value{'border-bottom-style'} = $prop_value{'border-top-style'};
772     $prop_value{'border-left-style'} = $prop_value{'border-right-style'};
773     } else {
774     $onerror->(type => 'syntax error:keyword:'.$prop_name,
775     level => $self->{must_level},
776     token => $t);
777     return ($t, undef);
778     }
779    
780     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
781     if ($t->{type} == IDENT_TOKEN) {
782     my $prop_value = lc $t->{value}; ## TODO: case folding
783     $t = $tt->get_next_token;
784     if (not $has_inherit and
785     $border_style_keyword->{$prop_value} and
786     $self->{prop_value}->{'border-right-style'}->{$prop_value}) {
787     $prop_value{'border-right-style'} = ["KEYWORD", $prop_value];
788     } else {
789     $onerror->(type => 'syntax error:keyword:'.$prop_name,
790     level => $self->{must_level},
791     token => $t);
792     return ($t, undef);
793     }
794     $prop_value{'border-left-style'} = $prop_value{'border-right-style'};
795    
796     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
797     if ($t->{type} == IDENT_TOKEN) {
798     my $prop_value = lc $t->{value}; ## TODO: case folding
799     $t = $tt->get_next_token;
800     if ($border_style_keyword->{$prop_value} and
801     $self->{prop_value}->{'border-bottom-style'}->{$prop_value}) {
802     $prop_value{'border-bottom-style'} = ["KEYWORD", $prop_value];
803     } else {
804     $onerror->(type => 'syntax error:keyword:'.$prop_name,
805     level => $self->{must_level},
806     token => $t);
807     return ($t, undef);
808     }
809    
810     $t = $tt->get_next_token while $t->{type} == S_TOKEN;
811     if ($t->{type} == IDENT_TOKEN) {
812     my $prop_value = lc $t->{value}; ## TODO: case folding
813     $t = $tt->get_next_token;
814     if ($border_style_keyword->{$prop_value} and
815     $self->{prop_value}->{'border-left-style'}->{$prop_value}) {
816     $prop_value{'border-left-style'} = ["KEYWORD", $prop_value];
817     } else {
818     $onerror->(type => 'syntax error:keyword:'.$prop_name,
819     level => $self->{must_level},
820     token => $t);
821     return ($t, undef);
822     }
823     }
824     }
825     }
826    
827     return ($t, \%prop_value);
828     },
829     serialize => sub {
830     my ($self, $prop_name, $value) = @_;
831    
832     local $Error::Depth = $Error::Depth + 1;
833     my @v;
834     push @v, $self->border_top_style;
835     return undef unless defined $v[-1];
836     push @v, $self->border_right_style;
837     return undef unless defined $v[-1];
838     push @v, $self->border_bottom_style;
839     return undef unless defined $v[-1];
840     push @v, $self->border_bottom_style;
841     return undef unless defined $v[-1];
842    
843     pop @v if $v[1] eq $v[3];
844     pop @v if $v[0] eq $v[2];
845     pop @v if $v[0] eq $v[1];
846     return join ' ', @v;
847     },
848     };
849     $Attr->{border_style} = $Prop->{'border-style'};
850    
851 wakaba 1.1 1;
852 wakaba 1.10 ## $Date: 2008/01/01 07:07:28 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24