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

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

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.15 by wakaba, Tue Jan 1 15:43:47 2008 UTC revision 1.42 by wakaba, Thu Jan 24 13:09:00 2008 UTC
# Line 4  use Whatpm::CSS::Tokenizer qw(:token); Line 4  use Whatpm::CSS::Tokenizer qw(:token);
4  require Whatpm::CSS::SelectorsParser;  require Whatpm::CSS::SelectorsParser;
5    
6  sub new ($) {  sub new ($) {
7    my $self = bless {onerror => sub { }, must_level => 'm',    my $self = bless {must_level => 'm',
8                      message_level => 'w',                      message_level => 'w',
9                      unsupported_level => 'unsupported'}, shift;                      unsupported_level => 'unsupported'}, shift;
10    # $self->{base_uri}    # $self->{base_uri}
11      # $self->{unitless_px} = 1/0
12      # $self->{hashless_rgb} = 1/0
13    
14      ## Default error handler
15      $self->{onerror} = sub {
16        my %opt = @_;
17        require Carp;
18        Carp::carp
19            (sprintf 'Document <%s>: Line %d column %d (token %s): %s%s',
20                 defined $opt{uri} ? ${$opt{uri}} : 'thisdocument:/',
21                 $opt{token}->{line},
22                 $opt{token}->{column},
23                 Whatpm::CSS::Tokenizer->serialize_token ($opt{token}),
24                 $opt{type},
25                 defined $opt{value} ? " (value $opt{value})" : '');
26      };
27    
28      ## Media-dependent RGB color range clipper
29      $self->{clip_color} = sub {
30        shift; #my $self = shift;
31        my $value = shift;
32        if (defined $value and $value->[0] eq 'RGBA') {
33          my ($r, $g, $b) = @$value[1, 2, 3];
34          $r = 0 if $r < 0;  $r = 255 if $r > 255;
35          $g = 0 if $g < 0;  $g = 255 if $g > 255;
36          $b = 0 if $b < 0;  $b = 255 if $b > 255;
37          return ['RGBA', $r, $g, $b, $value->[4]];
38        }
39        return $value;
40      };
41    
42      ## System dependent font expander
43      $self->{get_system_font} = sub {
44        #my ($self, $normalized_system_font_name, $font_properties) = @_;
45        ## Modify $font_properties hash (except for 'font-family' property).
46        return $_[2];
47      };
48    
49    return $self;    return $self;
50  } # new  } # new
# Line 28  sub parse_char_string ($$) { Line 65  sub parse_char_string ($$) {
65    pos ($s) = 0;    pos ($s) = 0;
66    my $line = 1;    my $line = 1;
67    my $column = 0;    my $column = 0;
   
   my $_onerror = $self->{onerror};  
   my $onerror = sub {  
     $_onerror->(@_, line => $line, column => $column);  
   };  
68        
69    my $tt = Whatpm::CSS::Tokenizer->new;    my $tt = Whatpm::CSS::Tokenizer->new;
70    $tt->{onerror} = $onerror;    my $onerror = $tt->{onerror} = $self->{onerror};
71    $tt->{get_char} = sub {    $tt->{get_char} = sub ($) {
72      if (pos $s < length $s) {      if (pos $s < length $s) {
73        my $c = ord substr $s, pos ($s)++, 1;        my $c = ord substr $s, pos ($s)++, 1;
74        if ($c == 0x000A) {        if ($c == 0x000A) {
# Line 52  sub parse_char_string ($$) { Line 84  sub parse_char_string ($$) {
84        } else {        } else {
85          $column++;          $column++;
86        }        }
87          ## TODO: $tt -> $_[0]
88          $tt->{line} = $line;
89          $tt->{column} = $column;
90        return $c;        return $c;
91      } else {      } else {
92          $tt->{column} = $column + 1; ## Set the same number always.
93        return -1;        return -1;
94      }      }
95    }; # $tt->{get_char}    }; # $tt->{get_char}
96    $tt->init;    $tt->init;
97    
98    my $sp = Whatpm::CSS::SelectorsParser->new;    my $sp = Whatpm::CSS::SelectorsParser->new;
99    $sp->{onerror} = $onerror;    $sp->{onerror} = $self->{onerror};
100    $sp->{must_level} = $self->{must_level};    $sp->{must_level} = $self->{must_level};
101    $sp->{pseudo_element} = $self->{pseudo_element};    $sp->{pseudo_element} = $self->{pseudo_element};
102    $sp->{pseudo_class} = $self->{pseudo_class};    $sp->{pseudo_class} = $self->{pseudo_class};
103    
104    my $nsmap = {};    my $nsmap = {prefix_to_uri => {}, uri_to_prefixes => {}};
105      # $nsmap->{prefix_to_uri}->{p/""} = uri/undef
106      # $nsmap->{uri_to_prefixes}->{uri} = ["p|"/"",...]/undef
107      # $nsmap->{has_namespace} = 1/0
108    $sp->{lookup_namespace_uri} = sub {    $sp->{lookup_namespace_uri} = sub {
109      return $nsmap->{$_[0]}; # $_[0] is '' (default namespace) or prefix      return $nsmap->{prefix_to_uri}->{$_[0]}; # $_[0] is '' (default) or prefix
110    }; # $sp->{lookup_namespace_uri}    }; # $sp->{lookup_namespace_uri}
111    
   ## TODO: Supported pseudo classes and elements...  
   
112    require Message::DOM::CSSStyleSheet;    require Message::DOM::CSSStyleSheet;
113    require Message::DOM::CSSRule;    require Message::DOM::CSSRule;
114    require Message::DOM::CSSStyleDeclaration;    require Message::DOM::CSSStyleDeclaration;
115    
116    $self->{base_uri} = $self->{href} unless defined $self->{base_uri};    $self->{base_uri} = $self->{href} unless defined $self->{base_uri};
117      $sp->{href} = $self->{href};
118    
119    my $state = BEFORE_STATEMENT_STATE;    my $state = BEFORE_STATEMENT_STATE;
120    my $t = $tt->get_next_token;    my $t = $tt->get_next_token;
# Line 103  sub parse_char_string ($$) { Line 141  sub parse_char_string ($$) {
141            my $prefix;            my $prefix;
142            if ($t->{type} == IDENT_TOKEN) {            if ($t->{type} == IDENT_TOKEN) {
143              $prefix = lc $t->{value};              $prefix = lc $t->{value};
144              ## TODO: Unicode lowercase              ## TODO: case (Unicode lowercase)
145                            
146              $t = $tt->get_next_token;              $t = $tt->get_next_token;
147              $t = $tt->get_next_token while $t->{type} == S_TOKEN;              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
# Line 121  sub parse_char_string ($$) { Line 159  sub parse_char_string ($$) {
159    
160              if ($t->{type} == SEMICOLON_TOKEN) {              if ($t->{type} == SEMICOLON_TOKEN) {
161                if ($namespace_allowed) {                if ($namespace_allowed) {
162                  $nsmap->{defined $prefix ? $prefix : ''} = $uri;                  my $p = $prefix;
163                    $nsmap->{has_namespace} = 1;
164                    if (defined $prefix) {
165                      $nsmap->{prefix_to_uri}->{$prefix} = $uri;
166                      $p .= '|';
167                    } else {
168                      $nsmap->{prefix_to_uri}->{''} = $uri;
169                      $p = '';
170                    }
171                    for my $u (keys %{$nsmap->{uri_to_prefixes}}) {
172                      next if $u eq $uri;
173                      my $list = $nsmap->{uri_to_prefixes}->{$u};
174                      next unless $list;
175                      for (reverse 0..$#$list) {
176                        splice @$list, $_, 1, () if $list->[$_] eq $p;
177                      }
178                    }
179                    push @{$nsmap->{uri_to_prefixes}->{$uri} ||= []}, $p;
180                  push @$current_rules,                  push @$current_rules,
181                      Message::DOM::CSSNamespaceRule->____new ($prefix, $uri);                      Message::DOM::CSSNamespaceRule->____new ($prefix, $uri);
182                  undef $charset_allowed;                  undef $charset_allowed;
183                } else {                } else {
184                  $onerror->(type => 'at:namespace:not allowed',                  $onerror->(type => 'at-rule not allowed:namespace',
185                             level => $self->{must_level},                             level => $self->{must_level},
186                               uri => \$self->{href},
187                             token => $t);                             token => $t);
188                }                }
189                                
# Line 141  sub parse_char_string ($$) { Line 197  sub parse_char_string ($$) {
197              #              #
198            }            }
199    
200            $onerror->(type => 'syntax error:at:namespace',            $onerror->(type => 'at-rule syntax error:namespace',
201                       level => $self->{must_level},                       level => $self->{must_level},
202                         uri => \$self->{href},
203                       token => $t);                       token => $t);
204            #            #
205          } elsif (lc $t->{value} eq 'charset') { ## TODO: case folding          } elsif (lc $t->{value} eq 'charset') { ## TODO: case folding
# Line 161  sub parse_char_string ($$) { Line 218  sub parse_char_string ($$) {
218                      Message::DOM::CSSCharsetRule->____new ($encoding);                      Message::DOM::CSSCharsetRule->____new ($encoding);
219                  undef $charset_allowed;                  undef $charset_allowed;
220                } else {                } else {
221                   $onerror->(type => 'at:charset:not allowed',                   $onerror->(type => 'at-rule not allowed:charset',
222                              level => $self->{must_level},                              level => $self->{must_level},
223                                uri => \$self->{href},
224                              token => $t);                              token => $t);
225                }                }
226    
# Line 178  sub parse_char_string ($$) { Line 236  sub parse_char_string ($$) {
236              #              #
237            }            }
238    
239            $onerror->(type => 'syntax error:at:charset',            $onerror->(type => 'at-rule syntax error:charset',
240                       level => $self->{must_level},                       level => $self->{must_level},
241                         uri => \$self->{href},
242                       token => $t);                       token => $t);
243            #            #
244          ## NOTE: When adding support for new at-rule, insert code          ## NOTE: When adding support for new at-rule, insert code
245          ## "undef $charset_allowed" and "undef $namespace_token" as          ## "undef $charset_allowed" and "undef $namespace_token" as
246          ## appropriate.          ## appropriate.
247          } else {          } else {
248            $onerror->(type => 'not supported:at:'.$t->{value},            $onerror->(type => 'at-rule not supported',
249                       level => $self->{unsupported_level},                       level => $self->{unsupported_level},
250                       token => $t);                       uri => \$self->{href},
251                         token => $t,
252                         value => $t->{value});
253          }          }
254    
255          $t = $tt->get_next_token;          $t = $tt->get_next_token;
# Line 201  sub parse_char_string ($$) { Line 262  sub parse_char_string ($$) {
262          redo S;          redo S;
263        } elsif ($t->{type} == EOF_TOKEN) {        } elsif ($t->{type} == EOF_TOKEN) {
264          if (@$open_rules > 1) {          if (@$open_rules > 1) {
265            $onerror->(type => 'syntax error:block not closed',            $onerror->(type => 'block not closed',
266                       level => $self->{must_level},                       level => $self->{must_level},
267                         uri => \$self->{href},
268                       token => $t);                       token => $t);
269          }          }
270    
# Line 227  sub parse_char_string ($$) { Line 289  sub parse_char_string ($$) {
289            $t = $tt->get_next_token;            $t = $tt->get_next_token;
290            redo S;            redo S;
291          } else {          } else {
292            $onerror->(type => 'syntax error:after selectors',            $onerror->(type => 'no declaration block',
293                       level => $self->{must_level},                       level => $self->{must_level},
294                         uri => \$self->{href},
295                       token => $t);                       token => $t);
296    
297            ## Stay in the state.            ## Stay in the state.
# Line 242  sub parse_char_string ($$) { Line 305  sub parse_char_string ($$) {
305    
306        my $prop_def;        my $prop_def;
307        my $prop_value;        my $prop_value;
308        my $prop_flag;        my $prop_flag = '';
309        $t = $tt->get_next_token while $t->{type} == S_TOKEN;        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
310        if ($t->{type} == IDENT_TOKEN) { # property        if ($t->{type} == IDENT_TOKEN) { # property
311          my $prop_name = lc $t->{value}; ## TODO: case folding          my $prop_name = lc $t->{value}; ## TODO: case folding
312          $t = $tt->get_next_token;          $t = $tt->get_next_token;
313            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
314          if ($t->{type} == COLON_TOKEN) {          if ($t->{type} == COLON_TOKEN) {
315            $t = $tt->get_next_token;            $t = $tt->get_next_token;
316            $t = $tt->get_next_token while $t->{type} == S_TOKEN;            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
# Line 271  sub parse_char_string ($$) { Line 335  sub parse_char_string ($$) {
335    
336                    #                    #
337                  } else {                  } else {
338                    $onerror->(type => 'syntax error:important',                    $onerror->(type => 'priority syntax error',
339                               level => $self->{must_level},                               level => $self->{must_level},
340                                 uri => \$self->{href},
341                               token => $t);                               token => $t);
342                                        
343                    ## Reprocess.                    ## Reprocess.
# Line 290  sub parse_char_string ($$) { Line 355  sub parse_char_string ($$) {
355                redo S;                redo S;
356              }              }
357            } else {            } else {
358              $onerror->(type => 'not supported:property',              $onerror->(type => 'property not supported',
359                         level => $self->{unsupported_level},                         level => $self->{unsupported_level},
360                         token => $t, value => $prop_name);                         token => $t, value => $prop_name,
361                           uri => \$self->{href});
362    
363              #              #
364              $state = IGNORED_DECLARATION_STATE;              $state = IGNORED_DECLARATION_STATE;
365              redo S;              redo S;
366            }            }
367          } else {          } else {
368            $onerror->(type => 'syntax error:property colon',            $onerror->(type => 'no property colon',
369                       level => $self->{must_level},                       level => $self->{must_level},
370                         uri => \$self->{href},
371                       token => $t);                       token => $t);
372    
373            #            #
# Line 318  sub parse_char_string ($$) { Line 385  sub parse_char_string ($$) {
385          ## Stay in the state.          ## Stay in the state.
386          #redo S;          #redo S;
387        } elsif ($t->{type} == EOF_TOKEN) {        } elsif ($t->{type} == EOF_TOKEN) {
388          $onerror->(type => 'syntax error:ruleset not closed',          $onerror->(type => 'block not closed',
389                     level => $self->{must_level},                     level => $self->{must_level},
390                       uri => \$self->{href},
391                     token => $t);                     token => $t);
392          ## Reprocess.          ## Reprocess.
393          $state = BEFORE_STATEMENT_STATE;          $state = BEFORE_STATEMENT_STATE;
394          #redo S;          #redo S;
395        } else {        } else {
396          if ($prop_value) {          if ($prop_value) {
397            $onerror->(type => 'syntax error:property semicolon',            $onerror->(type => 'no property semicolon',
398                       level => $self->{must_level},                       level => $self->{must_level},
399                         uri => \$self->{href},
400                       token => $t);                       token => $t);
401          } else {          } else {
402            $onerror->(type => 'syntax error:property name',            $onerror->(type => 'no property name',
403                       level => $self->{must_level},                       level => $self->{must_level},
404                         uri => \$self->{href},
405                       token => $t);                       token => $t);
406          }          }
407    
# Line 340  sub parse_char_string ($$) { Line 410  sub parse_char_string ($$) {
410          redo S;          redo S;
411        }        }
412    
413        my $important = (defined $prop_flag and $prop_flag eq 'important');        my $important = ($prop_flag eq 'important');
414        for my $set_prop_name (keys %{$prop_value or {}}) {        for my $set_prop_name (keys %{$prop_value or {}}) {
415          my $set_prop_def = $Prop->{$set_prop_name};          my $set_prop_def = $Prop->{$set_prop_name};
416          $$current_decls->{$set_prop_def->{key}}          $$current_decls->{$set_prop_def->{key}}
417              = [$prop_value->{$set_prop_name}, $prop_flag]              = [$prop_value->{$set_prop_name}, $prop_flag]
418              if $important or              if $important or
419                  not $$current_decls->{$set_prop_def->{key}} or                  not $$current_decls->{$set_prop_def->{key}} or
420                  not defined $$current_decls->{$set_prop_def->{key}}->[1];                  $$current_decls->{$set_prop_def->{key}}->[1] ne 'important';
421        }        }
422        redo S;        redo S;
423      } elsif ($state == IGNORED_STATEMENT_STATE or      } elsif ($state == IGNORED_STATEMENT_STATE or
# Line 372  sub parse_char_string ($$) { Line 442  sub parse_char_string ($$) {
442              ## Stay in the state.              ## Stay in the state.
443              redo S;              redo S;
444            }            }
445            } elsif ({
446              RBRACE_TOKEN, 1,
447              #RBRACKET_TOKEN, 1,
448              #RPAREN_TOKEN, 1,
449              SEMICOLON_TOKEN, 1,
450            }->{$t->{type}}) {
451              $t = $tt->get_next_token;
452              ## Stay in the state.
453              #
454          } else {          } else {
455            #            #
456          }          }
# Line 381  sub parse_char_string ($$) { Line 460  sub parse_char_string ($$) {
460            $state = $state == IGNORED_STATEMENT_STATE            $state = $state == IGNORED_STATEMENT_STATE
461                ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;                ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;
462            redo S;            redo S;
463          } elsif ($state == IGNORED_DECLARATION_STATE and          } elsif ($t->{type} == RBRACE_TOKEN) {
464                   $t->{type} == RBRACE_TOKEN) {            if ($state == IGNORED_DECLARATION_STATE) {
465            $t = $tt->get_next_token;              $t = $tt->get_next_token;
466            $state = BEFORE_STATEMENT_STATE;              $state = BEFORE_STATEMENT_STATE;
467            redo S;              redo S;
468              } else {
469                ## NOTE: Maybe this state cannot be reached.
470                $t = $tt->get_next_token;
471                ## Stay in the state.
472                redo S;
473              }
474          } elsif ($t->{type} == EOF_TOKEN) {          } elsif ($t->{type} == EOF_TOKEN) {
475            ## Reprocess.            ## Reprocess.
476            $state = $state == IGNORED_STATEMENT_STATE            $state = $state == IGNORED_STATEMENT_STATE
477                ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;                ? BEFORE_STATEMENT_STATE : BEFORE_DECLARATION_STATE;
478            redo S;            redo S;
479            #} elsif ($t->{type} == RBRACKET_TOKEN or $t->{type} == RPAREN_TOKEN) {
480            #  $t = $tt->get_next_token;
481            #  ## Stay in the state.
482            #  #
483          } else {          } else {
484            #            #
485          }          }
# Line 399  sub parse_char_string ($$) { Line 488  sub parse_char_string ($$) {
488        while (not {        while (not {
489          EOF_TOKEN, 1,          EOF_TOKEN, 1,
490          RBRACE_TOKEN, 1,          RBRACE_TOKEN, 1,
491          RBRACKET_TOKEN, 1,          ## NOTE: ']' and ')' are disabled for browser compatibility.
492          RPAREN_TOKEN, 1,          #RBRACKET_TOKEN, 1,
493            #RPAREN_TOKEN, 1,
494          SEMICOLON_TOKEN, 1,          SEMICOLON_TOKEN, 1,
495        }->{$t->{type}}) {        }->{$t->{type}}) {
496          if ($t->{type} == LBRACE_TOKEN) {          if ($t->{type} == LBRACE_TOKEN) {
497            push @$closing_tokens, RBRACE_TOKEN;            push @$closing_tokens, RBRACE_TOKEN;
498          } elsif ($t->{type} == LBRACKET_TOKEN) {          #} elsif ($t->{type} == LBRACKET_TOKEN) {
499            push @$closing_tokens, RBRACKET_TOKEN;          #  push @$closing_tokens, RBRACKET_TOKEN;
500          } elsif ($t->{type} == LPAREN_TOKEN or $t->{type} == FUNCTION_TOKEN) {          #} elsif ($t->{type} == LPAREN_TOKEN or $t->{type} == FUNCTION_TOKEN) {
501            push @$closing_tokens, RPAREN_TOKEN;          #  push @$closing_tokens, RPAREN_TOKEN;
502          }          }
503    
504          $t = $tt->get_next_token;          $t = $tt->get_next_token;
# Line 429  sub parse_char_string ($$) { Line 519  sub parse_char_string ($$) {
519         ## TODO: owner_node         ## TODO: owner_node
520         ## TODO: media         ## TODO: media
521         type => 'text/css', ## TODO: OK?         type => 'text/css', ## TODO: OK?
522         _parser => $self);         _parser => $self, _nsmap => $nsmap);
523    return $ss;    return $ss;
524  } # parse_char_string  } # parse_char_string
525    
# Line 445  my $default_serializer = sub { Line 535  my $default_serializer = sub {
535      ## any browser for lighter/bolder cases.  We need to fix this, but      ## any browser for lighter/bolder cases.  We need to fix this, but
536      ## how?      ## how?
537      return $value->[1]; ## TODO: big or small number cases?      return $value->[1]; ## TODO: big or small number cases?
538      } elsif ($value->[0] eq 'DIMENSION') {
539        return $value->[1] . $value->[2]; ## NOTE: This is what browsers do.
540      } elsif ($value->[0] eq 'PERCENTAGE') {
541        return $value->[1] . '%';
542    } elsif ($value->[0] eq 'KEYWORD') {    } elsif ($value->[0] eq 'KEYWORD') {
543      return $value->[1];      return $value->[1];
544    } elsif ($value->[0] eq 'URI') {    } elsif ($value->[0] eq 'URI') {
545      ## NOTE: This is what browsers do.      ## NOTE: This is what browsers do.
546      return 'url('.$value->[1].')';      return 'url('.$value->[1].')';
547      } elsif ($value->[0] eq 'RGBA') {
548        if ($value->[4] == 1) {
549          return 'rgb('.$value->[1].', '.$value->[2].', '.$value->[3].')';
550        } elsif ($value->[4] == 0) {
551          ## TODO: check what browsers do...
552          return 'transparent';
553        } else {
554          return 'rgba('.$value->[1].', '.$value->[2].', '.$value->[3].', '
555              .$value->[4].')';
556        }
557    } elsif ($value->[0] eq 'INHERIT') {    } elsif ($value->[0] eq 'INHERIT') {
558      return 'inherit';      return 'inherit';
559      } elsif ($value->[0] eq 'DECORATION') {
560        my @v = ();
561        push @v, 'underline' if $value->[1];
562        push @v, 'overline' if $value->[2];
563        push @v, 'line-through' if $value->[3];
564        push @v, 'blink' if $value->[4];
565        return 'none' unless @v;
566        return join ' ', @v;
567    } else {    } else {
568      return undef;      return '';
569    }    }
570  }; # $default_serializer  }; # $default_serializer
571    
572    my $x11_colors = {
573                      'aliceblue' =>        [0xf0, 0xf8, 0xff],
574                      'antiquewhite' =>     [0xfa, 0xeb, 0xd7],
575                      'aqua' =>     [0x00, 0xff, 0xff],
576                      'aquamarine' =>       [0x7f, 0xff, 0xd4],
577                      'azure' =>    [0xf0, 0xff, 0xff],
578                      'beige' =>    [0xf5, 0xf5, 0xdc],
579                      'bisque' =>   [0xff, 0xe4, 0xc4],
580                      'black' =>    [0x00, 0x00, 0x00],
581                      'blanchedalmond' =>   [0xff, 0xeb, 0xcd],
582                      'blue' =>     [0x00, 0x00, 0xff],
583                      'blueviolet' =>       [0x8a, 0x2b, 0xe2],
584                      'brown' =>    [0xa5, 0x2a, 0x2a],
585                      'burlywood' =>        [0xde, 0xb8, 0x87],
586                      'cadetblue' =>        [0x5f, 0x9e, 0xa0],
587                      'chartreuse' =>       [0x7f, 0xff, 0x00],
588                      'chocolate' =>        [0xd2, 0x69, 0x1e],
589                      'coral' =>    [0xff, 0x7f, 0x50],
590                      'cornflowerblue' =>   [0x64, 0x95, 0xed],
591                      'cornsilk' => [0xff, 0xf8, 0xdc],
592                      'crimson' =>  [0xdc, 0x14, 0x3c],
593                      'cyan' =>     [0x00, 0xff, 0xff],
594                      'darkblue' => [0x00, 0x00, 0x8b],
595                      'darkcyan' => [0x00, 0x8b, 0x8b],
596                      'darkgoldenrod' =>    [0xb8, 0x86, 0x0b],
597                      'darkgray' => [0xa9, 0xa9, 0xa9],
598                      'darkgreen' =>        [0x00, 0x64, 0x00],
599                      'darkgrey' => [0xa9, 0xa9, 0xa9],
600                      'darkkhaki' =>        [0xbd, 0xb7, 0x6b],
601                      'darkmagenta' =>      [0x8b, 0x00, 0x8b],
602                      'darkolivegreen' =>   [0x55, 0x6b, 0x2f],
603                      'darkorange' =>       [0xff, 0x8c, 0x00],
604                      'darkorchid' =>       [0x99, 0x32, 0xcc],
605                      'darkred' =>  [0x8b, 0x00, 0x00],
606                      'darksalmon' =>       [0xe9, 0x96, 0x7a],
607                      'darkseagreen' =>     [0x8f, 0xbc, 0x8f],
608                      'darkslateblue' =>    [0x48, 0x3d, 0x8b],
609                      'darkslategray' =>    [0x2f, 0x4f, 0x4f],
610                      'darkslategrey' =>    [0x2f, 0x4f, 0x4f],
611                      'darkturquoise' =>    [0x00, 0xce, 0xd1],
612                      'darkviolet' =>       [0x94, 0x00, 0xd3],
613                      'deeppink' => [0xff, 0x14, 0x93],
614                      'deepskyblue' =>      [0x00, 0xbf, 0xff],
615                      'dimgray' =>  [0x69, 0x69, 0x69],
616                      'dimgrey' =>  [0x69, 0x69, 0x69],
617                      'dodgerblue' =>       [0x1e, 0x90, 0xff],
618                      'firebrick' =>        [0xb2, 0x22, 0x22],
619                      'floralwhite' =>      [0xff, 0xfa, 0xf0],
620                      'forestgreen' =>      [0x22, 0x8b, 0x22],
621                      'fuchsia' =>  [0xff, 0x00, 0xff],
622                      'gainsboro' =>        [0xdc, 0xdc, 0xdc],
623                      'ghostwhite' =>       [0xf8, 0xf8, 0xff],
624                      'gold' =>     [0xff, 0xd7, 0x00],
625                      'goldenrod' =>        [0xda, 0xa5, 0x20],
626                      'gray' =>     [0x80, 0x80, 0x80],
627                      'green' =>    [0x00, 0x80, 0x00],
628                      'greenyellow' =>      [0xad, 0xff, 0x2f],
629                      'grey' =>     [0x80, 0x80, 0x80],
630                      'honeydew' => [0xf0, 0xff, 0xf0],
631                      'hotpink' =>  [0xff, 0x69, 0xb4],
632                      'indianred' =>        [0xcd, 0x5c, 0x5c],
633                      'indigo' =>   [0x4b, 0x00, 0x82],
634                      'ivory' =>    [0xff, 0xff, 0xf0],
635                      'khaki' =>    [0xf0, 0xe6, 0x8c],
636                      'lavender' => [0xe6, 0xe6, 0xfa],
637                      'lavenderblush' =>    [0xff, 0xf0, 0xf5],
638                      'lawngreen' =>        [0x7c, 0xfc, 0x00],
639                      'lemonchiffon' =>     [0xff, 0xfa, 0xcd],
640                      'lightblue' =>        [0xad, 0xd8, 0xe6],
641                      'lightcoral' =>       [0xf0, 0x80, 0x80],
642                      'lightcyan' =>        [0xe0, 0xff, 0xff],
643                      'lightgoldenrodyellow' =>     [0xfa, 0xfa, 0xd2],
644                      'lightgray' =>        [0xd3, 0xd3, 0xd3],
645                      'lightgreen' =>       [0x90, 0xee, 0x90],
646                      'lightgrey' =>        [0xd3, 0xd3, 0xd3],
647                      'lightpink' =>        [0xff, 0xb6, 0xc1],
648                      'lightsalmon' =>      [0xff, 0xa0, 0x7a],
649                      'lightseagreen' =>    [0x20, 0xb2, 0xaa],
650                      'lightskyblue' =>     [0x87, 0xce, 0xfa],
651                      'lightslategray' =>   [0x77, 0x88, 0x99],
652                      'lightslategrey' =>   [0x77, 0x88, 0x99],
653                      'lightsteelblue' =>   [0xb0, 0xc4, 0xde],
654                      'lightyellow' =>      [0xff, 0xff, 0xe0],
655                      'lime' =>     [0x00, 0xff, 0x00],
656                      'limegreen' =>        [0x32, 0xcd, 0x32],
657                      'linen' =>    [0xfa, 0xf0, 0xe6],
658                      'magenta' =>  [0xff, 0x00, 0xff],
659                      'maroon' =>   [0x80, 0x00, 0x00],
660                      'mediumaquamarine' => [0x66, 0xcd, 0xaa],
661                      'mediumblue' =>       [0x00, 0x00, 0xcd],
662                      'mediumorchid' =>     [0xba, 0x55, 0xd3],
663                      'mediumpurple' =>     [0x93, 0x70, 0xdb],
664                      'mediumseagreen' =>   [0x3c, 0xb3, 0x71],
665                      'mediumslateblue' =>  [0x7b, 0x68, 0xee],
666                      'mediumspringgreen' =>        [0x00, 0xfa, 0x9a],
667                      'mediumturquoise' =>  [0x48, 0xd1, 0xcc],
668                      'mediumvioletred' =>  [0xc7, 0x15, 0x85],
669                      'midnightblue' =>     [0x19, 0x19, 0x70],
670                      'mintcream' =>        [0xf5, 0xff, 0xfa],
671                      'mistyrose' =>        [0xff, 0xe4, 0xe1],
672                      'moccasin' => [0xff, 0xe4, 0xb5],
673                      'navajowhite' =>      [0xff, 0xde, 0xad],
674                      'navy' =>     [0x00, 0x00, 0x80],
675                      'oldlace' =>  [0xfd, 0xf5, 0xe6],
676                      'olive' =>    [0x80, 0x80, 0x00],
677                      'olivedrab' =>        [0x6b, 0x8e, 0x23],
678                      'orange' =>   [0xff, 0xa5, 0x00],
679                      'orangered' =>        [0xff, 0x45, 0x00],
680                      'orchid' =>   [0xda, 0x70, 0xd6],
681                      'palegoldenrod' =>    [0xee, 0xe8, 0xaa],
682                      'palegreen' =>        [0x98, 0xfb, 0x98],
683                      'paleturquoise' =>    [0xaf, 0xee, 0xee],
684                      'palevioletred' =>    [0xdb, 0x70, 0x93],
685                      'papayawhip' =>       [0xff, 0xef, 0xd5],
686                      'peachpuff' =>        [0xff, 0xda, 0xb9],
687                      'peru' =>     [0xcd, 0x85, 0x3f],
688                      'pink' =>     [0xff, 0xc0, 0xcb],
689                      'plum' =>     [0xdd, 0xa0, 0xdd],
690                      'powderblue' =>       [0xb0, 0xe0, 0xe6],
691                      'purple' =>   [0x80, 0x00, 0x80],
692                      'red' =>      [0xff, 0x00, 0x00],
693                      'rosybrown' =>        [0xbc, 0x8f, 0x8f],
694                      'royalblue' =>        [0x41, 0x69, 0xe1],
695                      'saddlebrown' =>      [0x8b, 0x45, 0x13],
696                      'salmon' =>   [0xfa, 0x80, 0x72],
697                      'sandybrown' =>       [0xf4, 0xa4, 0x60],
698                      'seagreen' => [0x2e, 0x8b, 0x57],
699                      'seashell' => [0xff, 0xf5, 0xee],
700                      'sienna' =>   [0xa0, 0x52, 0x2d],
701                      'silver' =>   [0xc0, 0xc0, 0xc0],
702                      'skyblue' =>  [0x87, 0xce, 0xeb],
703                      'slateblue' =>        [0x6a, 0x5a, 0xcd],
704                      'slategray' =>        [0x70, 0x80, 0x90],
705                      'slategrey' =>        [0x70, 0x80, 0x90],
706                      'snow' =>     [0xff, 0xfa, 0xfa],
707                      'springgreen' =>      [0x00, 0xff, 0x7f],
708                      'steelblue' =>        [0x46, 0x82, 0xb4],
709                      'tan' =>      [0xd2, 0xb4, 0x8c],
710                      'teal' =>     [0x00, 0x80, 0x80],
711                      'thistle' =>  [0xd8, 0xbf, 0xd8],
712                      'tomato' =>   [0xff, 0x63, 0x47],
713                      'turquoise' =>        [0x40, 0xe0, 0xd0],
714                      'violet' =>   [0xee, 0x82, 0xee],
715                      'wheat' =>    [0xf5, 0xde, 0xb3],
716                      'white' =>    [0xff, 0xff, 0xff],
717                      'whitesmoke' =>       [0xf5, 0xf5, 0xf5],
718                      'yellow' =>   [0xff, 0xff, 0x00],
719                      'yellowgreen' =>      [0x9a, 0xcd, 0x32],
720    }; # $x11_colors
721    
722    my $system_colors = {
723      activeborder => 1, activecaption => 1, appworkspace => 1, background => 1,
724      buttonface => 1, buttonhighlight => 1, buttonshadow => 1, buttontext => 1,
725      captiontext => 1, graytext => 1, highlight => 1, highlighttext => 1,
726      inactiveborder => 1, inactivecaption => 1, inactivecaptiontext => 1,
727      infobackground => 1, infotext => 1, menu => 1, menutext => 1,
728      scrollbar => 1, threeddarkshadow => 1, threedface => 1, threedhighlight => 1,
729      threedlightshadow => 1, threedshadow => 1, window => 1, windowframe => 1,
730      windowtext => 1,
731    }; # $system_colors
732    
733    my $parse_color = sub {
734      my ($self, $prop_name, $tt, $t, $onerror) = @_;
735    
736      ## See
737      ## <http://suika.fam.cx/gate/2005/sw/%3Ccolor%3E>,
738      ## <http://suika.fam.cx/gate/2005/sw/rgb>,
739      ## <http://suika.fam.cx/gate/2005/sw/-moz-rgba>,
740      ## <http://suika.fam.cx/gate/2005/sw/hsl>,
741      ## <http://suika.fam.cx/gate/2005/sw/-moz-hsla>, and
742      ## <http://suika.fam.cx/gate/2005/sw/color>
743      ## for browser compatibility issue.
744    
745      ## NOTE: Implementing CSS3 Color CR (2003), except for attr(),
746      ## rgba(), and hsla().
747      ## NOTE: rgb(...{EOF} is not supported (only Opera does).
748    
749      if ($t->{type} == IDENT_TOKEN) {
750        my $value = lc $t->{value}; ## TODO: case
751        if ($x11_colors->{$value} or
752            $system_colors->{$value}) {
753          ## NOTE: "For systems that do not have a corresponding value, the
754          ## specified value should be mapped to the nearest system value, or to
755          ## a default color." [CSS 2.1].
756          $t = $tt->get_next_token;
757          return ($t, {$prop_name => ['KEYWORD', $value]});
758        } elsif ({
759          transparent => 1, ## For 'background-color' in CSS2.1, everywhre in CSS3.
760          flavor => 1, ## CSS3.
761          invert => 1, ## For 'outline-color' in CSS2.1.
762          '-moz-use-text-color' => 1, ## For <border-color> in Gecko.
763          '-manakai-default' => 1, ## CSS2.1 initial for 'color'
764          '-manakai-invert-or-currentcolor' => 1, ## CSS2.1 initial4'outline-color'
765        }->{$value} and $self->{prop_value}->{$prop_name}->{$value}) {
766          $t = $tt->get_next_token;
767          return ($t, {$prop_name => ['KEYWORD', $value]});
768        } elsif ($value eq 'currentcolor' or $value eq '-moz-use-text-color') {
769          $t = $tt->get_next_token;
770          if ($prop_name eq 'color') {
771            return ($t, {$prop_name => ['INHERIT']});
772          } else {
773            return ($t, {$prop_name => ['KEYWORD', $value]});
774          }
775        } elsif ($value eq 'inherit') {
776          $t = $tt->get_next_token;
777          return ($t, {$prop_name => ['INHERIT']});
778        }
779      }
780    
781      if ($t->{type} == HASH_TOKEN or
782          ($self->{hashless_rgb} and {
783            IDENT_TOKEN, 1,
784            NUMBER_TOKEN, 1,
785            DIMENSION_TOKEN, 1,
786          }->{$t->{type}})) {
787        my $v = lc (defined $t->{number} ? $t->{number} : '' . $t->{value}); ## TODO: case
788        if ($v =~ /\A([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})\z/) {
789          $t = $tt->get_next_token;
790          return ($t, {$prop_name => ['RGBA', hex $1, hex $2, hex $3, 1]});
791        } elsif ($v =~ /\A([0-9a-f])([0-9a-f])([0-9a-f])\z/) {
792          $t = $tt->get_next_token;
793          return ($t, {$prop_name => ['RGBA', hex $1.$1, hex $2.$2,
794                                      hex $3.$3, 1]});
795        }
796      }
797    
798      if ($t->{type} == FUNCTION_TOKEN) {
799        my $func = lc $t->{value}; ## TODO: case
800        if ($func eq 'rgb') {
801          $t = $tt->get_next_token;
802          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
803          my $sign = 1;
804          if ($t->{type} == MINUS_TOKEN) {
805            $sign = -1;
806            $t = $tt->get_next_token;
807          }
808          if ($t->{type} == NUMBER_TOKEN) {
809            my $r = $t->{number} * $sign;
810            $t = $tt->get_next_token;
811            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
812            if ($t->{type} == COMMA_TOKEN) {
813              $t = $tt->get_next_token;
814              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
815              $sign = 1;
816              if ($t->{type} == MINUS_TOKEN) {
817                $sign = -1;
818                $t = $tt->get_next_token;
819              }
820              if ($t->{type} == NUMBER_TOKEN) {
821                my $g = $t->{number} * $sign;
822                $t = $tt->get_next_token;
823                $t = $tt->get_next_token while $t->{type} == S_TOKEN;
824                if ($t->{type} == COMMA_TOKEN) {
825                  $t = $tt->get_next_token;
826                  $t = $tt->get_next_token while $t->{type} == S_TOKEN;
827                  $sign = 1;
828                  if ($t->{type} == MINUS_TOKEN) {
829                    $sign = -1;
830                    $t = $tt->get_next_token;
831                  }
832                  if ($t->{type} == NUMBER_TOKEN) {
833                    my $b = $t->{number} * $sign;
834                    $t = $tt->get_next_token;
835                    $t = $tt->get_next_token while $t->{type} == S_TOKEN;
836                    if ($t->{type} == RPAREN_TOKEN) {
837                      $t = $tt->get_next_token;
838                      return ($t,
839                              {$prop_name =>
840                               $self->{clip_color}->($self,
841                                                     ['RGBA', $r, $g, $b, 1])});
842                    }
843                  }
844                }
845              }
846            }
847          } elsif ($t->{type} == PERCENTAGE_TOKEN) {
848            my $r = $t->{number} * 255 / 100 * $sign;
849            $t = $tt->get_next_token;
850            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
851            if ($t->{type} == COMMA_TOKEN) {
852              $t = $tt->get_next_token;
853              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
854              $sign = 1;
855              if ($t->{type} == MINUS_TOKEN) {
856                $sign = -1;
857                $t = $tt->get_next_token;
858              }          
859              if ($t->{type} == PERCENTAGE_TOKEN) {
860                my $g = $t->{number} * 255 / 100 * $sign;
861                $t = $tt->get_next_token;
862                $t = $tt->get_next_token while $t->{type} == S_TOKEN;
863                if ($t->{type} == COMMA_TOKEN) {
864                  $t = $tt->get_next_token;
865                  $t = $tt->get_next_token while $t->{type} == S_TOKEN;
866                  $sign = 1;
867                  if ($t->{type} == MINUS_TOKEN) {
868                    $sign = -1;
869                    $t = $tt->get_next_token;
870                  }
871                  if ($t->{type} == PERCENTAGE_TOKEN) {
872                    my $b = $t->{number} * 255 / 100 * $sign;
873                    $t = $tt->get_next_token;
874                    $t = $tt->get_next_token while $t->{type} == S_TOKEN;
875                    if ($t->{type} == RPAREN_TOKEN) {
876                      $t = $tt->get_next_token;
877                      return ($t,
878                              {$prop_name =>
879                               $self->{clip_color}->($self,
880                                                     ['RGBA', $r, $g, $b, 1])});
881                    }
882                  }
883                }
884              }
885            }
886          }
887        } elsif ($func eq 'hsl') {
888          $t = $tt->get_next_token;
889          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
890          my $sign = 1;
891          if ($t->{type} == MINUS_TOKEN) {
892            $sign = -1;
893            $t = $tt->get_next_token;
894          }
895          if ($t->{type} == NUMBER_TOKEN) {
896            my $h = (((($t->{number} * $sign) % 360) + 360) % 360) / 360;
897            $t = $tt->get_next_token;
898            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
899            if ($t->{type} == COMMA_TOKEN) {
900              $t = $tt->get_next_token;
901              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
902              $sign = 1;
903              if ($t->{type} == MINUS_TOKEN) {
904                $sign = -1;
905                $t = $tt->get_next_token;
906              }
907              if ($t->{type} == PERCENTAGE_TOKEN) {
908                my $s = $t->{number} * $sign / 100;
909                $s = 0 if $s < 0;
910                $s = 1 if $s > 1;
911                $t = $tt->get_next_token;
912                $t = $tt->get_next_token while $t->{type} == S_TOKEN;
913                if ($t->{type} == COMMA_TOKEN) {
914                  $t = $tt->get_next_token;
915                  $t = $tt->get_next_token while $t->{type} == S_TOKEN;
916                  $sign = 1;
917                  if ($t->{type} == MINUS_TOKEN) {
918                    $sign = -1;
919                    $t = $tt->get_next_token;
920                  }
921                  if ($t->{type} == PERCENTAGE_TOKEN) {
922                    my $l = $t->{number} * $sign / 100;
923                    $l = 0 if $l < 0;
924                    $l = 1 if $l > 1;
925                    $t = $tt->get_next_token;
926                    $t = $tt->get_next_token while $t->{type} == S_TOKEN;
927                    if ($t->{type} == RPAREN_TOKEN) {
928                      my $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
929                      my $m1 = $l * 2 - $m2;
930                      my $hue2rgb = sub ($$$) {
931                        my ($m1, $m2, $h) = @_;
932                        $h++ if $h < 0;
933                        $h-- if $h > 1;
934                        return $m1 + ($m2 - $m1) * $h * 6 if $h * 6 < 1;
935                        return $m2 if $h * 2 < 1;
936                        return $m1 + ($m2 - $m1) * (2/3 - $h) * 6 if $h * 3 < 2;
937                        return $m1;
938                      };
939                      $t = $tt->get_next_token;
940                      return ($t,
941                              {$prop_name =>
942                               $self->{clip_color}
943                                   ->($self,
944                                      ['RGBA',
945                                       $hue2rgb->($m1, $m2, $h + 1/3),
946                                       $hue2rgb->($m1, $m2, $h),
947                                       $hue2rgb->($m1, $m2, $h - 1/3), 1])});
948                    }
949                  }
950                }
951              }
952            }
953          }
954        }
955      }
956      
957      $onerror->(type => 'syntax error:color',
958                 level => $self->{must_level},
959                 uri => \$self->{href},
960                 token => $t);
961      
962      return ($t, undef);
963    }; # $parse_color
964    
965  $Prop->{color} = {  $Prop->{color} = {
966    css => 'color',    css => 'color',
967    dom => 'color',    dom => 'color',
968    key => 'color',    key => 'color',
969    parse => sub {    parse => $parse_color,
970      my ($self, $prop_name, $tt, $t, $onerror) = @_;    serialize => $default_serializer,
971      initial => ['KEYWORD', '-manakai-default'],
972      inherited => 1,
973      compute => sub ($$$$) {
974        my ($self, $element, $prop_name, $specified_value) = @_;
975    
976      if ($t->{type} == IDENT_TOKEN) {      if (defined $specified_value) {
977        if (lc $t->{value} eq 'blue') { ## TODO: case folding        if ($specified_value->[0] eq 'KEYWORD') {
978          $t = $tt->get_next_token;          if ($x11_colors->{$specified_value->[1]}) {
979          return ($t, {$prop_name => ["RGBA", 0, 0, 255, 1]});            return ['RGBA', @{$x11_colors->{$specified_value->[1]}}, 1];
980            } elsif ($specified_value->[1] eq 'transparent') {
981              return ['RGBA', 0, 0, 0, 0];
982            } elsif ($specified_value->[1] eq 'currentcolor' or
983                     $specified_value->[1] eq '-moz-use-text-color' or
984                     ($specified_value->[1] eq '-manakai-invert-or-currentcolor'and
985                      not $self->{has_invert})) {
986              unless ($prop_name eq 'color') {
987                return $self->get_computed_value ($element, 'color');
988              } else {
989                ## NOTE: This is an error, since it should have been
990                ## converted to 'inherit' at parse time.
991                return ['KEYWORD', '-manakai-default'];
992              }
993            } elsif ($specified_value->[1] eq '-manakai-invert-or-currentcolor') {
994              return ['KEYWORD', 'invert'];
995            }
996          }
997        }
998        
999        return $specified_value;
1000      },
1001    };
1002    $Attr->{color} = $Prop->{color};
1003    $Key->{color} = $Prop->{color};
1004    
1005    $Prop->{'background-color'} = {
1006      css => 'background-color',
1007      dom => 'background_color',
1008      key => 'background_color',
1009      parse => $parse_color,
1010      serialize => $default_serializer,
1011      serialize_multiple => sub {
1012        my $self = shift;
1013    
1014        ## TODO: !important handling is incorrect.
1015    
1016        my $r = {};
1017        my $has_all;
1018        
1019        local $Error::Depth = $Error::Depth + 1;
1020        my $x = $self->background_position_x;
1021        my $y = $self->background_position_y;
1022        my $xi = $self->get_property_priority ('background-position-x');
1023        my $yi = $self->get_property_priority ('background-position-y');
1024        $xi = ' !' . $xi if length $xi;
1025        $yi = ' !' . $yi if length $yi;
1026        if (length $x) {
1027          if (length $y) {
1028            if ($xi eq $yi) {
1029              $r->{'background-position'} = $x . ' ' . $y . $xi;
1030              $has_all = 1;
1031            } else {
1032              $r->{'background-position-x'} = $x . $xi;
1033              $r->{'background-position-y'} = $y . $yi;
1034            }
1035          } else {
1036            $r->{'background-position-x'} = $x . $xi;
1037          }
1038        } else {
1039          if (length $y) {
1040            $r->{'background-position-y'} = $y . $yi;
1041        } else {        } else {
1042          #          #
1043        }        }
1044        }
1045        
1046        for my $prop (qw/color image repeat attachment/) {
1047          my $prop_name = 'background_'.$prop;
1048          my $value = $self->$prop_name;
1049          if (length $value) {
1050            $r->{'background-'.$prop} = $value;
1051            my $i = $self->get_property_priority ('background-'.$prop);
1052            $r->{'background-'.$prop} .= ' !'.$i if length $i;
1053          } else {
1054            undef $has_all;
1055          }
1056        }
1057    
1058        if ($has_all) {
1059          my @v;
1060          push @v, $r->{'background-color'}
1061              unless $r->{'background-color'} eq 'transparent';
1062          push @v, $r->{'background-image'}
1063              unless $r->{'background-image'} eq 'none';
1064          push @v, $r->{'background-repeat'}
1065              unless $r->{'background-repeat'} eq 'repeat';
1066          push @v, $r->{'background-attachment'}
1067              unless $r->{'background-attachment'} eq 'scroll';
1068          push @v, $r->{'background-position'}
1069              unless $r->{'background-position'} eq '0% 0%';
1070          if (@v) {
1071            return {background => join ' ', @v};
1072          } else {
1073            return {background => 'transparent none repeat scroll 0% 0%'};
1074          }
1075      } else {      } else {
1076        #        return $r;
1077      }      }
1078      },
1079      initial => ['KEYWORD', 'transparent'],
1080      #inherited => 0,
1081      compute => $Prop->{color}->{compute},
1082    };
1083    $Attr->{background_color} = $Prop->{'background-color'};
1084    $Key->{background_color} = $Prop->{'background-color'};
1085    
1086      $onerror->(type => 'syntax error:color',  $Prop->{'border-top-color'} = {
1087                 level => $self->{must_level},    css => 'border-top-color',
1088                 token => $t);    dom => 'border_top_color',
1089          key => 'border_top_color',
1090      return ($t, undef);    parse => $parse_color,
1091      serialize => $default_serializer,
1092      serialize_multiple => sub {
1093        my $self = shift;
1094        ## NOTE: This algorithm returns the same result as that of Firefox 2
1095        ## in many case, but not always.
1096        my $r = {
1097          'border-top-color' => $self->border_top_color,
1098          'border-top-style' => $self->border_top_style,
1099          'border-top-width' => $self->border_top_width,
1100          'border-right-color' => $self->border_right_color,
1101          'border-right-style' => $self->border_right_style,
1102          'border-right-width' => $self->border_right_width,
1103          'border-bottom-color' => $self->border_bottom_color,
1104          'border-bottom-style' => $self->border_bottom_style,
1105          'border-bottom-width' => $self->border_bottom_width,
1106          'border-left-color' => $self->border_left_color,
1107          'border-left-style' => $self->border_left_style,
1108          'border-left-width' => $self->border_left_width,
1109        };
1110        my $i = 0;
1111        for my $prop (qw/border-top border-right border-bottom border-left/) {
1112          if (defined $r->{$prop.'-color'} and
1113              defined $r->{$prop.'-style'} and
1114              defined $r->{$prop.'-width'}) {
1115            $r->{$prop} = $r->{$prop.'-width'} . ' ' .
1116                  $r->{$prop.'-style'} . ' ' .
1117                  $r->{$prop.'-color'};
1118            delete $r->{$prop.'-width'};
1119            delete $r->{$prop.'-style'};
1120            delete $r->{$prop.'-color'};
1121            $i++;
1122          }
1123        }
1124        if ($i == 4 and $r->{'border-top'} eq $r->{'border-right'} and
1125            $r->{'border-right'} eq $r->{'border-bottom'} and
1126            $r->{'border-bottom'} eq $r->{'border-left'}) {
1127          return {border => $r->{'border-top'}};
1128        }
1129    
1130        unless ($i) {
1131          for my $prop (qw/color style width/) {
1132            if (defined $r->{'border-top-'.$prop} and
1133                defined $r->{'border-bottom-'.$prop} and
1134                defined $r->{'border-right-'.$prop} and
1135                defined $r->{'border-left-'.$prop}) {
1136              my @v = ($r->{'border-top-'.$prop},
1137                       $r->{'border-right-'.$prop},
1138                       $r->{'border-bottom-'.$prop},
1139                       $r->{'border-left-'.$prop});
1140              pop @v if $r->{'border-right-'.$prop} eq $r->{'border-left-'.$prop};
1141              pop @v if $r->{'border-bottom-'.$prop} eq $r->{'border-top-'.$prop};
1142              pop @v if $r->{'border-right-'.$prop} eq $r->{'border-top-'.$prop};
1143              $r->{'border-'.$prop} = join ' ', @v;
1144              delete $r->{'border-top-'.$prop};
1145              delete $r->{'border-bottom-'.$prop};
1146              delete $r->{'border-right-'.$prop};
1147              delete $r->{'border-left-'.$prop};
1148            }
1149          }
1150        }
1151    
1152        delete $r->{$_} for grep {not defined $r->{$_}} keys %$r;
1153        return $r;
1154    },    },
1155    serialize => sub {    initial => ['KEYWORD', 'currentcolor'],
1156      my ($self, $prop_name, $value) = @_;    #inherited => 0,
1157      if ($value->[0] eq 'RGBA') { ## TODO: %d? %f?    compute => $Prop->{color}->{compute},
1158        return sprintf 'rgba(%d, %d, %d, %f)', @$value[1, 2, 3, 4];  };
1159    $Attr->{border_top_color} = $Prop->{'border-top-color'};
1160    $Key->{border_top_color} = $Prop->{'border-top-color'};
1161    
1162    $Prop->{'border-right-color'} = {
1163      css => 'border-right-color',
1164      dom => 'border_right_color',
1165      key => 'border_right_color',
1166      parse => $parse_color,
1167      serialize => $default_serializer,
1168      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
1169      initial => ['KEYWORD', 'currentcolor'],
1170      #inherited => 0,
1171      compute => $Prop->{color}->{compute},
1172    };
1173    $Attr->{border_right_color} = $Prop->{'border-right-color'};
1174    $Key->{border_right_color} = $Prop->{'border-right-color'};
1175    
1176    $Prop->{'border-bottom-color'} = {
1177      css => 'border-bottom-color',
1178      dom => 'border_bottom_color',
1179      key => 'border_bottom_color',
1180      parse => $parse_color,
1181      serialize => $default_serializer,
1182      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
1183      initial => ['KEYWORD', 'currentcolor'],
1184      #inherited => 0,
1185      compute => $Prop->{color}->{compute},
1186    };
1187    $Attr->{border_bottom_color} = $Prop->{'border-bottom-color'};
1188    $Key->{border_bottom_color} = $Prop->{'border-bottom-color'};
1189    
1190    $Prop->{'border-left-color'} = {
1191      css => 'border-left-color',
1192      dom => 'border_left_color',
1193      key => 'border_left_color',
1194      parse => $parse_color,
1195      serialize => $default_serializer,
1196      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
1197      initial => ['KEYWORD', 'currentcolor'],
1198      #inherited => 0,
1199      compute => $Prop->{color}->{compute},
1200    };
1201    $Attr->{border_left_color} = $Prop->{'border-left-color'};
1202    $Key->{border_left_color} = $Prop->{'border-left-color'};
1203    
1204    $Prop->{'outline-color'} = {
1205      css => 'outline-color',
1206      dom => 'outline_color',
1207      key => 'outline_color',
1208      parse => $parse_color,
1209      serialize => $default_serializer,
1210      serialize_multiple => sub {
1211        my $self = shift;
1212        my $oc = $self->outline_color;
1213        my $os = $self->outline_style;
1214        my $ow = $self->outline_width;
1215        my $r = {};
1216        if (length $oc and length $os and length $ow) {
1217          $r->{outline} = $ow . ' ' . $os . ' ' . $oc;
1218      } else {      } else {
1219        return undef;        $r->{'outline-color'} = $oc if length $oc;
1220          $r->{'outline-style'} = $os if length $os;
1221          $r->{'outline-width'} = $ow if length $ow;
1222      }      }
1223        return $r;
1224    },    },
1225    initial => ["KEYWORD", "-manakai-initial-color"], ## NOTE: UA-dependent in CSS 2.1.    initial => ['KEYWORD', '-manakai-invert-or-currentcolor'],
1226    inherited => 1,    #inherited => 0,
1227    compute => $compute_as_specified,    compute => $Prop->{color}->{compute},
1228  };  };
1229  $Attr->{color} = $Prop->{color};  $Attr->{outline_color} = $Prop->{'outline-color'};
1230  $Key->{color} = $Prop->{color};  $Key->{outline_color} = $Prop->{'outline-color'};
1231    
1232  my $one_keyword_parser = sub {  my $one_keyword_parser = sub {
1233    my ($self, $prop_name, $tt, $t, $onerror) = @_;    my ($self, $prop_name, $tt, $t, $onerror) = @_;
# Line 510  my $one_keyword_parser = sub { Line 1243  my $one_keyword_parser = sub {
1243      }      }
1244    }    }
1245        
1246    $onerror->(type => 'syntax error:keyword:'.$prop_name,    $onerror->(type => "syntax error:'".$prop_name."'",
1247               level => $self->{must_level},               level => $self->{must_level},
1248                 uri => \$self->{href},
1249               token => $t);               token => $t);
1250    return ($t, undef);    return ($t, undef);
1251  };  };
# Line 538  $Prop->{display} = { Line 1272  $Prop->{display} = {
1272      ## WARNING: |compute| for 'float' property invoke this CODE      ## WARNING: |compute| for 'float' property invoke this CODE
1273      ## in some case.  Careless modification might cause a infinite loop.      ## in some case.  Careless modification might cause a infinite loop.
1274    
1275      if ($specified_value->[0] eq 'KEYWORD') {      if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
1276        if ($specified_value->[1] eq 'none') {        if ($specified_value->[1] eq 'none') {
1277          ## Case 1 [CSS 2.1]          ## Case 1 [CSS 2.1]
1278          return $specified_value;          return $specified_value;
# Line 621  $Prop->{float} = { Line 1355  $Prop->{float} = {
1355      ## WARNING: |compute| for 'display' property invoke this CODE      ## WARNING: |compute| for 'display' property invoke this CODE
1356      ## in some case.  Careless modification might cause a infinite loop.      ## in some case.  Careless modification might cause a infinite loop.
1357            
1358      if ($specified_value->[0] eq 'KEYWORD') {      if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
1359        if ($specified_value->[1] eq 'none') {        if ($specified_value->[1] eq 'none') {
1360          ## Case 1 [CSS 2.1]          ## Case 1 [CSS 2.1]
1361          return $specified_value;          return $specified_value;
# Line 818  $Prop->{'background-repeat'} = { Line 1552  $Prop->{'background-repeat'} = {
1552    key => 'background_repeat',    key => 'background_repeat',
1553    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1554    serialize => $default_serializer,    serialize => $default_serializer,
1555      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
1556    keyword => {    keyword => {
1557      repeat => 1, 'repeat-x' => 1, 'repeat-y' => 1, 'no-repeat' => 1,      repeat => 1, 'repeat-x' => 1, 'repeat-y' => 1, 'no-repeat' => 1,
1558    },    },
# Line 834  $Prop->{'background-attachment'} = { Line 1569  $Prop->{'background-attachment'} = {
1569    key => 'background_attachment',    key => 'background_attachment',
1570    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1571    serialize => $default_serializer,    serialize => $default_serializer,
1572      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
1573    keyword => {    keyword => {
1574      scroll => 1, fixed => 1,      scroll => 1, fixed => 1,
1575    },    },
# Line 846  $Key->{backgroud_attachment} = $Prop->{' Line 1582  $Key->{backgroud_attachment} = $Prop->{'
1582    
1583  $Prop->{'font-style'} = {  $Prop->{'font-style'} = {
1584    css => 'font-style',    css => 'font-style',
1585    dom => 'font_size',    dom => 'font_style',
1586    key => 'font_size',    key => 'font_style',
1587    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1588    serialize => $default_serializer,    serialize => $default_serializer,
1589    keyword => {    keyword => {
# Line 876  $Prop->{'font-variant'} = { Line 1612  $Prop->{'font-variant'} = {
1612  $Attr->{font_variant} = $Prop->{'font-variant'};  $Attr->{font_variant} = $Prop->{'font-variant'};
1613  $Key->{font_variant} = $Prop->{'font-variant'};  $Key->{font_variant} = $Prop->{'font-variant'};
1614    
1615    $Prop->{'text-align'} = {
1616      css => 'text-align',
1617      dom => 'text_align',
1618      key => 'text_align',
1619      parse => $one_keyword_parser,
1620      serialize => $default_serializer,
1621      keyword => {
1622        left => 1, right => 1, center => 1, justify => 1, ## CSS 2
1623        begin => 1, end => 1, ## CSS 3
1624      },
1625      initial => ["KEYWORD", 'begin'],
1626      inherited => 1,
1627      compute => $compute_as_specified,
1628    };
1629    $Attr->{text_align} = $Prop->{'text-align'};
1630    $Key->{text_align} = $Prop->{'text-align'};
1631    
1632    $Prop->{'text-transform'} = {
1633      css => 'text-transform',
1634      dom => 'text_transform',
1635      key => 'text_transform',
1636      parse => $one_keyword_parser,
1637      serialize => $default_serializer,
1638      keyword => {
1639        capitalize => 1, uppercase => 1, lowercase => 1, none => 1,
1640      },
1641      initial => ["KEYWORD", 'none'],
1642      inherited => 1,
1643      compute => $compute_as_specified,
1644    };
1645    $Attr->{text_transform} = $Prop->{'text-transform'};
1646    $Key->{text_transform} = $Prop->{'text-transform'};
1647    
1648    $Prop->{'white-space'} = {
1649      css => 'white-space',
1650      dom => 'white_space',
1651      key => 'white_space',
1652      parse => $one_keyword_parser,
1653      serialize => $default_serializer,
1654      keyword => {
1655        normal => 1, pre => 1, nowrap => 1, 'pre-wrap' => 1, 'pre-line' => 1,
1656      },
1657      initial => ["KEYWORD", 'normal'],
1658      inherited => 1,
1659      compute => $compute_as_specified,
1660    };
1661    $Attr->{white_space} = $Prop->{'white-space'};
1662    $Key->{white_space} = $Prop->{'white-space'};
1663    
1664    $Prop->{'caption-side'} = {
1665      css => 'caption-side',
1666      dom => 'caption_side',
1667      key => 'caption_side',
1668      parse => $one_keyword_parser,
1669      serialize => $default_serializer,
1670      keyword => {
1671        top => 1, bottom => 1,
1672      },
1673      initial => ['KEYWORD', 'top'],
1674      inherited => 1,
1675      compute => $compute_as_specified,
1676    };
1677    $Attr->{caption_side} = $Prop->{'caption-side'};
1678    $Key->{caption_side} = $Prop->{'caption-side'};
1679    
1680    $Prop->{'table-layout'} = {
1681      css => 'table-layout',
1682      dom => 'table_layout',
1683      key => 'table_layout',
1684      parse => $one_keyword_parser,
1685      serialize => $default_serializer,
1686      keyword => {
1687        auto => 1, fixed => 1,
1688      },
1689      initial => ['KEYWORD', 'auto'],
1690      #inherited => 0,
1691      compute => $compute_as_specified,
1692    };
1693    $Attr->{table_layout} = $Prop->{'table-layout'};
1694    $Key->{table_layout} = $Prop->{'table-layout'};
1695    
1696    $Prop->{'border-collapse'} = {
1697      css => 'border-collapse',
1698      dom => 'border_collapse',
1699      key => 'border_collapse',
1700      parse => $one_keyword_parser,
1701      serialize => $default_serializer,
1702      keyword => {
1703        collapse => 1, separate => 1,
1704      },
1705      initial => ['KEYWORD', 'separate'],
1706      inherited => 1,
1707      compute => $compute_as_specified,
1708    };
1709    $Attr->{border_collapse} = $Prop->{'border-collapse'};
1710    $Key->{border_collapse} = $Prop->{'border-collapse'};
1711    
1712    $Prop->{'empty-cells'} = {
1713      css => 'empty-cells',
1714      dom => 'empty_cells',
1715      key => 'empty_cells',
1716      parse => $one_keyword_parser,
1717      serialize => $default_serializer,
1718      keyword => {
1719        show => 1, hide => 1,
1720      },
1721      initial => ['KEYWORD', 'show'],
1722      inherited => 1,
1723      compute => $compute_as_specified,
1724    };
1725    $Attr->{empty_cells} = $Prop->{'empty-cells'};
1726    $Key->{empty_cells} = $Prop->{'empty-cells'};
1727    
1728  $Prop->{'z-index'} = {  $Prop->{'z-index'} = {
1729    css => 'z-index',    css => 'z-index',
1730    dom => 'z_index',    dom => 'z_index',
# Line 907  $Prop->{'z-index'} = { Line 1756  $Prop->{'z-index'} = {
1756        }        }
1757      }      }
1758            
1759      $onerror->(type => 'syntax error:'.$prop_name,      $onerror->(type => "syntax error:'$prop_name'",
1760                 level => $self->{must_level},                 level => $self->{must_level},
1761                   uri => \$self->{href},
1762                 token => $t);                 token => $t);
1763      return ($t, undef);      return ($t, undef);
1764    },    },
# Line 948  $Prop->{orphans} = { Line 1798  $Prop->{orphans} = {
1798        }        }
1799      }      }
1800            
1801      $onerror->(type => 'syntax error:'.$prop_name,      $onerror->(type => "syntax error:'$prop_name'",
1802                 level => $self->{must_level},                 level => $self->{must_level},
1803                   uri => \$self->{href},
1804                 token => $t);                 token => $t);
1805      return ($t, undef);      return ($t, undef);
1806    },    },
# Line 974  $Prop->{widows} = { Line 1825  $Prop->{widows} = {
1825  $Attr->{widows} = $Prop->{widows};  $Attr->{widows} = $Prop->{widows};
1826  $Key->{widows} = $Prop->{widows};  $Key->{widows} = $Prop->{widows};
1827    
1828    $Prop->{opacity} = {
1829      css => 'opacity',
1830      dom => 'opacity',
1831      key => 'opacity',
1832      parse => sub {
1833        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1834    
1835        my $sign = 1;
1836        if ($t->{type} == MINUS_TOKEN) {
1837          $t = $tt->get_next_token;
1838          $sign = -1;
1839        }
1840    
1841        if ($t->{type} == NUMBER_TOKEN) {
1842          ## ISSUE: See <http://suika.fam.cx/gate/2005/sw/opacity> for
1843          ## browser compatibility issue.
1844          my $value = $t->{number};
1845          $t = $tt->get_next_token;
1846          return ($t, {$prop_name => ["NUMBER", $sign * $value]});
1847        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
1848          my $value = lc $t->{value}; ## TODO: case
1849          $t = $tt->get_next_token;
1850          if ($value eq 'inherit') {
1851            return ($t, {$prop_name => ['INHERIT']});
1852          }
1853        }
1854        
1855        $onerror->(type => "syntax error:'$prop_name'",
1856                   level => $self->{must_level},
1857                   uri => \$self->{href},
1858                   token => $t);
1859        return ($t, undef);
1860      },
1861      serialize => $default_serializer,
1862      initial => ['NUMBER', 2],
1863      inherited => 1,
1864      compute => sub {
1865        my ($self, $element, $prop_name, $specified_value) = @_;
1866    
1867        if (defined $specified_value) {
1868          if ($specified_value->[0] eq 'NUMBER') {
1869            if ($specified_value->[1] < 0) {
1870              return ['NUMBER', 0];
1871            } elsif ($specified_value->[1] > 1) {
1872              return ['NUMBER', 1];
1873            }
1874          }
1875        }
1876    
1877        return $specified_value;
1878      },
1879      serialize_multiple => sub {
1880        ## NOTE: This CODE is necessary to avoid two 'opacity' properties
1881        ## are outputed in |cssText| (for 'opacity' and for '-moz-opacity').
1882        return {opacity => shift->opacity},
1883      },
1884    };
1885    $Attr->{opacity} = $Prop->{opacity};
1886    $Key->{opacity} = $Prop->{opacity};
1887    
1888    $Prop->{'-moz-opacity'} = $Prop->{opacity};
1889    $Attr->{_moz_opacity} = $Attr->{opacity};
1890    
1891    my $length_unit = {
1892      em => 1, ex => 1, px => 1,
1893      in => 1, cm => 1, mm => 1, pt => 1, pc => 1,
1894    };
1895    
1896    $Prop->{'font-size'} = {
1897      css => 'font-size',
1898      dom => 'font_size',
1899      key => 'font_size',
1900      parse => sub {
1901        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1902    
1903        my $sign = 1;
1904        if ($t->{type} == MINUS_TOKEN) {
1905          $t = $tt->get_next_token;
1906          $sign = -1;
1907        }
1908    
1909        if ($t->{type} == DIMENSION_TOKEN) {
1910          my $value = $t->{number} * $sign;
1911          my $unit = lc $t->{value}; ## TODO: case
1912          $t = $tt->get_next_token;
1913          if ($length_unit->{$unit} and $value >= 0) {
1914            return ($t, {$prop_name => ['DIMENSION', $value, $unit]});
1915          }
1916        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
1917          my $value = $t->{number} * $sign;
1918          $t = $tt->get_next_token;
1919          return ($t, {$prop_name => ['PERCENTAGE', $value]}) if $value >= 0;
1920        } elsif ($t->{type} == NUMBER_TOKEN and
1921                 ($self->{unitless_px} or $t->{number} == 0)) {
1922          my $value = $t->{number} * $sign;
1923          $t = $tt->get_next_token;
1924          return ($t, {$prop_name => ['DIMENSION', $value, 'px']}) if $value >= 0;
1925        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
1926          my $value = lc $t->{value}; ## TODO: case
1927          $t = $tt->get_next_token;
1928          if ({
1929               'xx-small' => 1, 'x-small' => 1, small => 1, medium => 1,
1930               large => 1, 'x-large' => 1, 'xx-large' => 1,
1931               '-manakai-xxx-large' => 1, # -webkit-xxx-large
1932               larger => 1, smaller => 1,
1933              }->{$value}) {
1934            return ($t, {$prop_name => ['KEYWORD', $value]});        
1935          } elsif ($value eq 'inherit') {
1936            return ($t, {$prop_name => ['INHERIT']});
1937          }
1938        }
1939        
1940        $onerror->(type => "syntax error:'$prop_name",
1941                   level => $self->{must_level},
1942                   uri => \$self->{href},
1943                   token => $t);
1944        return ($t, undef);
1945      },
1946      serialize => $default_serializer,
1947      initial => ['KEYWORD', 'medium'],
1948      inherited => 1,
1949      compute => sub {
1950        my ($self, $element, $prop_name, $specified_value) = @_;
1951        
1952        if (defined $specified_value) {
1953          if ($specified_value->[0] eq 'DIMENSION') {
1954            my $unit = $specified_value->[2];
1955            my $value = $specified_value->[1];
1956    
1957            if ($unit eq 'em' or $unit eq 'ex') {
1958              $value *= 0.5 if $unit eq 'ex';
1959              ## TODO: Preferred way to determine the |ex| size is defined
1960              ## in CSS 2.1.
1961    
1962              my $parent_element = $element->manakai_parent_element;
1963              if (defined $parent_element) {
1964                $value *= $self->get_computed_value ($parent_element, $prop_name)
1965                    ->[1];
1966              } else {
1967                $value *= $self->{font_size}->[3]; # medium
1968              }
1969              $unit = 'px';
1970            } elsif ({in => 1, cm => 1, mm => 1, pt => 1, pc => 1}->{$unit}) {
1971              ($value *= 12, $unit = 'pc') if $unit eq 'pc';
1972              ($value /= 72, $unit = 'in') if $unit eq 'pt';
1973              ($value *= 2.54, $unit = 'cm') if $unit eq 'in';
1974              ($value *= 10, $unit = 'mm') if $unit eq 'cm';
1975              ($value /= 0.26, $unit = 'px') if $unit eq 'mm';
1976            }
1977            ## else: consistency error
1978    
1979            return ['DIMENSION', $value, $unit];
1980          } elsif ($specified_value->[0] eq 'PERCENTAGE') {
1981            my $parent_element = $element->manakai_parent_element;
1982            my $parent_cv;
1983            if (defined $parent_element) {
1984              $parent_cv = $self->get_computed_value
1985                  ($parent_element, $prop_name);
1986            } else {
1987              $parent_cv = [undef, $self->{font_size}->[3]];
1988            }
1989            return ['DIMENSION', $parent_cv->[1] * $specified_value->[1] / 100,
1990                    'px'];
1991          } elsif ($specified_value->[0] eq 'KEYWORD') {
1992            if ($specified_value->[1] eq 'larger') {
1993              my $parent_element = $element->manakai_parent_element;
1994              if (defined $parent_element) {
1995                my $parent_cv = $self->get_computed_value
1996                    ($parent_element, $prop_name);
1997                return ['DIMENSION',
1998                        $self->{get_larger_font_size}->($self, $parent_cv->[1]),
1999                        'px'];
2000              } else { ## 'larger' relative to 'medium', initial of 'font-size'
2001                return ['DIMENSION', $self->{font_size}->[4], 'px'];
2002              }
2003            } elsif ($specified_value->[1] eq 'smaller') {
2004              my $parent_element = $element->manakai_parent_element;
2005              if (defined $parent_element) {
2006                my $parent_cv = $self->get_computed_value
2007                    ($parent_element, $prop_name);
2008                return ['DIMENSION',
2009                        $self->{get_smaller_font_size}->($self, $parent_cv->[1]),
2010                        'px'];
2011              } else { ## 'smaller' relative to 'medium', initial of 'font-size'
2012                return ['DIMENSION', $self->{font_size}->[2], 'px'];
2013              }
2014            } else {
2015              return ['DIMENSION', $self->{font_size}->[{
2016                'xx-small' => 0,
2017                'x-small' => 1,
2018                small => 2,
2019                medium => 3,
2020                large => 4,
2021                'x-large' => 5,
2022                'xx-large' => 6,
2023                '-manakai-xxx-large' => 7,
2024              }->{$specified_value->[1]}], 'px'];
2025            }
2026          }
2027        }
2028        
2029        return $specified_value;
2030      },
2031    };
2032    $Attr->{font_size} = $Prop->{'font-size'};
2033    $Key->{font_size} = $Prop->{'font-size'};
2034    
2035    my $compute_length = sub {
2036      my ($self, $element, $prop_name, $specified_value) = @_;
2037      
2038      if (defined $specified_value) {
2039        if ($specified_value->[0] eq 'DIMENSION') {
2040          my $unit = $specified_value->[2];
2041          my $value = $specified_value->[1];
2042    
2043          if ($unit eq 'em' or $unit eq 'ex') {
2044            $value *= 0.5 if $unit eq 'ex';
2045            ## TODO: Preferred way to determine the |ex| size is defined
2046            ## in CSS 2.1.
2047    
2048            $value *= $self->get_computed_value ($element, 'font-size')->[1];
2049            $unit = 'px';
2050          } elsif ({in => 1, cm => 1, mm => 1, pt => 1, pc => 1}->{$unit}) {
2051            ($value *= 12, $unit = 'pc') if $unit eq 'pc';
2052            ($value /= 72, $unit = 'in') if $unit eq 'pt';
2053            ($value *= 2.54, $unit = 'cm') if $unit eq 'in';
2054            ($value *= 10, $unit = 'mm') if $unit eq 'cm';
2055            ($value /= 0.26, $unit = 'px') if $unit eq 'mm';
2056          }
2057    
2058          return ['DIMENSION', $value, $unit];
2059        }
2060      }
2061      
2062      return $specified_value;
2063    }; # $compute_length
2064    
2065    $Prop->{'letter-spacing'} = {
2066      css => 'letter-spacing',
2067      dom => 'letter_spacing',
2068      key => 'letter_spacing',
2069      parse => sub {
2070        my ($self, $prop_name, $tt, $t, $onerror) = @_;
2071    
2072        ## NOTE: Used also for 'word-spacing', '-manakai-border-spacing-x',
2073        ## and '-manakai-border-spacing-y'.
2074    
2075        my $sign = 1;
2076        if ($t->{type} == MINUS_TOKEN) {
2077          $t = $tt->get_next_token;
2078          $sign = -1;
2079        }
2080        my $allow_negative = $Prop->{$prop_name}->{allow_negative};
2081    
2082        if ($t->{type} == DIMENSION_TOKEN) {
2083          my $value = $t->{number} * $sign;
2084          my $unit = lc $t->{value}; ## TODO: case
2085          $t = $tt->get_next_token;
2086          if ($length_unit->{$unit} and ($allow_negative or $value >= 0)) {
2087            return ($t, {$prop_name => ['DIMENSION', $value, $unit]});
2088          }
2089        } elsif ($t->{type} == NUMBER_TOKEN and
2090                 ($self->{unitless_px} or $t->{number} == 0)) {
2091          my $value = $t->{number} * $sign;
2092          $t = $tt->get_next_token;
2093          return ($t, {$prop_name => ['DIMENSION', $value, 'px']})
2094              if $allow_negative or $value >= 0;
2095        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
2096          my $value = lc $t->{value}; ## TODO: case
2097          $t = $tt->get_next_token;
2098          if ($Prop->{$prop_name}->{keyword}->{$value}) {
2099            return ($t, {$prop_name => ['KEYWORD', $value]});        
2100          } elsif ($value eq 'inherit') {
2101            return ($t, {$prop_name => ['INHERIT']});
2102          }
2103        }
2104        
2105        $onerror->(type => "syntax error:'$prop_name'",
2106                   level => $self->{must_level},
2107                   uri => \$self->{href},
2108                   token => $t);
2109        return ($t, undef);
2110      },
2111      allow_negative => 1,
2112      keyword => {normal => 1},
2113      serialize => $default_serializer,
2114      initial => ['KEYWORD', 'normal'],
2115      inherited => 1,
2116      compute => $compute_length,
2117    };
2118    $Attr->{letter_spacing} = $Prop->{'letter-spacing'};
2119    $Key->{letter_spacing} = $Prop->{'letter-spacing'};
2120    
2121    $Prop->{'word-spacing'} = {
2122      css => 'word-spacing',
2123      dom => 'word_spacing',
2124      key => 'word_spacing',
2125      parse => $Prop->{'letter-spacing'}->{parse},
2126      allow_negative => 1,
2127      keyword => {normal => 1},
2128      serialize => $default_serializer,
2129      initial => ['KEYWORD', 'normal'],
2130      inherited => 1,
2131      compute => $compute_length,
2132    };
2133    $Attr->{word_spacing} = $Prop->{'word-spacing'};
2134    $Key->{word_spacing} = $Prop->{'word-spacing'};
2135    
2136    $Prop->{'-manakai-border-spacing-x'} = {
2137      css => '-manakai-border-spacing-x',
2138      dom => '_manakai_border_spacing_x',
2139      key => 'border_spacing_x',
2140      parse => $Prop->{'letter-spacing'}->{parse},
2141      #allow_negative => 0,
2142      #keyword => {},
2143      serialize => $default_serializer,
2144      serialize_multiple => sub {
2145        my $self = shift;
2146        
2147        local $Error::Depth = $Error::Depth + 1;
2148        my $x = $self->_manakai_border_spacing_x;
2149        my $y = $self->_manakai_border_spacing_y;
2150        my $xi = $self->get_property_priority ('-manakai-border-spacing-x');
2151        my $yi = $self->get_property_priority ('-manakai-border-spacing-y');
2152        $xi = ' !' . $xi if length $xi;
2153        $yi = ' !' . $yi if length $yi;
2154        if (length $x) {
2155          if (length $y) {
2156            if ($xi eq $yi) {
2157              if ($x eq $y) {
2158                return {'border-spacing' => $x . $xi};
2159              } else {
2160                return {'border-spacing' => $x . ' ' . $y . $xi};
2161              }
2162            } else {
2163              return {'-manakai-border-spacing-x' => $x . $xi,
2164                      '-manakai-border-spacing-y' => $y . $yi};
2165            }
2166          } else {
2167            return {'-manakai-border-spacing-x' => $x . $xi};
2168          }
2169        } else {
2170          if (length $y) {
2171            return {'-manakai-border-spacing-y' => $y . $yi};
2172          } else {
2173            return {};
2174          }
2175        }
2176      },
2177      initial => ['DIMENSION', 0, 'px'],
2178      inherited => 1,
2179      compute => $compute_length,
2180    };
2181    $Attr->{_manakai_border_spacing_x} = $Prop->{'-manakai-border-spacing-x'};
2182    $Key->{border_spacing_x} = $Prop->{'-manakai-border-spacing-x'};
2183    
2184    $Prop->{'-manakai-border-spacing-y'} = {
2185      css => '-manakai-border-spacing-y',
2186      dom => '_manakai_border_spacing_y',
2187      key => 'border_spacing_y',
2188      parse => $Prop->{'letter-spacing'}->{parse},
2189      #allow_negative => 0,
2190      #keyword => {},
2191      serialize => $default_serializer,
2192      serialize_multiple => $Prop->{'-manakai-border-spacing-x'}
2193          ->{serialize_multiple},
2194      initial => ['DIMENSION', 0, 'px'],
2195      inherited => 1,
2196      compute => $compute_length,
2197    };
2198    $Attr->{_manakai_border_spacing_y} = $Prop->{'-manakai-border-spacing-y'};
2199    $Key->{border_spacing_y} = $Prop->{'-manakai-border-spacing-y'};
2200    
2201    $Prop->{'margin-top'} = {
2202      css => 'margin-top',
2203      dom => 'margin_top',
2204      key => 'margin_top',
2205      parse => sub {
2206        my ($self, $prop_name, $tt, $t, $onerror) = @_;
2207    
2208        ## NOTE: Used for 'margin-top', 'margin-right', 'margin-bottom',
2209        ## 'margin-left', 'top', 'right', 'bottom', 'left', 'padding-top',
2210        ## 'padding-right', 'padding-bottom', 'padding-left',
2211        ## 'border-top-width', 'border-right-width', 'border-bottom-width',
2212        ## 'border-left-width', 'text-indent', 'background-position-x',
2213        ## and 'background-position-y'.
2214    
2215        my $sign = 1;
2216        my $has_sign;
2217        if ($t->{type} == MINUS_TOKEN) {
2218          $t = $tt->get_next_token;
2219          $has_sign = 1;
2220          $sign = -1;
2221        } elsif ($t->{type} == PLUS_TOKEN) {
2222          $t = $tt->get_next_token;
2223          $has_sign = 1;
2224        }
2225        my $allow_negative = $Prop->{$prop_name}->{allow_negative};
2226    
2227        if ($t->{type} == DIMENSION_TOKEN) {
2228          my $value = $t->{number} * $sign;
2229          my $unit = lc $t->{value}; ## TODO: case
2230          $t = $tt->get_next_token;
2231          if ($length_unit->{$unit} and ($allow_negative or $value >= 0)) {
2232            return ($t, {$prop_name => ['DIMENSION', $value, $unit]});
2233          }
2234        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
2235          my $value = $t->{number} * $sign;
2236          $t = $tt->get_next_token;
2237          return ($t, {$prop_name => ['PERCENTAGE', $value]})
2238              if $allow_negative or $value >= 0;
2239        } elsif ($t->{type} == NUMBER_TOKEN and
2240                 ($self->{unitless_px} or $t->{number} == 0)) {
2241          my $value = $t->{number} * $sign;
2242          $t = $tt->get_next_token;
2243          return ($t, {$prop_name => ['DIMENSION', $value, 'px']})
2244              if $allow_negative or $value >= 0;
2245        } elsif (not $has_sign and $t->{type} == IDENT_TOKEN) {
2246          my $value = lc $t->{value}; ## TODO: case
2247          if ($Prop->{$prop_name}->{keyword}->{$value}) {
2248            $t = $tt->get_next_token;
2249            return ($t, {$prop_name => ['KEYWORD', $value]});        
2250          } elsif ($value eq 'inherit') {
2251            $t = $tt->get_next_token;
2252            return ($t, {$prop_name => ['INHERIT']});
2253          }
2254          ## NOTE: In the "else" case, don't procede the |$t| pointer
2255          ## for the support of 'border-top' property (and similar ones).
2256        }
2257        
2258        $onerror->(type => "syntax error:'$prop_name'",
2259                   level => $self->{must_level},
2260                   uri => \$self->{href},
2261                   token => $t);
2262        return ($t, undef);
2263      },
2264      allow_negative => 1,
2265      keyword => {auto => 1},
2266      serialize => $default_serializer,
2267      serialize_multiple => sub {
2268        my $self = shift;
2269    
2270        my $use_shorthand = 1;
2271        my $t = $self->margin_top;
2272        undef $use_shorthand unless length $t;
2273        my $t_i = $self->get_property_priority ('margin-top');
2274        my $r = $self->margin_right;
2275        undef $use_shorthand
2276            if not length $r or
2277                ($r eq 'inherit' and $t ne 'inherit') or
2278                ($t eq 'inherit' and $r ne 'inherit');
2279        my $r_i = $self->get_property_priority ('margin-right');
2280        undef $use_shorthand unless $r_i eq $t_i;
2281        my $b = $self->margin_bottom;
2282        undef $use_shorthand
2283            if not length $b or
2284                ($b eq 'inherit' and $t ne 'inherit') or
2285                ($t eq 'inherit' and $b ne 'inherit');
2286        my $b_i = $self->get_property_priority ('margin-bottom');
2287        undef $use_shorthand unless $b_i eq $t_i;
2288        my $l = $self->margin_left;
2289        undef $use_shorthand
2290            if not length $l or
2291                ($l eq 'inherit' and $t ne 'inherit') or
2292                ($t eq 'inherit' and $l ne 'inherit');
2293        my $l_i = $self->get_property_priority ('margin-left');
2294        undef $use_shorthand unless $l_i eq $t_i;
2295    
2296        if ($use_shorthand) {
2297          $b .= ' ' . $l if $r ne $l;
2298          $r .= ' ' . $b if $t ne $b;
2299          $t .= ' ' . $r if $t ne $r;
2300          $t .= ' ! ' . $t_i if length $t_i;
2301          return {margin => $t};
2302        } else {
2303          my $v = {};
2304          if (length $t) {
2305            $v->{'margin-top'} = $t;
2306            $v->{'margin-top'} .= ' ! ' . $t_i if length $t_i;
2307          }
2308          if (length $r) {
2309            $v->{'margin-right'} = $r;
2310            $v->{'margin-right'} .= ' ! ' . $r_i if length $r_i;
2311          }
2312          if (length $b) {
2313            $v->{'margin-bottom'} = $b;
2314            $v->{'margin-bottom'} .= ' ! ' . $b_i if length $b_i;
2315          }
2316          if (length $l) {
2317            $v->{'margin-left'} = $l;
2318            $v->{'margin-left'} .= ' ! ' . $l_i if length $l_i;
2319          }
2320          return $v;
2321        }
2322      },
2323      initial => ['DIMENSION', 0, 'px'],
2324      #inherited => 0,
2325      compute => $compute_length,
2326    };
2327    $Attr->{margin_top} = $Prop->{'margin-top'};
2328    $Key->{margin_top} = $Prop->{'margin-top'};
2329    
2330    $Prop->{'margin-bottom'} = {
2331      css => 'margin-bottom',
2332      dom => 'margin_bottom',
2333      key => 'margin_bottom',
2334      parse => $Prop->{'margin-top'}->{parse},
2335      allow_negative => 1,
2336      keyword => {auto => 1},
2337      serialize => $default_serializer,
2338      serialize_multiple => $Prop->{'margin-top'}->{serialize_multiple},
2339      initial => ['DIMENSION', 0, 'px'],
2340      #inherited => 0,
2341      compute => $compute_length,
2342    };
2343    $Attr->{margin_bottom} = $Prop->{'margin-bottom'};
2344    $Key->{margin_bottom} = $Prop->{'margin-bottom'};
2345    
2346    $Prop->{'margin-right'} = {
2347      css => 'margin-right',
2348      dom => 'margin_right',
2349      key => 'margin_right',
2350      parse => $Prop->{'margin-top'}->{parse},
2351      allow_negative => 1,
2352      keyword => {auto => 1},
2353      serialize => $default_serializer,
2354      serialize_multiple => $Prop->{'margin-top'}->{serialize_multiple},
2355      initial => ['DIMENSION', 0, 'px'],
2356      #inherited => 0,
2357      compute => $compute_length,
2358    };
2359    $Attr->{margin_right} = $Prop->{'margin-right'};
2360    $Key->{margin_right} = $Prop->{'margin-right'};
2361    
2362    $Prop->{'margin-left'} = {
2363      css => 'margin-left',
2364      dom => 'margin_left',
2365      key => 'margin_left',
2366      parse => $Prop->{'margin-top'}->{parse},
2367      allow_negative => 1,
2368      keyword => {auto => 1},
2369      serialize => $default_serializer,
2370      serialize_multiple => $Prop->{'margin-top'}->{serialize_multiple},
2371      initial => ['DIMENSION', 0, 'px'],
2372      #inherited => 0,
2373      compute => $compute_length,
2374    };
2375    $Attr->{margin_left} = $Prop->{'margin-left'};
2376    $Key->{margin_left} = $Prop->{'margin-left'};
2377    
2378    $Prop->{top} = {
2379      css => 'top',
2380      dom => 'top',
2381      key => 'top',
2382      parse => $Prop->{'margin-top'}->{parse},
2383      allow_negative => 1,
2384      keyword => {auto => 1},
2385      serialize => $default_serializer,
2386      initial => ['KEYWORD', 'auto'],
2387      #inherited => 0,
2388      compute_multiple => sub {
2389        my ($self, $element, $eid, $prop_name) = @_;
2390    
2391        my $pos_value = $self->get_computed_value ($element, 'position');
2392        if (defined $pos_value and $pos_value->[0] eq 'KEYWORD') {
2393          if ($pos_value->[1] eq 'static') {
2394            $self->{computed_value}->{$eid}->{top} = ['KEYWORD', 'auto'];
2395            $self->{computed_value}->{$eid}->{bottom} = ['KEYWORD', 'auto'];
2396            return;
2397          } elsif ($pos_value->[1] eq 'relative') {
2398            my $top_specified = $self->get_specified_value_no_inherit
2399              ($element, 'top');
2400            if (defined $top_specified and
2401                ($top_specified->[0] eq 'DIMENSION' or
2402                 $top_specified->[0] eq 'PERCENTAGE')) {
2403              my $tv = $self->{computed_value}->{$eid}->{top}
2404                  = $compute_length->($self, $element, 'top', $top_specified);
2405              $self->{computed_value}->{$eid}->{bottom}
2406                  = [$tv->[0], -$tv->[1], $tv->[2]];
2407            } else { # top: auto
2408              my $bottom_specified = $self->get_specified_value_no_inherit
2409                  ($element, 'bottom');
2410              if (defined $bottom_specified and
2411                  ($bottom_specified->[0] eq 'DIMENSION' or
2412                   $bottom_specified->[0] eq 'PERCENTAGE')) {
2413                my $tv = $self->{computed_value}->{$eid}->{bottom}
2414                    = $compute_length->($self, $element, 'bottom',
2415                                        $bottom_specified);
2416                $self->{computed_value}->{$eid}->{top}
2417                    = [$tv->[0], -$tv->[1], $tv->[2]];
2418              } else { # bottom: auto
2419                $self->{computed_value}->{$eid}->{top} = ['DIMENSION', 0, 'px'];
2420                $self->{computed_value}->{$eid}->{bottom} = ['DIMENSION', 0, 'px'];
2421              }
2422            }
2423            return;
2424          }
2425        }
2426    
2427        my $top_specified = $self->get_specified_value_no_inherit
2428            ($element, 'top');
2429        $self->{computed_value}->{$eid}->{top}
2430            = $compute_length->($self, $element, 'top', $top_specified);
2431        my $bottom_specified = $self->get_specified_value_no_inherit
2432            ($element, 'bottom');
2433        $self->{computed_value}->{$eid}->{bottom}
2434            = $compute_length->($self, $element, 'bottom', $bottom_specified);
2435      },
2436    };
2437    $Attr->{top} = $Prop->{top};
2438    $Key->{top} = $Prop->{top};
2439    
2440    $Prop->{bottom} = {
2441      css => 'bottom',
2442      dom => 'bottom',
2443      key => 'bottom',
2444      parse => $Prop->{'margin-top'}->{parse},
2445      allow_negative => 1,
2446      keyword => {auto => 1},
2447      serialize => $default_serializer,
2448      initial => ['KEYWORD', 'auto'],
2449      #inherited => 0,
2450      compute_multiple => $Prop->{top}->{compute_multiple},
2451    };
2452    $Attr->{bottom} = $Prop->{bottom};
2453    $Key->{bottom} = $Prop->{bottom};
2454    
2455    $Prop->{left} = {
2456      css => 'left',
2457      dom => 'left',
2458      key => 'left',
2459      parse => $Prop->{'margin-top'}->{parse},
2460      allow_negative => 1,
2461      keyword => {auto => 1},
2462      serialize => $default_serializer,
2463      initial => ['KEYWORD', 'auto'],
2464      #inherited => 0,
2465      compute_multiple => sub {
2466        my ($self, $element, $eid, $prop_name) = @_;
2467    
2468        my $pos_value = $self->get_computed_value ($element, 'position');
2469        if (defined $pos_value and $pos_value->[0] eq 'KEYWORD') {
2470          if ($pos_value->[1] eq 'static') {
2471            $self->{computed_value}->{$eid}->{left} = ['KEYWORD', 'auto'];
2472            $self->{computed_value}->{$eid}->{right} = ['KEYWORD', 'auto'];
2473            return;
2474          } elsif ($pos_value->[1] eq 'relative') {
2475            my $left_specified = $self->get_specified_value_no_inherit
2476                ($element, 'left');
2477            if (defined $left_specified and
2478                ($left_specified->[0] eq 'DIMENSION' or
2479                 $left_specified->[0] eq 'PERCENTAGE')) {
2480              my $right_specified = $self->get_specified_value_no_inherit
2481                  ($element, 'right');
2482              if (defined $right_specified and
2483                  ($right_specified->[0] eq 'DIMENSION' or
2484                   $right_specified->[0] eq 'PERCENTAGE')) {
2485                my $direction = $self->get_computed_value ($element, 'direction');
2486                if (defined $direction and $direction->[0] eq 'KEYWORD' and
2487                    $direction->[0] eq 'ltr') {
2488                  my $tv = $self->{computed_value}->{$eid}->{left}
2489                      = $compute_length->($self, $element, 'left',
2490                                          $left_specified);
2491                  $self->{computed_value}->{$eid}->{right}
2492                      = [$tv->[0], -$tv->[1], $tv->[2]];
2493                } else {
2494                  my $tv = $self->{computed_value}->{$eid}->{right}
2495                      = $compute_length->($self, $element, 'right',
2496                                          $right_specified);
2497                  $self->{computed_value}->{$eid}->{left}
2498                      = [$tv->[0], -$tv->[1], $tv->[2]];
2499                }
2500              } else {
2501                my $tv = $self->{computed_value}->{$eid}->{left}
2502                    = $compute_length->($self, $element, 'left', $left_specified);
2503                $self->{computed_value}->{$eid}->{right}
2504                    = [$tv->[0], -$tv->[1], $tv->[2]];
2505              }
2506            } else { # left: auto
2507              my $right_specified = $self->get_specified_value_no_inherit
2508                  ($element, 'right');
2509              if (defined $right_specified and
2510                  ($right_specified->[0] eq 'DIMENSION' or
2511                   $right_specified->[0] eq 'PERCENTAGE')) {
2512                my $tv = $self->{computed_value}->{$eid}->{right}
2513                    = $compute_length->($self, $element, 'right',
2514                                        $right_specified);
2515                $self->{computed_value}->{$eid}->{left}
2516                    = [$tv->[0], -$tv->[1], $tv->[2]];
2517              } else { # right: auto
2518                $self->{computed_value}->{$eid}->{left} = ['DIMENSION', 0, 'px'];
2519                $self->{computed_value}->{$eid}->{right} = ['DIMENSION', 0, 'px'];
2520              }
2521            }
2522            return;
2523          }
2524        }
2525    
2526        my $left_specified = $self->get_specified_value_no_inherit
2527            ($element, 'left');
2528        $self->{computed_value}->{$eid}->{left}
2529            = $compute_length->($self, $element, 'left', $left_specified);
2530        my $right_specified = $self->get_specified_value_no_inherit
2531            ($element, 'right');
2532        $self->{computed_value}->{$eid}->{right}
2533            = $compute_length->($self, $element, 'right', $right_specified);
2534      },
2535    };
2536    $Attr->{left} = $Prop->{left};
2537    $Key->{left} = $Prop->{left};
2538    
2539    $Prop->{right} = {
2540      css => 'right',
2541      dom => 'right',
2542      key => 'right',
2543      parse => $Prop->{'margin-top'}->{parse},
2544      serialize => $default_serializer,
2545      initial => ['KEYWORD', 'auto'],
2546      #inherited => 0,
2547      compute_multiple => $Prop->{left}->{compute_multiple},
2548    };
2549    $Attr->{right} = $Prop->{right};
2550    $Key->{right} = $Prop->{right};
2551    
2552    $Prop->{width} = {
2553      css => 'width',
2554      dom => 'width',
2555      key => 'width',
2556      parse => $Prop->{'margin-top'}->{parse},
2557      #allow_negative => 0,
2558      keyword => {auto => 1},
2559      serialize => $default_serializer,
2560      initial => ['KEYWORD', 'auto'],
2561      #inherited => 0,
2562      compute => $compute_length,
2563          ## NOTE: See <http://suika.fam.cx/gate/2005/sw/width> for
2564          ## browser compatibility issues.
2565    };
2566    $Attr->{width} = $Prop->{width};
2567    $Key->{width} = $Prop->{width};
2568    
2569    $Prop->{'min-width'} = {
2570      css => 'min-width',
2571      dom => 'min_width',
2572      key => 'min_width',
2573      parse => $Prop->{'margin-top'}->{parse},
2574      #allow_negative => 0,
2575      #keyword => {},
2576      serialize => $default_serializer,
2577      initial => ['DIMENSION', 0, 'px'],
2578      #inherited => 0,
2579      compute => $compute_length,
2580    };
2581    $Attr->{min_width} = $Prop->{'min-width'};
2582    $Key->{min_width} = $Prop->{'min-width'};
2583    
2584    $Prop->{'max-width'} = {
2585      css => 'max-width',
2586      dom => 'max_width',
2587      key => 'max_width',
2588      parse => $Prop->{'margin-top'}->{parse},
2589      #allow_negative => 0,
2590      keyword => {none => 1},
2591      serialize => $default_serializer,
2592      initial => ['KEYWORD', 'none'],
2593      #inherited => 0,
2594      compute => $compute_length,
2595    };
2596    $Attr->{max_width} = $Prop->{'max-width'};
2597    $Key->{max_width} = $Prop->{'max-width'};
2598    
2599    $Prop->{height} = {
2600      css => 'height',
2601      dom => 'height',
2602      key => 'height',
2603      parse => $Prop->{'margin-top'}->{parse},
2604      #allow_negative => 0,
2605      keyword => {auto => 1},
2606      serialize => $default_serializer,
2607      initial => ['KEYWORD', 'auto'],
2608      #inherited => 0,
2609      compute => $compute_length,
2610          ## NOTE: See <http://suika.fam.cx/gate/2005/sw/height> for
2611          ## browser compatibility issues.
2612    };
2613    $Attr->{height} = $Prop->{height};
2614    $Key->{height} = $Prop->{height};
2615    
2616    $Prop->{'min-height'} = {
2617      css => 'min-height',
2618      dom => 'min_height',
2619      key => 'min_height',
2620      parse => $Prop->{'margin-top'}->{parse},
2621      #allow_negative => 0,
2622      #keyword => {},
2623      serialize => $default_serializer,
2624      initial => ['DIMENSION', 0, 'px'],
2625      #inherited => 0,
2626      compute => $compute_length,
2627    };
2628    $Attr->{min_height} = $Prop->{'min-height'};
2629    $Key->{min_height} = $Prop->{'min-height'};
2630    
2631    $Prop->{'max-height'} = {
2632      css => 'max-height',
2633      dom => 'max_height',
2634      key => 'max_height',
2635      parse => $Prop->{'margin-top'}->{parse},
2636      #allow_negative => 0,
2637      keyword => {none => 1},
2638      serialize => $default_serializer,
2639      initial => ['KEYWORD', 'none'],
2640      #inherited => 0,
2641      compute => $compute_length,
2642    };
2643    $Attr->{max_height} = $Prop->{'max-height'};
2644    $Key->{max_height} = $Prop->{'max-height'};
2645    
2646    $Prop->{'line-height'} = {
2647      css => 'line-height',
2648      dom => 'line_height',
2649      key => 'line_height',
2650      parse => sub {
2651        my ($self, $prop_name, $tt, $t, $onerror) = @_;
2652    
2653        ## NOTE: Similar to 'margin-top', but different handling
2654        ## for unitless numbers.
2655    
2656        my $sign = 1;
2657        if ($t->{type} == MINUS_TOKEN) {
2658          $t = $tt->get_next_token;
2659          $sign = -1;
2660        }
2661        my $allow_negative = $Prop->{$prop_name}->{allow_negative};
2662    
2663        if ($t->{type} == DIMENSION_TOKEN) {
2664          my $value = $t->{number} * $sign;
2665          my $unit = lc $t->{value}; ## TODO: case
2666          $t = $tt->get_next_token;
2667          if ($length_unit->{$unit} and $value >= 0) {
2668            return ($t, {$prop_name => ['DIMENSION', $value, $unit]});
2669          }
2670        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
2671          my $value = $t->{number} * $sign;
2672          $t = $tt->get_next_token;
2673          return ($t, {$prop_name => ['PERCENTAGE', $value]})
2674              if $value >= 0;
2675        } elsif ($t->{type} == NUMBER_TOKEN) {
2676          my $value = $t->{number} * $sign;
2677          $t = $tt->get_next_token;
2678          return ($t, {$prop_name => ['NUMBER', $value]}) if $value >= 0;
2679        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
2680          my $value = lc $t->{value}; ## TODO: case
2681          $t = $tt->get_next_token;
2682          if ($value eq 'normal') {
2683            return ($t, {$prop_name => ['KEYWORD', $value]});        
2684          } elsif ($value eq 'inherit') {
2685            return ($t, {$prop_name => ['INHERIT']});
2686          }
2687        }
2688        
2689        $onerror->(type => "syntax error:'$prop_name'",
2690                   level => $self->{must_level},
2691                   uri => \$self->{href},
2692                   token => $t);
2693        return ($t, undef);
2694      },
2695      serialize => $default_serializer,
2696      initial => ['KEYWORD', 'normal'],
2697      inherited => 1,
2698      compute => $compute_length,
2699    };
2700    $Attr->{line_height} = $Prop->{'line-height'};
2701    $Key->{line_height} = $Prop->{'line-height'};
2702    
2703    $Prop->{'vertical-align'} = {
2704      css => 'vertical-align',
2705      dom => 'vertical_align',
2706      key => 'vertical_align',
2707      parse => $Prop->{'margin-top'}->{parse},
2708      allow_negative => 1,
2709      keyword => {
2710        baseline => 1, sub => 1, super => 1, top => 1, 'text-top' => 1,
2711        middle => 1, bottom => 1, 'text-bottom' => 1,
2712      },
2713      ## NOTE: Currently, we don't support option to select subset of keywords
2714      ## supported by application (i.e.
2715      ## $parser->{prop_value}->{'line-height'->{$keyword}).  Should we support
2716      ## it?
2717      serialize => $default_serializer,
2718      initial => ['KEYWORD', 'baseline'],
2719      #inherited => 0,
2720      compute => $compute_length,
2721          ## NOTE: See <http://suika.fam.cx/gate/2005/sw/vertical-align> for
2722          ## browser compatibility issues.
2723    };
2724    $Attr->{vertical_align} = $Prop->{'vertical-align'};
2725    $Key->{vertical_align} = $Prop->{'vertical-align'};
2726    
2727    $Prop->{'text-indent'} = {
2728      css => 'text-indent',
2729      dom => 'text_indent',
2730      key => 'text_indent',
2731      parse => $Prop->{'margin-top'}->{parse},
2732      allow_negative => 1,
2733      keyword => {},
2734      serialize => $default_serializer,
2735      initial => ['DIMENSION', 0, 'px'],
2736      inherited => 1,
2737      compute => $compute_length,
2738    };
2739    $Attr->{text_indent} = $Prop->{'text-indent'};
2740    $Key->{text_indent} = $Prop->{'text-indent'};
2741    
2742    $Prop->{'background-position-x'} = {
2743      css => 'background-position-x',
2744      dom => 'background_position_x',
2745      key => 'background_position_x',
2746      parse => $Prop->{'margin-top'}->{parse},
2747      allow_negative => 1,
2748      keyword => {left => 1, center => 1, right => 1},
2749      serialize => $default_serializer,
2750      initial => ['PERCENTAGE', 0],
2751      #inherited => 0,
2752      compute => sub {
2753        my ($self, $element, $prop_name, $specified_value) = @_;
2754    
2755        if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
2756          my $v = {
2757            left => 0, center => 50, right => 100, top => 0, bottom => 100,
2758          }->{$specified_value->[1]};
2759          if (defined $v) {
2760            return ['PERCENTAGE', $v];
2761          } else {
2762            return $specified_value;
2763          }
2764        } else {
2765          return $compute_length->(@_);
2766        }
2767      },
2768      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
2769    };
2770    $Attr->{background_position_x} = $Prop->{'background-position-x'};
2771    $Key->{background_position_x} = $Prop->{'background-position-x'};
2772    
2773    $Prop->{'background-position-y'} = {
2774      css => 'background-position-y',
2775      dom => 'background_position_y',
2776      key => 'background_position_y',
2777      parse => $Prop->{'margin-top'}->{parse},
2778      allow_negative => 1,
2779      keyword => {top => 1, center => 1, bottom => 1},
2780      serialize => $default_serializer,
2781      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
2782      initial => ['PERCENTAGE', 0],
2783      #inherited => 0,
2784      compute => $Prop->{'background-position-x'}->{compute},
2785    };
2786    $Attr->{background_position_y} = $Prop->{'background-position-y'};
2787    $Key->{background_position_y} = $Prop->{'background-position-y'};
2788    
2789    $Prop->{'padding-top'} = {
2790      css => 'padding-top',
2791      dom => 'padding_top',
2792      key => 'padding_top',
2793      parse => $Prop->{'margin-top'}->{parse},
2794      #allow_negative => 0,
2795      #keyword => {},
2796      serialize => $default_serializer,
2797      initial => ['DIMENSION', 0, 'px'],
2798      #inherited => 0,
2799      compute => $compute_length,
2800    };
2801    $Attr->{padding_top} = $Prop->{'padding-top'};
2802    $Key->{padding_top} = $Prop->{'padding-top'};
2803    
2804    $Prop->{'padding-bottom'} = {
2805      css => 'padding-bottom',
2806      dom => 'padding_bottom',
2807      key => 'padding_bottom',
2808      parse => $Prop->{'padding-top'}->{parse},
2809      #allow_negative => 0,
2810      #keyword => {},
2811      serialize => $default_serializer,
2812      initial => ['DIMENSION', 0, 'px'],
2813      #inherited => 0,
2814      compute => $compute_length,
2815    };
2816    $Attr->{padding_bottom} = $Prop->{'padding-bottom'};
2817    $Key->{padding_bottom} = $Prop->{'padding-bottom'};
2818    
2819    $Prop->{'padding-right'} = {
2820      css => 'padding-right',
2821      dom => 'padding_right',
2822      key => 'padding_right',
2823      parse => $Prop->{'padding-top'}->{parse},
2824      #allow_negative => 0,
2825      #keyword => {},
2826      serialize => $default_serializer,
2827      initial => ['DIMENSION', 0, 'px'],
2828      #inherited => 0,
2829      compute => $compute_length,
2830    };
2831    $Attr->{padding_right} = $Prop->{'padding-right'};
2832    $Key->{padding_right} = $Prop->{'padding-right'};
2833    
2834    $Prop->{'padding-left'} = {
2835      css => 'padding-left',
2836      dom => 'padding_left',
2837      key => 'padding_left',
2838      parse => $Prop->{'padding-top'}->{parse},
2839      #allow_negative => 0,
2840      #keyword => {},
2841      serialize => $default_serializer,
2842      initial => ['DIMENSION', 0, 'px'],
2843      #inherited => 0,
2844      compute => $compute_length,
2845    };
2846    $Attr->{padding_left} = $Prop->{'padding-left'};
2847    $Key->{padding_left} = $Prop->{'padding-left'};
2848    
2849    $Prop->{'border-top-width'} = {
2850      css => 'border-top-width',
2851      dom => 'border_top_width',
2852      key => 'border_top_width',
2853      parse => $Prop->{'margin-top'}->{parse},
2854      #allow_negative => 0,
2855      keyword => {thin => 1, medium => 1, thick => 1},
2856      serialize => $default_serializer,
2857      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
2858      initial => ['KEYWORD', 'medium'],
2859      #inherited => 0,
2860      compute => sub {
2861        my ($self, $element, $prop_name, $specified_value) = @_;
2862    
2863        ## NOTE: Used for 'border-top-width', 'border-right-width',
2864        ## 'border-bottom-width', 'border-right-width', and
2865        ## 'outline-width'.
2866    
2867        my $style_prop = $prop_name;
2868        $style_prop =~ s/width/style/;
2869        my $style = $self->get_computed_value ($element, $style_prop);
2870        if (defined $style and $style->[0] eq 'KEYWORD' and
2871            ($style->[1] eq 'none' or $style->[1] eq 'hidden')) {
2872          return ['DIMENSION', 0, 'px'];
2873        }
2874    
2875        my $value = $compute_length->(@_);
2876        if (defined $value and $value->[0] eq 'KEYWORD') {
2877          if ($value->[1] eq 'thin') {
2878            return ['DIMENSION', 1, 'px']; ## Firefox/Opera
2879          } elsif ($value->[1] eq 'medium') {
2880            return ['DIMENSION', 3, 'px']; ## Firefox/Opera
2881          } elsif ($value->[1] eq 'thick') {
2882            return ['DIMENSION', 5, 'px']; ## Firefox
2883          }
2884        }
2885        return $value;
2886      },
2887      ## NOTE: CSS3 will allow <percentage> as an option in <border-width>.
2888      ## Opera 9 has already implemented it.
2889    };
2890    $Attr->{border_top_width} = $Prop->{'border-top-width'};
2891    $Key->{border_top_width} = $Prop->{'border-top-width'};
2892    
2893    $Prop->{'border-right-width'} = {
2894      css => 'border-right-width',
2895      dom => 'border_right_width',
2896      key => 'border_right_width',
2897      parse => $Prop->{'border-top-width'}->{parse},
2898      #allow_negative => 0,
2899      keyword => {thin => 1, medium => 1, thick => 1},
2900      serialize => $default_serializer,
2901      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
2902      initial => ['KEYWORD', 'medium'],
2903      #inherited => 0,
2904      compute => $Prop->{'border-top-width'}->{compute},
2905    };
2906    $Attr->{border_right_width} = $Prop->{'border-right-width'};
2907    $Key->{border_right_width} = $Prop->{'border-right-width'};
2908    
2909    $Prop->{'border-bottom-width'} = {
2910      css => 'border-bottom-width',
2911      dom => 'border_bottom_width',
2912      key => 'border_bottom_width',
2913      parse => $Prop->{'border-top-width'}->{parse},
2914      #allow_negative => 0,
2915      keyword => {thin => 1, medium => 1, thick => 1},
2916      serialize => $default_serializer,
2917      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
2918      initial => ['KEYWORD', 'medium'],
2919      #inherited => 0,
2920      compute => $Prop->{'border-top-width'}->{compute},
2921    };
2922    $Attr->{border_bottom_width} = $Prop->{'border-bottom-width'};
2923    $Key->{border_bottom_width} = $Prop->{'border-bottom-width'};
2924    
2925    $Prop->{'border-left-width'} = {
2926      css => 'border-left-width',
2927      dom => 'border_left_width',
2928      key => 'border_left_width',
2929      parse => $Prop->{'border-top-width'}->{parse},
2930      #allow_negative => 0,
2931      keyword => {thin => 1, medium => 1, thick => 1},
2932      serialize => $default_serializer,
2933      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
2934      initial => ['KEYWORD', 'medium'],
2935      #inherited => 0,
2936      compute => $Prop->{'border-top-width'}->{compute},
2937    };
2938    $Attr->{border_left_width} = $Prop->{'border-left-width'};
2939    $Key->{border_left_width} = $Prop->{'border-left-width'};
2940    
2941    $Prop->{'outline-width'} = {
2942      css => 'outline-width',
2943      dom => 'outline_width',
2944      key => 'outline_width',
2945      parse => $Prop->{'border-top-width'}->{parse},
2946      #allow_negative => 0,
2947      keyword => {thin => 1, medium => 1, thick => 1},
2948      serialize => $default_serializer,
2949      serialize_multiple => $Prop->{'outline-color'}->{serialize_multiple},
2950      initial => ['KEYWORD', 'medium'],
2951      #inherited => 0,
2952      compute => $Prop->{'border-top-width'}->{compute},
2953    };
2954    $Attr->{outline_width} = $Prop->{'outline-width'};
2955    $Key->{outline_width} = $Prop->{'outline-width'};
2956    
2957  $Prop->{'font-weight'} = {  $Prop->{'font-weight'} = {
2958    css => 'font-weight',    css => 'font-weight',
2959    dom => 'font_weight',    dom => 'font_weight',
# Line 1001  $Prop->{'font-weight'} = { Line 2981  $Prop->{'font-weight'} = {
2981        }        }
2982      }      }
2983            
2984      $onerror->(type => 'syntax error:'.$prop_name,      $onerror->(type => "syntax error:'$prop_name'",
2985                 level => $self->{must_level},                 level => $self->{must_level},
2986                   uri => \$self->{href},
2987                 token => $t);                 token => $t);
2988      return ($t, undef);      return ($t, undef);
2989    },    },
# Line 1012  $Prop->{'font-weight'} = { Line 2993  $Prop->{'font-weight'} = {
2993    compute => sub {    compute => sub {
2994      my ($self, $element, $prop_name, $specified_value) = @_;      my ($self, $element, $prop_name, $specified_value) = @_;
2995    
2996      if ($specified_value->[0] eq 'KEYWORD') {      if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
2997        if ($specified_value->[1] eq 'normal') {        if ($specified_value->[1] eq 'normal') {
2998          return ['WEIGHT', 400, 0];          return ['WEIGHT', 400, 0];
2999        } elsif ($specified_value->[1] eq 'bold') {        } elsif ($specified_value->[1] eq 'bold') {
# Line 1036  $Prop->{'font-weight'} = { Line 3017  $Prop->{'font-weight'} = {
3017            return ['WEIGHT', 400, 1];            return ['WEIGHT', 400, 1];
3018          }          }
3019        }        }
3020      } elsif ($specified_value->[0] eq 'WEIGHT') {      #} elsif (defined $specified_value and $specified_value->[0] eq 'WEIGHT') {
3021        #        #
3022      }      }
3023    
# Line 1068  my $uri_or_none_parser = sub { Line 3049  my $uri_or_none_parser = sub {
3049      #  my $value = $t->{value};      #  my $value = $t->{value};
3050      #  $t = $tt->get_next_token;      #  $t = $tt->get_next_token;
3051      #  if ($t->{type} == EOF_TOKEN) {      #  if ($t->{type} == EOF_TOKEN) {
3052      #    $onerror->(type => 'syntax error:eof:'.$prop_name,      #    $onerror->(type => 'uri not closed',
3053      #               level => $self->{must_level},      #               level => $self->{must_level},
3054        #               uri => \$self->{href},
3055      #               token => $t);      #               token => $t);
3056      #          #    
3057      #    return ($t, {$prop_name => ['URI', $value, \($self->{base_uri})]});      #    return ($t, {$prop_name => ['URI', $value, \($self->{base_uri})]});
3058      #  }      #  }
3059      }      }
3060            
3061      $onerror->(type => 'syntax error:'.$prop_name,      $onerror->(type => "syntax error:'$prop_name'",
3062                 level => $self->{must_level},                 level => $self->{must_level},
3063                   uri => \$self->{href},
3064                 token => $t);                 token => $t);
3065      return ($t, undef);      return ($t, undef);
3066  }; # $uri_or_none_parser  }; # $uri_or_none_parser
# Line 1119  $Prop->{'background-image'} = { Line 3102  $Prop->{'background-image'} = {
3102    key => 'background_image',    key => 'background_image',
3103    parse => $uri_or_none_parser,    parse => $uri_or_none_parser,
3104    serialize => $default_serializer,    serialize => $default_serializer,
3105      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
3106    initial => ['KEYWORD', 'none'],    initial => ['KEYWORD', 'none'],
3107    #inherited => 0,    #inherited => 0,
3108    compute => $compute_uri_or_none,    compute => $compute_uri_or_none,
# Line 1137  $Prop->{'border-top-style'} = { Line 3121  $Prop->{'border-top-style'} = {
3121    key => 'border_top_style',    key => 'border_top_style',
3122    parse => $one_keyword_parser,    parse => $one_keyword_parser,
3123    serialize => $default_serializer,    serialize => $default_serializer,
3124      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3125    keyword => $border_style_keyword,    keyword => $border_style_keyword,
3126    initial => ["KEYWORD", "none"],    initial => ["KEYWORD", "none"],
3127    #inherited => 0,    #inherited => 0,
# Line 1151  $Prop->{'border-right-style'} = { Line 3136  $Prop->{'border-right-style'} = {
3136    key => 'border_right_style',    key => 'border_right_style',
3137    parse => $one_keyword_parser,    parse => $one_keyword_parser,
3138    serialize => $default_serializer,    serialize => $default_serializer,
3139      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3140    keyword => $border_style_keyword,    keyword => $border_style_keyword,
3141    initial => ["KEYWORD", "none"],    initial => ["KEYWORD", "none"],
3142    #inherited => 0,    #inherited => 0,
# Line 1165  $Prop->{'border-bottom-style'} = { Line 3151  $Prop->{'border-bottom-style'} = {
3151    key => 'border_bottom_style',    key => 'border_bottom_style',
3152    parse => $one_keyword_parser,    parse => $one_keyword_parser,
3153    serialize => $default_serializer,    serialize => $default_serializer,
3154      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3155    keyword => $border_style_keyword,    keyword => $border_style_keyword,
3156    initial => ["KEYWORD", "none"],    initial => ["KEYWORD", "none"],
3157    #inherited => 0,    #inherited => 0,
# Line 1179  $Prop->{'border-left-style'} = { Line 3166  $Prop->{'border-left-style'} = {
3166    key => 'border_left_style',    key => 'border_left_style',
3167    parse => $one_keyword_parser,    parse => $one_keyword_parser,
3168    serialize => $default_serializer,    serialize => $default_serializer,
3169      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3170    keyword => $border_style_keyword,    keyword => $border_style_keyword,
3171    initial => ["KEYWORD", "none"],    initial => ["KEYWORD", "none"],
3172    #inherited => 0,    #inherited => 0,
# Line 1187  $Prop->{'border-left-style'} = { Line 3175  $Prop->{'border-left-style'} = {
3175  $Attr->{border_left_style} = $Prop->{'border-left-style'};  $Attr->{border_left_style} = $Prop->{'border-left-style'};
3176  $Key->{border_left_style} = $Prop->{'border-left-style'};  $Key->{border_left_style} = $Prop->{'border-left-style'};
3177    
3178    $Prop->{'outline-style'} = {
3179      css => 'outline-style',
3180      dom => 'outline_style',
3181      key => 'outline_style',
3182      parse => $one_keyword_parser,
3183      serialize => $default_serializer,
3184      serialize_multiple => $Prop->{'outline-color'}->{serialize_multiple},
3185      keyword => {%$border_style_keyword},
3186      initial => ['KEYWORD', 'none'],
3187      #inherited => 0,
3188      compute => $compute_as_specified,
3189    };
3190    $Attr->{outline_style} = $Prop->{'outline-style'};
3191    $Key->{outline_style} = $Prop->{'outline-style'};
3192    delete $Prop->{'outline-style'}->{keyword}->{hidden};
3193    
3194    my $generic_font_keywords => {
3195      serif => 1, 'sans-serif' => 1, cursive => 1,
3196      fantasy => 1, monospace => 1, '-manakai-default' => 1,
3197      '-manakai-caption' => 1, '-manakai-icon' => 1,
3198      '-manakai-menu' => 1, '-manakai-message-box' => 1,
3199      '-manakai-small-caption' => 1, '-manakai-status-bar' => 1,
3200    };
3201    ## NOTE: "All five generic font families are defined to exist in all CSS
3202    ## implementations (they need not necessarily map to five distinct actual
3203    ## fonts)." [CSS 2.1].
3204    ## NOTE: "If no font with the indicated characteristics exists on a given
3205    ## platform, the user agent should either intelligently substitute (e.g., a
3206    ## smaller version of the 'caption' font might be used for the 'small-caption'
3207    ## font), or substitute a user agent default font." [CSS 2.1].
3208    
3209  $Prop->{'font-family'} = {  $Prop->{'font-family'} = {
3210    css => 'font-family',    css => 'font-family',
3211    dom => 'font_family',    dom => 'font_family',
# Line 1201  $Prop->{'font-family'} = { Line 3220  $Prop->{'font-family'} = {
3220    
3221      my $font_name = '';      my $font_name = '';
3222      my $may_be_generic = 1;      my $may_be_generic = 1;
3223      my $may_be_inherit = 1;      my $may_be_inherit = ($prop_name ne 'font');
3224      my $has_s = 0;      my $has_s = 0;
3225      F: {      F: {
3226        if ($t->{type} == IDENT_TOKEN) {        if ($t->{type} == IDENT_TOKEN) {
# Line 1218  $Prop->{'font-family'} = { Line 3237  $Prop->{'font-family'} = {
3237          undef $may_be_generic;          undef $may_be_generic;
3238          undef $has_s;          undef $has_s;
3239          $t = $tt->get_next_token;          $t = $tt->get_next_token;
3240        } elsif ($t->{type} == COMMA_TOKEN) {        } elsif ($t->{type} == COMMA_TOKEN) { ## TODO: case
3241          if ($may_be_generic and          if ($may_be_generic and $generic_font_keywords->{lc $font_name}) {
             {  
               serif => 1, 'sans-serif' => 1, cursive => 1,  
               fantasy => 1, monospace => 1, '-manakai-default' => 1,  
             }->{lc $font_name}) { ## TODO: case  
3242            push @prop_value, ['KEYWORD', $font_name];            push @prop_value, ['KEYWORD', $font_name];
3243          } elsif (not $may_be_generic or length $font_name) {          } elsif (not $may_be_generic or length $font_name) {
3244            push @prop_value, ["STRING", $font_name];            push @prop_value, ["STRING", $font_name];
# Line 1238  $Prop->{'font-family'} = { Line 3253  $Prop->{'font-family'} = {
3253          $has_s = 1;          $has_s = 1;
3254          $t = $tt->get_next_token;          $t = $tt->get_next_token;
3255        } else {        } else {
3256          if ($may_be_generic and          if ($may_be_generic and $generic_font_keywords->{lc $font_name}) {
3257              {            push @prop_value, ['KEYWORD', $font_name]; ## TODO: case
               serif => 1, 'sans-serif' => 1, cursive => 1,  
               fantasy => 1, monospace => 1, '-manakai-default' => 1,  
             }->{lc $font_name}) { ## TODO: case  
           push @prop_value, ['KEYWORD', $font_name];  
3258          } elsif (not $may_be_generic or length $font_name) {          } elsif (not $may_be_generic or length $font_name) {
3259            push @prop_value, ['STRING', $font_name];            push @prop_value, ['STRING', $font_name];
3260          } else {          } else {
3261            $onerror->(type => 'syntax error:'.$prop_name,            $onerror->(type => "syntax error:'$prop_name'",
3262                       level => $self->{must_level},                       level => $self->{must_level},
3263                         uri => \$self->{href},
3264                       token => $t);                       token => $t);
3265            return ($t, undef);            return ($t, undef);
3266          }          }
# Line 1284  $Prop->{'font-family'} = { Line 3296  $Prop->{'font-family'} = {
3296      } elsif ($value->[0] eq 'INHERIT') {      } elsif ($value->[0] eq 'INHERIT') {
3297        return 'inherit';        return 'inherit';
3298      } else {      } else {
3299        return undef;        return '';
3300      }      }
3301    },    },
3302    initial => ['FONT', ['KEYWORD', '-manakai-default']],    initial => ['FONT', ['KEYWORD', '-manakai-default']],
# Line 1294  $Prop->{'font-family'} = { Line 3306  $Prop->{'font-family'} = {
3306  $Attr->{font_family} = $Prop->{'font-family'};  $Attr->{font_family} = $Prop->{'font-family'};
3307  $Key->{font_family} = $Prop->{'font-family'};  $Key->{font_family} = $Prop->{'font-family'};
3308    
3309    $Prop->{cursor} = {
3310      css => 'cursor',
3311      dom => 'cursor',
3312      key => 'cursor',
3313      parse => sub {
3314        my ($self, $prop_name, $tt, $t, $onerror) = @_;
3315    
3316        ## NOTE: See <http://suika.fam.cx/gate/2005/sw/cursor> for browser
3317        ## compatibility issues.
3318    
3319        my @prop_value = ('CURSOR');
3320    
3321        F: {
3322          if ($t->{type} == IDENT_TOKEN) {
3323            my $v = lc $t->{value}; ## TODO: case
3324            $t = $tt->get_next_token;
3325            if ($Prop->{$prop_name}->{keyword}->{$v}) {
3326              push @prop_value, ['KEYWORD', $v];
3327              last F;
3328            } elsif ($v eq 'inherit' and @prop_value == 1) {
3329              return ($t, {$prop_name => ['INHERIT']});
3330            } else {
3331              $onerror->(type => "syntax error:'$prop_name'",
3332                         level => $self->{must_level},
3333                         uri => \$self->{href},
3334                         token => $t);
3335              return ($t, undef);
3336            }
3337          } elsif ($t->{type} == URI_TOKEN) {
3338            push @prop_value, ['URI', $t->{value}, \($self->{base_uri})];
3339            $t = $tt->get_next_token;
3340          } else {
3341            $onerror->(type => "syntax error:'$prop_name'",
3342                       level => $self->{must_level},
3343                       uri => \$self->{href},
3344                       token => $t);
3345            return ($t, undef);
3346          }
3347        
3348          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3349          if ($t->{type} == COMMA_TOKEN) {
3350            $t = $tt->get_next_token;
3351            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3352            redo F;
3353          }
3354        } # F
3355    
3356        return ($t, {$prop_name => \@prop_value});
3357      },
3358      serialize => sub {
3359        my ($self, $prop_name, $value) = @_;
3360    
3361        if ($value->[0] eq 'CURSOR') {
3362          return join ', ', map {
3363            if ($_->[0] eq 'URI') {
3364              'url('.$_->[1].')'; ## NOTE: This is what Firefox does.
3365            } elsif ($_->[0] eq 'KEYWORD') {
3366              $_->[1];
3367            } else {
3368              ## NOTE: This should be an error.
3369              '""';
3370            }
3371          } @$value[1..$#$value];
3372        } elsif ($value->[0] eq 'INHERIT') {
3373          return 'inherit';
3374        } else {
3375          return '';
3376        }
3377      },
3378      keyword => {
3379        auto => 1, crosshair => 1, default => 1, pointer => 1, move => 1,
3380        'e-resize' => 1, 'ne-resize' => 1, 'nw-resize' => 1, 'n-resize' => 1,
3381        'n-resize' => 1, 'se-resize' => 1, 'sw-resize' => 1, 's-resize' => 1,
3382        'w-resize' => 1, text => 1, wait => 1, help => 1, progress => 1,
3383      },
3384      initial => ['CURSOR', ['KEYWORD', 'auto']],
3385      inherited => 1,
3386      compute => sub {
3387        my ($self, $element, $prop_name, $specified_value) = @_;
3388    
3389        if (defined $specified_value and $specified_value->[0] eq 'CURSOR') {
3390          my @new_value = ('CURSOR');
3391          for my $value (@$specified_value[1..$#$specified_value]) {
3392            if ($value->[0] eq 'URI') {
3393              if (defined $value->[2]) {
3394                require Message::DOM::DOMImplementation;
3395                push @new_value, ['URI',
3396                                  Message::DOM::DOMImplementation
3397                                      ->create_uri_reference ($value->[1])
3398                                      ->get_absolute_reference (${$value->[2]})
3399                                      ->get_uri_reference,
3400                                  $value->[2]];
3401              } else {
3402                push @new_value, $value;
3403              }
3404            } else {
3405              push @new_value, $value;
3406            }
3407          }
3408          return \@new_value;
3409        }
3410    
3411        return $specified_value;
3412      },
3413    };
3414    $Attr->{cursor} = $Prop->{cursor};
3415    $Key->{cursor} = $Prop->{cursor};
3416    
3417  $Prop->{'border-style'} = {  $Prop->{'border-style'} = {
3418    css => 'border-style',    css => 'border-style',
3419    dom => 'border_style',    dom => 'border_style',
# Line 1301  $Prop->{'border-style'} = { Line 3421  $Prop->{'border-style'} = {
3421      my ($self, $prop_name, $tt, $t, $onerror) = @_;      my ($self, $prop_name, $tt, $t, $onerror) = @_;
3422    
3423      my %prop_value;      my %prop_value;
     my $has_inherit;  
3424      if ($t->{type} == IDENT_TOKEN) {      if ($t->{type} == IDENT_TOKEN) {
3425        my $prop_value = lc $t->{value}; ## TODO: case folding        my $prop_value = lc $t->{value}; ## TODO: case folding
3426        $t = $tt->get_next_token;        $t = $tt->get_next_token;
# Line 1310  $Prop->{'border-style'} = { Line 3429  $Prop->{'border-style'} = {
3429          $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];          $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];
3430        } elsif ($prop_value eq 'inherit') {        } elsif ($prop_value eq 'inherit') {
3431          $prop_value{'border-top-style'} = ["INHERIT"];          $prop_value{'border-top-style'} = ["INHERIT"];
3432          $has_inherit = 1;          $prop_value{'border-right-style'} = $prop_value{'border-top-style'};
3433            $prop_value{'border-bottom-style'} = $prop_value{'border-top-style'};
3434            $prop_value{'border-left-style'} = $prop_value{'border-right-style'};
3435            return ($t, \%prop_value);
3436        } else {        } else {
3437          $onerror->(type => 'syntax error:keyword:'.$prop_name,          $onerror->(type => "syntax error:'$prop_name'",
3438                     level => $self->{must_level},                     level => $self->{must_level},
3439                       uri => \$self->{href},
3440                     token => $t);                     token => $t);
3441          return ($t, undef);          return ($t, undef);
3442        }        }
# Line 1321  $Prop->{'border-style'} = { Line 3444  $Prop->{'border-style'} = {
3444        $prop_value{'border-bottom-style'} = $prop_value{'border-top-style'};        $prop_value{'border-bottom-style'} = $prop_value{'border-top-style'};
3445        $prop_value{'border-left-style'} = $prop_value{'border-right-style'};        $prop_value{'border-left-style'} = $prop_value{'border-right-style'};
3446      } else {      } else {
3447        $onerror->(type => 'syntax error:keyword:'.$prop_name,        $onerror->(type => "syntax error:'$prop_name'",
3448                   level => $self->{must_level},                   level => $self->{must_level},
3449                     uri => \$self->{href},
3450                   token => $t);                   token => $t);
3451        return ($t, undef);        return ($t, undef);
3452      }      }
# Line 1331  $Prop->{'border-style'} = { Line 3455  $Prop->{'border-style'} = {
3455      if ($t->{type} == IDENT_TOKEN) {      if ($t->{type} == IDENT_TOKEN) {
3456        my $prop_value = lc $t->{value}; ## TODO: case folding        my $prop_value = lc $t->{value}; ## TODO: case folding
3457        $t = $tt->get_next_token;        $t = $tt->get_next_token;
3458        if (not $has_inherit and        if ($border_style_keyword->{$prop_value} and
           $border_style_keyword->{$prop_value} and  
3459            $self->{prop_value}->{'border-right-style'}->{$prop_value}) {            $self->{prop_value}->{'border-right-style'}->{$prop_value}) {
3460          $prop_value{'border-right-style'} = ["KEYWORD", $prop_value];          $prop_value{'border-right-style'} = ["KEYWORD", $prop_value];
3461        } else {        } else {
3462          $onerror->(type => 'syntax error:keyword:'.$prop_name,          $onerror->(type => "syntax error:'$prop_name'",
3463                     level => $self->{must_level},                     level => $self->{must_level},
3464                       uri => \$self->{href},
3465                     token => $t);                     token => $t);
3466          return ($t, undef);          return ($t, undef);
3467        }        }
# Line 1351  $Prop->{'border-style'} = { Line 3475  $Prop->{'border-style'} = {
3475              $self->{prop_value}->{'border-bottom-style'}->{$prop_value}) {              $self->{prop_value}->{'border-bottom-style'}->{$prop_value}) {
3476            $prop_value{'border-bottom-style'} = ["KEYWORD", $prop_value];            $prop_value{'border-bottom-style'} = ["KEYWORD", $prop_value];
3477          } else {          } else {
3478            $onerror->(type => 'syntax error:keyword:'.$prop_name,            $onerror->(type => "syntax error:'$prop_name'",
3479                       level => $self->{must_level},                       level => $self->{must_level},
3480                         uri => \$self->{href},
3481                       token => $t);                       token => $t);
3482            return ($t, undef);            return ($t, undef);
3483          }          }
# Line 1365  $Prop->{'border-style'} = { Line 3490  $Prop->{'border-style'} = {
3490                $self->{prop_value}->{'border-left-style'}->{$prop_value}) {                $self->{prop_value}->{'border-left-style'}->{$prop_value}) {
3491              $prop_value{'border-left-style'} = ["KEYWORD", $prop_value];              $prop_value{'border-left-style'} = ["KEYWORD", $prop_value];
3492            } else {            } else {
3493              $onerror->(type => 'syntax error:keyword:'.$prop_name,              $onerror->(type => "syntax error:'$prop_name'",
3494                         level => $self->{must_level},                         level => $self->{must_level},
3495                           uri => \$self->{href},
3496                         token => $t);                         token => $t);
3497              return ($t, undef);              return ($t, undef);
3498            }            }
# Line 1382  $Prop->{'border-style'} = { Line 3508  $Prop->{'border-style'} = {
3508      local $Error::Depth = $Error::Depth + 1;      local $Error::Depth = $Error::Depth + 1;
3509      my @v;      my @v;
3510      push @v, $self->border_top_style;      push @v, $self->border_top_style;
3511      return undef unless defined $v[-1];      return '' unless length $v[-1];
3512      push @v, $self->border_right_style;      push @v, $self->border_right_style;
3513      return undef unless defined $v[-1];      return '' unless length $v[-1];
     push @v, $self->border_bottom_style;  
     return undef unless defined $v[-1];  
3514      push @v, $self->border_bottom_style;      push @v, $self->border_bottom_style;
3515      return undef unless defined $v[-1];      return '' unless length $v[-1];
3516        push @v, $self->border_left_style;
3517        return '' unless length $v[-1];
3518    
3519      pop @v if $v[1] eq $v[3];      pop @v if $v[1] eq $v[3];
3520      pop @v if $v[0] eq $v[2];      pop @v if $v[0] eq $v[2];
3521      pop @v if $v[0] eq $v[1];      pop @v if $v[0] eq $v[1];
3522      return join ' ', @v;      return join ' ', @v;
3523    },    },
3524      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3525  };  };
3526  $Attr->{border_style} = $Prop->{'border-style'};  $Attr->{border_style} = $Prop->{'border-style'};
3527    
3528    $Prop->{'border-color'} = {
3529      css => 'border-color',
3530      dom => 'border_color',
3531      parse => sub {
3532        my ($self, $prop_name, $tt, $t, $onerror) = @_;
3533    
3534        my %prop_value;
3535        ($t, my $pv) = $parse_color->($self, 'border-color', $tt, $t, $onerror);
3536        if (not defined $pv) {
3537          return ($t, undef);
3538        }
3539        $prop_value{'border-top-color'} = $pv->{'border-color'};
3540        $prop_value{'border-bottom-color'} = $prop_value{'border-top-color'};
3541        $prop_value{'border-right-color'} = $prop_value{'border-top-color'};
3542        $prop_value{'border-left-color'}= $prop_value{'border-right-color'};
3543        if ($prop_value{'border-top-color'}->[0] eq 'INHERIT') {
3544          return ($t, \%prop_value);
3545        }
3546    
3547        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3548        if ({
3549             IDENT_TOKEN, 1,
3550             HASH_TOKEN, 1, NUMBER_TOKEN, 1, DIMENSION_TOKEN, 1,
3551             FUNCTION_TOKEN, 1,
3552            }->{$t->{type}}) {
3553          ($t, $pv) = $parse_color->($self, 'border-color', $tt, $t, $onerror);
3554          if (not defined $pv) {
3555            return ($t, undef);
3556          } elsif ($pv->{'border-color'}->[0] eq 'INHERIT') {
3557            $onerror->(type => "syntax error:'$prop_name'",
3558                       level => $self->{must_level},
3559                       uri => \$self->{href},
3560                       token => $t);
3561            return ($t, undef);
3562          }
3563          $prop_value{'border-right-color'} = $pv->{'border-color'};
3564          $prop_value{'border-left-color'}= $prop_value{'border-right-color'};
3565    
3566          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3567          if ({
3568               IDENT_TOKEN, 1,
3569               HASH_TOKEN, 1, NUMBER_TOKEN, 1, DIMENSION_TOKEN, 1,
3570               FUNCTION_TOKEN, 1,
3571              }->{$t->{type}}) {
3572            ($t, $pv) = $parse_color->($self, 'border-color', $tt, $t, $onerror);
3573            if (not defined $pv) {
3574              return ($t, undef);
3575            } elsif ($pv->{'border-color'}->[0] eq 'INHERIT') {
3576              $onerror->(type => "syntax error:'$prop_name'",
3577                         level => $self->{must_level},
3578                         uri => \$self->{href},
3579                         token => $t);
3580              return ($t, undef);
3581            }
3582            $prop_value{'border-bottom-color'} = $pv->{'border-color'};
3583    
3584            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3585            if ({
3586                 IDENT_TOKEN, 1,
3587                 HASH_TOKEN, 1, NUMBER_TOKEN, 1, DIMENSION_TOKEN, 1,
3588                 FUNCTION_TOKEN, 1,
3589                }->{$t->{type}}) {
3590              ($t, $pv) = $parse_color->($self, 'border-color', $tt, $t, $onerror);
3591              if (not defined $pv) {
3592                return ($t, undef);
3593              } elsif ($pv->{'border-color'}->[0] eq 'INHERIT') {
3594                $onerror->(type => "syntax error:'$prop_name'",
3595                           level => $self->{must_level},
3596                           uri => \$self->{href},
3597                           token => $t);
3598                return ($t, undef);
3599              }
3600              $prop_value{'border-left-color'} = $pv->{'border-color'};
3601            }
3602          }
3603        }
3604        
3605        return ($t, \%prop_value);
3606      },
3607      serialize => sub {
3608        my ($self, $prop_name, $value) = @_;
3609        
3610        local $Error::Depth = $Error::Depth + 1;
3611        my @v;
3612        push @v, $self->border_top_color;
3613        return '' unless length $v[-1];
3614        push @v, $self->border_right_color;
3615        return '' unless length $v[-1];
3616        push @v, $self->border_bottom_color;
3617        return '' unless length $v[-1];
3618        push @v, $self->border_left_color;
3619        return '' unless length $v[-1];
3620    
3621        pop @v if $v[1] eq $v[3];
3622        pop @v if $v[0] eq $v[2];
3623        pop @v if $v[0] eq $v[1];
3624        return join ' ', @v;
3625      },
3626      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3627    };
3628    $Attr->{border_color} = $Prop->{'border-color'};
3629    
3630    $Prop->{'border-top'} = {
3631      css => 'border-top',
3632      dom => 'border_top',
3633      parse => sub {
3634        my ($self, $prop_name, $tt, $t, $onerror) = @_;
3635    
3636        my %prop_value;
3637        my $pv;
3638        ## NOTE: Since $onerror is disabled for three invocations below,
3639        ## some informative warning messages (if they are added someday) will not
3640        ## be reported.
3641        ($t, $pv) = $parse_color->($self, $prop_name.'-color', $tt, $t, sub {});
3642        if (defined $pv) {
3643          if ($pv->{$prop_name.'-color'}->[0] eq 'INHERIT') {
3644            return ($t, {$prop_name.'-color' => ['INHERIT'],
3645                         $prop_name.'-style' => ['INHERIT'],
3646                         $prop_name.'-width' => ['INHERIT']});
3647          } else {
3648            $prop_value{$prop_name.'-color'} = $pv->{$prop_name.'-color'};
3649          }
3650        } else {
3651          ($t, $pv) = $Prop->{'border-top-width'}->{parse}
3652              ->($self, $prop_name.'-width', $tt, $t, sub {});
3653          if (defined $pv) {
3654            $prop_value{$prop_name.'-width'} = $pv->{$prop_name.'-width'};
3655          } else {
3656            ($t, $pv) = $Prop->{'border-top-style'}->{parse}
3657                ->($self, $prop_name.'-style', $tt, $t, sub {});
3658            if (defined $pv) {
3659              $prop_value{$prop_name.'-style'} = $pv->{$prop_name.'-style'};
3660            } else {
3661              $onerror->(type => "syntax error:'$prop_name'",
3662                         level => $self->{must_level},
3663                         uri => \$self->{href},
3664                         token => $t);
3665              return ($t, undef);
3666            }
3667          }
3668        }
3669    
3670        for (1..2) {
3671          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3672          if ($t->{type} == IDENT_TOKEN) {
3673            my $prop_value = lc $t->{value}; ## TODO: case
3674            if ($border_style_keyword->{$prop_value} and
3675                $self->{prop_value}->{'border-top-style'}->{$prop_value} and
3676                not defined $prop_value{$prop_name.'-style'}) {
3677              $prop_value{$prop_name.'-style'} = ['KEYWORD', $prop_value];
3678              $t = $tt->get_next_token;
3679              next;
3680            } elsif ({thin => 1, medium => 1, thick => 1}->{$prop_value} and
3681                     not defined $prop_value{$prop_name.'-width'}) {
3682              $prop_value{$prop_name.'-width'} = ['KEYWORD', $prop_value];
3683              $t = $tt->get_next_token;
3684              next;
3685            }
3686          }
3687    
3688          undef $pv;
3689          ($t, $pv) = $parse_color->($self, $prop_name.'-color', $tt, $t, $onerror)
3690              if not defined $prop_value{$prop_name.'-color'} and
3691                  {
3692                    IDENT_TOKEN, 1,
3693                    HASH_TOKEN, 1, NUMBER_TOKEN, 1, DIMENSION_TOKEN, 1,
3694                    FUNCTION_TOKEN, 1,
3695                  }->{$t->{type}};
3696          if (defined $pv) {
3697            if ($pv->{$prop_name.'-color'}->[0] eq 'INHERIT') {
3698              $onerror->(type => "syntax error:'$prop_name'",
3699                         level => $self->{must_level},
3700                         uri => \$self->{href},
3701                         token => $t);
3702            } else {
3703              $prop_value{$prop_name.'-color'} = $pv->{$prop_name.'-color'};
3704            }
3705          } else {
3706            undef $pv;
3707            ($t, $pv) = $Prop->{'border-top-width'}->{parse}
3708                ->($self, $prop_name.'-width',
3709                   $tt, $t, $onerror)
3710                if not defined $prop_value{$prop_name.'-width'} and
3711                    {
3712                      DIMENSION_TOKEN, 1,
3713                      NUMBER_TOKEN, 1,
3714                      IDENT_TOKEN, 1,
3715                      MINUS_TOKEN, 1,
3716                    }->{$t->{type}};
3717            if (defined $pv) {
3718              if ($pv->{$prop_name.'-width'}->[0] eq 'INHERIT') {
3719                $onerror->(type => "syntax error:'$prop_name'",
3720                           level => $self->{must_level},
3721                           uri => \$self->{href},
3722                           token => $t);
3723              } else {
3724                $prop_value{$prop_name.'-width'} = $pv->{$prop_name.'-width'};
3725              }
3726            } else {
3727              last;
3728            }
3729          }    
3730        }
3731    
3732        $prop_value{$prop_name.'-color'}
3733            ||= $Prop->{$prop_name.'-color'}->{initial};
3734        $prop_value{$prop_name.'-width'}
3735            ||= $Prop->{$prop_name.'-width'}->{initial};
3736        $prop_value{$prop_name.'-style'}
3737            ||= $Prop->{$prop_name.'-style'}->{initial};
3738        
3739        return ($t, \%prop_value);
3740      },
3741      serialize => sub {
3742        my ($self, $prop_name, $value) = @_;
3743        
3744        local $Error::Depth = $Error::Depth + 1;
3745        my $width_prop = $prop_name . '_width';  $width_prop =~ tr/-/_/;
3746        my $width_value = $self->$width_prop;
3747        return '' unless length $width_value;
3748        my $style_prop = $prop_name . '_style';  $style_prop =~ tr/-/_/;
3749        my $style_value = $self->$style_prop;
3750        return '' unless length $style_value;
3751        my $color_prop = $prop_name . '_color';  $color_prop =~ tr/-/_/;
3752        my $color_value = $self->$color_prop;
3753        return '' unless length $color_value;
3754    
3755        return $width_value . ' ' . $style_value . ' ' . $color_value;
3756      },
3757      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3758    };
3759    $Attr->{border_top} = $Prop->{'border-top'};
3760    
3761    $Prop->{'border-right'} = {
3762      css => 'border-right',
3763      dom => 'border_right',
3764      parse => $Prop->{'border-top'}->{parse},
3765      serialize => $Prop->{'border-top'}->{serialize},
3766      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3767    };
3768    $Attr->{border_right} = $Prop->{'border-right'};
3769    
3770    $Prop->{'border-bottom'} = {
3771      css => 'border-bottom',
3772      dom => 'border_bottom',
3773      parse => $Prop->{'border-top'}->{parse},
3774      serialize => $Prop->{'border-top'}->{serialize},
3775      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3776    };
3777    $Attr->{border_bottom} = $Prop->{'border-bottom'};
3778    
3779    $Prop->{'border-left'} = {
3780      css => 'border-left',
3781      dom => 'border_left',
3782      parse => $Prop->{'border-top'}->{parse},
3783      serialize => $Prop->{'border-top'}->{serialize},
3784      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3785    };
3786    $Attr->{border_left} = $Prop->{'border-left'};
3787    
3788    $Prop->{outline} = {
3789      css => 'outline',
3790      dom => 'outline',
3791      parse => $Prop->{'border-top'}->{parse},
3792      serialize => $Prop->{'border-top'}->{serialize},
3793      serialize_multiple => $Prop->{'outline-color'}->{serialize_multiple},
3794    };
3795    $Attr->{outline} = $Prop->{outline};
3796    
3797    $Prop->{border} = {
3798      css => 'border',
3799      dom => 'border',
3800      parse => sub {
3801        my ($self, $prop_name, $tt, $t, $onerror) = @_;
3802        my $prop_value;
3803        ($t, $prop_value) = $Prop->{'border-top'}->{parse}
3804            ->($self, 'border-top', $tt, $t, $onerror);
3805        return ($t, undef) unless defined $prop_value;
3806        
3807        for (qw/border-right border-bottom border-left/) {
3808          $prop_value->{$_.'-color'} = $prop_value->{'border-top-color'}
3809              if defined $prop_value->{'border-top-color'};
3810          $prop_value->{$_.'-style'} = $prop_value->{'border-top-style'}
3811              if defined $prop_value->{'border-top-style'};
3812          $prop_value->{$_.'-width'} = $prop_value->{'border-top-width'}
3813              if defined $prop_value->{'border-top-width'};
3814        }
3815        return ($t, $prop_value);
3816      },
3817      serialize => sub {
3818        my ($self, $prop_name, $value) = @_;
3819        
3820        local $Error::Depth = $Error::Depth + 1;
3821        my $bt = $self->border_top;
3822        return '' unless length $bt;
3823        my $br = $self->border_right;
3824        return '' unless length $br;
3825        return '' unless $bt eq $br;
3826        my $bb = $self->border_bottom;
3827        return '' unless length $bb;
3828        return '' unless $bt eq $bb;
3829        my $bl = $self->border_left;
3830        return '' unless length $bl;
3831        return '' unless $bt eq $bl;
3832    
3833        return $bt;
3834      },
3835      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
3836    };
3837    $Attr->{border} = $Prop->{border};
3838    
3839    $Prop->{margin} = {
3840      css => 'margin',
3841      dom => 'margin',
3842      parse => sub {
3843        my ($self, $prop_name, $tt, $t, $onerror) = @_;
3844    
3845        my %prop_value;
3846    
3847        my $sign = 1;
3848        my $has_sign;
3849        if ($t->{type} == MINUS_TOKEN) {
3850          $t = $tt->get_next_token;
3851          $has_sign = 1;
3852          $sign = -1;
3853        } elsif ($t->{type} == PLUS_TOKEN) {
3854          $t = $tt->get_next_token;
3855          $has_sign = 1;
3856        }
3857    
3858        if ($t->{type} == DIMENSION_TOKEN) {
3859          my $value = $t->{number} * $sign;
3860          my $unit = lc $t->{value}; ## TODO: case
3861          $t = $tt->get_next_token;
3862          if ($length_unit->{$unit}) {
3863            $prop_value{'margin-top'} = ['DIMENSION', $value, $unit];
3864          } else {
3865            $onerror->(type => "syntax error:'$prop_name'",
3866                       level => $self->{must_level},
3867                       uri => \$self->{href},
3868                       token => $t);
3869            return ($t, undef);
3870          }
3871        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
3872          my $value = $t->{number} * $sign;
3873          $t = $tt->get_next_token;
3874          $prop_value{'margin-top'} = ['PERCENTAGE', $value];
3875        } elsif ($t->{type} == NUMBER_TOKEN and
3876                 ($self->{unitless_px} or $t->{number} == 0)) {
3877          my $value = $t->{number} * $sign;
3878          $t = $tt->get_next_token;
3879          $prop_value{'margin-top'} = ['DIMENSION', $value, 'px'];
3880        } elsif (not $has_sign and $t->{type} == IDENT_TOKEN) {
3881          my $prop_value = lc $t->{value}; ## TODO: case folding
3882          $t = $tt->get_next_token;
3883          if ($prop_value eq 'auto') {
3884            $prop_value{'margin-top'} = ['KEYWORD', $prop_value];
3885          } elsif ($prop_value eq 'inherit') {
3886            $prop_value{'margin-top'} = ['INHERIT'];
3887            $prop_value{'margin-right'} = $prop_value{'margin-top'};
3888            $prop_value{'margin-bottom'} = $prop_value{'margin-top'};
3889            $prop_value{'margin-left'} = $prop_value{'margin-right'};
3890            return ($t, \%prop_value);
3891          } else {
3892            $onerror->(type => "syntax error:'$prop_name'",
3893                       level => $self->{must_level},
3894                       uri => \$self->{href},
3895                       token => $t);
3896            return ($t, undef);
3897          }
3898        } else {
3899          $onerror->(type => "syntax error:'$prop_name'",
3900                     level => $self->{must_level},
3901                     uri => \$self->{href},
3902                     token => $t);
3903          return ($t, undef);
3904        }
3905        $prop_value{'margin-right'} = $prop_value{'margin-top'};
3906        $prop_value{'margin-bottom'} = $prop_value{'margin-top'};
3907        $prop_value{'margin-left'} = $prop_value{'margin-right'};
3908    
3909        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3910        undef $has_sign;
3911        $sign = 1;
3912        if ($t->{type} == MINUS_TOKEN) {
3913          $t = $tt->get_next_token;
3914          $has_sign = 1;
3915          $sign = -1;
3916        } elsif ($t->{type} == PLUS_TOKEN) {
3917          $t = $tt->get_next_token;
3918          $has_sign = 1;
3919        }
3920    
3921        if ($t->{type} == DIMENSION_TOKEN) {
3922          my $value = $t->{number} * $sign;
3923          my $unit = lc $t->{value}; ## TODO: case
3924          $t = $tt->get_next_token;
3925          if ($length_unit->{$unit}) {
3926            $prop_value{'margin-right'} = ['DIMENSION', $value, $unit];
3927          } else {
3928            $onerror->(type => "syntax error:'$prop_name'",
3929                       level => $self->{must_level},
3930                       uri => \$self->{href},
3931                       token => $t);
3932            return ($t, undef);
3933          }
3934        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
3935          my $value = $t->{number} * $sign;
3936          $t = $tt->get_next_token;
3937          $prop_value{'margin-right'} = ['PERCENTAGE', $value];
3938        } elsif ($t->{type} == NUMBER_TOKEN and
3939                 ($self->{unitless_px} or $t->{number} == 0)) {
3940          my $value = $t->{number} * $sign;
3941          $t = $tt->get_next_token;
3942          $prop_value{'margin-right'} = ['DIMENSION', $value, 'px'];
3943        } elsif (not $has_sign and $t->{type} == IDENT_TOKEN) {
3944          my $prop_value = lc $t->{value}; ## TODO: case folding
3945          $t = $tt->get_next_token;
3946          if ($prop_value eq 'auto') {
3947            $prop_value{'margin-right'} = ['KEYWORD', $prop_value];
3948          } else {
3949            $onerror->(type => "syntax error:'$prop_name'",
3950                       level => $self->{must_level},
3951                       uri => \$self->{href},
3952                       token => $t);
3953            return ($t, undef);
3954          }
3955        } else {
3956          if ($has_sign) {
3957            $onerror->(type => "syntax error:'$prop_name'",
3958                       level => $self->{must_level},
3959                       uri => \$self->{href},
3960                       token => $t);
3961            return ($t, undef);
3962          }
3963          return ($t, \%prop_value);
3964        }
3965        $prop_value{'margin-left'} = $prop_value{'margin-right'};
3966    
3967        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
3968        undef $has_sign;
3969        $sign = 1;
3970        if ($t->{type} == MINUS_TOKEN) {
3971          $t = $tt->get_next_token;
3972          $has_sign = 1;
3973          $sign = -1;
3974        } elsif ($t->{type} == PLUS_TOKEN) {
3975          $t = $tt->get_next_token;
3976          $has_sign = 1;
3977        }
3978    
3979        if ($t->{type} == DIMENSION_TOKEN) {
3980          my $value = $t->{number} * $sign;
3981          my $unit = lc $t->{value}; ## TODO: case
3982          $t = $tt->get_next_token;
3983          if ($length_unit->{$unit}) {
3984            $prop_value{'margin-bottom'} = ['DIMENSION', $value, $unit];
3985          } else {
3986            $onerror->(type => "syntax error:'$prop_name'",
3987                       level => $self->{must_level},
3988                       uri => \$self->{href},
3989                       token => $t);
3990            return ($t, undef);
3991          }
3992        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
3993          my $value = $t->{number} * $sign;
3994          $t = $tt->get_next_token;
3995          $prop_value{'margin-bottom'} = ['PERCENTAGE', $value];
3996        } elsif ($t->{type} == NUMBER_TOKEN and
3997                 ($self->{unitless_px} or $t->{number} == 0)) {
3998          my $value = $t->{number} * $sign;
3999          $t = $tt->get_next_token;
4000          $prop_value{'margin-bottom'} = ['DIMENSION', $value, 'px'];
4001        } elsif (not $has_sign and $t->{type} == IDENT_TOKEN) {
4002          my $prop_value = lc $t->{value}; ## TODO: case folding
4003          $t = $tt->get_next_token;
4004          if ($prop_value eq 'auto') {
4005            $prop_value{'margin-bottom'} = ['KEYWORD', $prop_value];
4006          } else {
4007            $onerror->(type => "syntax error:'$prop_name'",
4008                       level => $self->{must_level},
4009                       uri => \$self->{href},
4010                       token => $t);
4011            return ($t, undef);
4012          }
4013        } else {
4014          if ($has_sign) {
4015            $onerror->(type => "syntax error:'$prop_name'",
4016                       level => $self->{must_level},
4017                       uri => \$self->{href},
4018                       token => $t);
4019            return ($t, undef);
4020          }
4021          return ($t, \%prop_value);
4022        }
4023    
4024        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4025        undef $has_sign;
4026        $sign = 1;
4027        if ($t->{type} == MINUS_TOKEN) {
4028          $t = $tt->get_next_token;
4029          $has_sign = 1;
4030          $sign = -1;
4031        } elsif ($t->{type} == PLUS_TOKEN) {
4032          $t = $tt->get_next_token;
4033          $has_sign = 1;
4034        }
4035    
4036        if ($t->{type} == DIMENSION_TOKEN) {
4037          my $value = $t->{number} * $sign;
4038          my $unit = lc $t->{value}; ## TODO: case
4039          $t = $tt->get_next_token;
4040          if ($length_unit->{$unit}) {
4041            $prop_value{'margin-left'} = ['DIMENSION', $value, $unit];
4042          } else {
4043            $onerror->(type => "syntax error:'$prop_name'",
4044                       level => $self->{must_level},
4045                       uri => \$self->{href},
4046                       token => $t);
4047            return ($t, undef);
4048          }
4049        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4050          my $value = $t->{number} * $sign;
4051          $t = $tt->get_next_token;
4052          $prop_value{'margin-left'} = ['PERCENTAGE', $value];
4053        } elsif ($t->{type} == NUMBER_TOKEN and
4054                 ($self->{unitless_px} or $t->{number} == 0)) {
4055          my $value = $t->{number} * $sign;
4056          $t = $tt->get_next_token;
4057          $prop_value{'margin-left'} = ['DIMENSION', $value, 'px'];
4058        } elsif (not $has_sign and $t->{type} == IDENT_TOKEN) {
4059          my $prop_value = lc $t->{value}; ## TODO: case folding
4060          $t = $tt->get_next_token;
4061          if ($prop_value eq 'auto') {
4062            $prop_value{'margin-left'} = ['KEYWORD', $prop_value];
4063          } else {
4064            $onerror->(type => "syntax error:'$prop_name'",
4065                       level => $self->{must_level},
4066                       uri => \$self->{href},
4067                       token => $t);
4068            return ($t, undef);
4069          }
4070        } else {
4071          if ($has_sign) {
4072            $onerror->(type => "syntax error:'$prop_name'",
4073                       level => $self->{must_level},
4074                       uri => \$self->{href},
4075                       token => $t);
4076            return ($t, undef);
4077          }
4078          return ($t, \%prop_value);
4079        }
4080    
4081        return ($t, \%prop_value);
4082      },
4083      serialize => sub {
4084        my ($self, $prop_name, $value) = @_;
4085        
4086        local $Error::Depth = $Error::Depth + 1;
4087        my @v;
4088        push @v, $self->margin_top;
4089        return '' unless length $v[-1];
4090        push @v, $self->margin_right;
4091        return '' unless length $v[-1];
4092        push @v, $self->margin_bottom;
4093        return '' unless length $v[-1];
4094        push @v, $self->margin_left;
4095        return '' unless length $v[-1];
4096    
4097        pop @v if $v[1] eq $v[3];
4098        pop @v if $v[0] eq $v[2];
4099        pop @v if $v[0] eq $v[1];
4100        return join ' ', @v;
4101      },
4102      serialize_multiple => $Prop->{'margin-top'}->{serialize_multiple},
4103    };
4104    $Attr->{margin} = $Prop->{margin};
4105    
4106    $Prop->{padding} = {
4107      css => 'padding',
4108      dom => 'padding',
4109      parse => sub {
4110        my ($self, $prop_name, $tt, $t, $onerror) = @_;
4111    
4112        my %prop_value;
4113    
4114        my $sign = 1;
4115        if ($t->{type} == MINUS_TOKEN) {
4116          $t = $tt->get_next_token;
4117          $sign = -1;
4118        }
4119    
4120        if ($t->{type} == DIMENSION_TOKEN) {
4121          my $value = $t->{number} * $sign;
4122          my $unit = lc $t->{value}; ## TODO: case
4123          $t = $tt->get_next_token;
4124          if ($length_unit->{$unit} and $value >= 0) {
4125            $prop_value{'padding-top'} = ['DIMENSION', $value, $unit];
4126          } else {
4127            $onerror->(type => "syntax error:'$prop_name'",
4128                       level => $self->{must_level},
4129                       uri => \$self->{href},
4130                       token => $t);
4131            return ($t, undef);
4132          }
4133        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4134          my $value = $t->{number} * $sign;
4135          $t = $tt->get_next_token;
4136          $prop_value{'padding-top'} = ['PERCENTAGE', $value];
4137          unless ($value >= 0) {
4138            $onerror->(type => "syntax error:'$prop_name'",
4139                       level => $self->{must_level},
4140                       uri => \$self->{href},
4141                       token => $t);
4142            return ($t, undef);
4143          }
4144        } elsif ($t->{type} == NUMBER_TOKEN and
4145                 ($self->{unitless_px} or $t->{number} == 0)) {
4146          my $value = $t->{number} * $sign;
4147          $t = $tt->get_next_token;
4148          $prop_value{'padding-top'} = ['DIMENSION', $value, 'px'];
4149          unless ($value >= 0) {
4150            $onerror->(type => "syntax error:'$prop_name'",
4151                       level => $self->{must_level},
4152                       uri => \$self->{href},
4153                       token => $t);
4154            return ($t, undef);
4155          }
4156        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
4157          my $prop_value = lc $t->{value}; ## TODO: case folding
4158          $t = $tt->get_next_token;
4159          if ($prop_value eq 'inherit') {
4160            $prop_value{'padding-top'} = ['INHERIT'];
4161            $prop_value{'padding-right'} = $prop_value{'padding-top'};
4162            $prop_value{'padding-bottom'} = $prop_value{'padding-top'};
4163            $prop_value{'padding-left'} = $prop_value{'padding-right'};
4164            return ($t, \%prop_value);
4165          } else {
4166            $onerror->(type => "syntax error:'$prop_name'",
4167                       level => $self->{must_level},
4168                       uri => \$self->{href},
4169                       token => $t);
4170            return ($t, undef);
4171          }
4172        } else {
4173          $onerror->(type => "syntax error:'$prop_name'",
4174                     level => $self->{must_level},
4175                     uri => \$self->{href},
4176                     token => $t);
4177          return ($t, undef);
4178        }
4179        $prop_value{'padding-right'} = $prop_value{'padding-top'};
4180        $prop_value{'padding-bottom'} = $prop_value{'padding-top'};
4181        $prop_value{'padding-left'} = $prop_value{'padding-right'};
4182    
4183        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4184        $sign = 1;
4185        if ($t->{type} == MINUS_TOKEN) {
4186          $t = $tt->get_next_token;
4187          $sign = -1;
4188        }
4189    
4190        if ($t->{type} == DIMENSION_TOKEN) {
4191          my $value = $t->{number} * $sign;
4192          my $unit = lc $t->{value}; ## TODO: case
4193          $t = $tt->get_next_token;
4194          if ($length_unit->{$unit} and $value >= 0) {
4195            $prop_value{'padding-right'} = ['DIMENSION', $value, $unit];
4196          } else {
4197            $onerror->(type => "syntax error:'$prop_name'",
4198                       level => $self->{must_level},
4199                       uri => \$self->{href},
4200                       token => $t);
4201            return ($t, undef);
4202          }
4203        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4204          my $value = $t->{number} * $sign;
4205          $t = $tt->get_next_token;
4206          $prop_value{'padding-right'} = ['PERCENTAGE', $value];
4207          unless ($value >= 0) {
4208            $onerror->(type => "syntax error:'$prop_name'",
4209                       level => $self->{must_level},
4210                       uri => \$self->{href},
4211                       token => $t);
4212            return ($t, undef);
4213          }
4214        } elsif ($t->{type} == NUMBER_TOKEN and
4215                 ($self->{unitless_px} or $t->{number} == 0)) {
4216          my $value = $t->{number} * $sign;
4217          $t = $tt->get_next_token;
4218          $prop_value{'padding-right'} = ['DIMENSION', $value, 'px'];
4219          unless ($value >= 0) {
4220            $onerror->(type => "syntax error:'$prop_name'",
4221                       level => $self->{must_level},
4222                       uri => \$self->{href},
4223                       token => $t);
4224            return ($t, undef);
4225          }
4226        } else {
4227          if ($sign < 0) {
4228            $onerror->(type => "syntax error:'$prop_name'",
4229                       level => $self->{must_level},
4230                       uri => \$self->{href},
4231                       token => $t);
4232            return ($t, undef);
4233          }
4234          return ($t, \%prop_value);
4235        }
4236        $prop_value{'padding-left'} = $prop_value{'padding-right'};
4237    
4238        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4239        $sign = 1;
4240        if ($t->{type} == MINUS_TOKEN) {
4241          $t = $tt->get_next_token;
4242          $sign = -1;
4243        }
4244    
4245        if ($t->{type} == DIMENSION_TOKEN) {
4246          my $value = $t->{number} * $sign;
4247          my $unit = lc $t->{value}; ## TODO: case
4248          $t = $tt->get_next_token;
4249          if ($length_unit->{$unit} and $value >= 0) {
4250            $prop_value{'padding-bottom'} = ['DIMENSION', $value, $unit];
4251          } else {
4252            $onerror->(type => "syntax error:'$prop_name'",
4253                       level => $self->{must_level},
4254                       uri => \$self->{href},
4255                       token => $t);
4256            return ($t, undef);
4257          }
4258        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4259          my $value = $t->{number} * $sign;
4260          $t = $tt->get_next_token;
4261          $prop_value{'padding-bottom'} = ['PERCENTAGE', $value];
4262          unless ($value >= 0) {
4263            $onerror->(type => "syntax error:'$prop_name'",
4264                       level => $self->{must_level},
4265                       uri => \$self->{href},
4266                       token => $t);
4267            return ($t, undef);
4268          }
4269        } elsif ($t->{type} == NUMBER_TOKEN and
4270                 ($self->{unitless_px} or $t->{number} == 0)) {
4271          my $value = $t->{number} * $sign;
4272          $t = $tt->get_next_token;
4273          $prop_value{'padding-bottom'} = ['DIMENSION', $value, 'px'];
4274          unless ($value >= 0) {
4275            $onerror->(type => "syntax error:'$prop_name'",
4276                       level => $self->{must_level},
4277                       uri => \$self->{href},
4278                       token => $t);
4279            return ($t, undef);
4280          }
4281        } else {
4282          if ($sign < 0) {
4283            $onerror->(type => "syntax error:'$prop_name'",
4284                       level => $self->{must_level},
4285                       uri => \$self->{href},
4286                       token => $t);
4287            return ($t, undef);
4288          }
4289          return ($t, \%prop_value);
4290        }
4291    
4292        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4293        $sign = 1;
4294        if ($t->{type} == MINUS_TOKEN) {
4295          $t = $tt->get_next_token;
4296          $sign = -1;
4297        }
4298    
4299        if ($t->{type} == DIMENSION_TOKEN) {
4300          my $value = $t->{number} * $sign;
4301          my $unit = lc $t->{value}; ## TODO: case
4302          $t = $tt->get_next_token;
4303          if ($length_unit->{$unit} and $value >= 0) {
4304            $prop_value{'padding-left'} = ['DIMENSION', $value, $unit];
4305          } else {
4306            $onerror->(type => "syntax error:'$prop_name'",
4307                       level => $self->{must_level},
4308                       uri => \$self->{href},
4309                       token => $t);
4310            return ($t, undef);
4311          }
4312        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4313          my $value = $t->{number} * $sign;
4314          $t = $tt->get_next_token;
4315          $prop_value{'padding-left'} = ['PERCENTAGE', $value];
4316          unless ($value >= 0) {
4317            $onerror->(type => "syntax error:'$prop_name'",
4318                       level => $self->{must_level},
4319                       uri => \$self->{href},
4320                       token => $t);
4321            return ($t, undef);
4322          }
4323        } elsif ($t->{type} == NUMBER_TOKEN and
4324                 ($self->{unitless_px} or $t->{number} == 0)) {
4325          my $value = $t->{number} * $sign;
4326          $t = $tt->get_next_token;
4327          $prop_value{'padding-left'} = ['DIMENSION', $value, 'px'];
4328          unless ($value >= 0) {
4329            $onerror->(type => "syntax error:'$prop_name'",
4330                       level => $self->{must_level},
4331                       uri => \$self->{href},
4332                       token => $t);
4333            return ($t, undef);
4334          }
4335        } else {
4336          if ($sign < 0) {
4337            $onerror->(type => "syntax error:'$prop_name'",
4338                       level => $self->{must_level},
4339                       uri => \$self->{href},
4340                       token => $t);
4341            return ($t, undef);
4342          }
4343          return ($t, \%prop_value);
4344        }
4345    
4346        return ($t, \%prop_value);
4347      },
4348      serialize => sub {
4349        my ($self, $prop_name, $value) = @_;
4350        
4351        local $Error::Depth = $Error::Depth + 1;
4352        my @v;
4353        push @v, $self->padding_top;
4354        return '' unless length $v[-1];
4355        push @v, $self->padding_right;
4356        return '' unless length $v[-1];
4357        push @v, $self->padding_bottom;
4358        return '' unless length $v[-1];
4359        push @v, $self->padding_left;
4360        return '' unless length $v[-1];
4361    
4362        pop @v if $v[1] eq $v[3];
4363        pop @v if $v[0] eq $v[2];
4364        pop @v if $v[0] eq $v[1];
4365        return join ' ', @v;
4366      },
4367    };
4368    $Attr->{padding} = $Prop->{padding};
4369    
4370    $Prop->{'border-spacing'} = {
4371      css => 'border-spacing',
4372      dom => 'border_spacing',
4373      parse => sub {
4374        my ($self, $prop_name, $tt, $t, $onerror) = @_;
4375    
4376        my %prop_value;
4377    
4378        my $sign = 1;
4379        if ($t->{type} == MINUS_TOKEN) {
4380          $t = $tt->get_next_token;
4381          $sign = -1;
4382        }
4383    
4384        if ($t->{type} == DIMENSION_TOKEN) {
4385          my $value = $t->{number} * $sign;
4386          my $unit = lc $t->{value}; ## TODO: case
4387          $t = $tt->get_next_token;
4388          if ($length_unit->{$unit} and $value >= 0) {
4389            $prop_value{'-manakai-border-spacing-x'} = ['DIMENSION', $value, $unit];
4390          } else {
4391            $onerror->(type => "syntax error:'$prop_name'",
4392                       level => $self->{must_level},
4393                       uri => \$self->{href},
4394                       token => $t);
4395            return ($t, undef);
4396          }
4397        } elsif ($t->{type} == NUMBER_TOKEN and
4398                 ($self->{unitless_px} or $t->{number} == 0)) {
4399          my $value = $t->{number} * $sign;
4400          $t = $tt->get_next_token;
4401          $prop_value{'-manakai-border-spacing-x'} = ['DIMENSION', $value, 'px'];
4402          unless ($value >= 0) {
4403            $onerror->(type => "syntax error:'$prop_name'",
4404                       level => $self->{must_level},
4405                       uri => \$self->{href},
4406                       token => $t);
4407            return ($t, undef);
4408          }
4409        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
4410          my $prop_value = lc $t->{value}; ## TODO: case folding
4411          $t = $tt->get_next_token;
4412          if ($prop_value eq 'inherit') {
4413            $prop_value{'-manakai-border-spacing-x'} = ['INHERIT'];
4414            $prop_value{'-manakai-border-spacing-y'}
4415                = $prop_value{'-manakai-border-spacing-x'};
4416            return ($t, \%prop_value);
4417          } else {
4418            $onerror->(type => "syntax error:'$prop_name'",
4419                       level => $self->{must_level},
4420                       uri => \$self->{href},
4421                       token => $t);
4422            return ($t, undef);
4423          }
4424        } else {
4425          $onerror->(type => "syntax error:'$prop_name'",
4426                     level => $self->{must_level},
4427                     uri => \$self->{href},
4428                     token => $t);
4429          return ($t, undef);
4430        }
4431        $prop_value{'-manakai-border-spacing-y'}
4432            = $prop_value{'-manakai-border-spacing-x'};
4433    
4434        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4435        $sign = 1;
4436        if ($t->{type} == MINUS_TOKEN) {
4437          $t = $tt->get_next_token;
4438          $sign = -1;
4439        }
4440    
4441        if ($t->{type} == DIMENSION_TOKEN) {
4442          my $value = $t->{number} * $sign;
4443          my $unit = lc $t->{value}; ## TODO: case
4444          $t = $tt->get_next_token;
4445          if ($length_unit->{$unit} and $value >= 0) {
4446            $prop_value{'-manakai-border-spacing-y'} = ['DIMENSION', $value, $unit];
4447          } else {
4448            $onerror->(type => "syntax error:'$prop_name'",
4449                       level => $self->{must_level},
4450                       uri => \$self->{href},
4451                       token => $t);
4452            return ($t, undef);
4453          }
4454        } elsif ($t->{type} == NUMBER_TOKEN and
4455                 ($self->{unitless_px} or $t->{number} == 0)) {
4456          my $value = $t->{number} * $sign;
4457          $t = $tt->get_next_token;
4458          $prop_value{'-manakai-border-spacing-y'} = ['DIMENSION', $value, 'px'];
4459          unless ($value >= 0) {
4460            $onerror->(type => "syntax error:'$prop_name'",
4461                       level => $self->{must_level},
4462                       uri => \$self->{href},
4463                       token => $t);
4464            return ($t, undef);
4465          }
4466        } else {
4467          if ($sign < 0) {
4468            $onerror->(type => "syntax error:'$prop_name'",
4469                       level => $self->{must_level},
4470                       uri => \$self->{href},
4471                       token => $t);
4472            return ($t, undef);
4473          }
4474          return ($t, \%prop_value);
4475        }
4476    
4477        return ($t, \%prop_value);
4478      },
4479      serialize => sub {
4480        my ($self, $prop_name, $value) = @_;
4481        
4482        local $Error::Depth = $Error::Depth + 1;
4483        my @v;
4484        push @v, $self->_manakai_border_spacing_x;
4485        return '' unless length $v[-1];
4486        push @v, $self->_manakai_border_spacing_y;
4487        return '' unless length $v[-1];
4488    
4489        pop @v if $v[0] eq $v[1];
4490        return join ' ', @v;
4491      },
4492      serialize_multiple => $Prop->{'-manakai-border-spacing-x'}
4493          ->{serialize_multiple},
4494    };
4495    $Attr->{border_spacing} = $Prop->{'border-spacing'};
4496    
4497    ## NOTE: See <http://suika.fam.cx/gate/2005/sw/background-position> for
4498    ## browser compatibility problems.
4499    $Prop->{'background-position'} = {
4500      css => 'background-position',
4501      dom => 'background_position',
4502      parse => sub {
4503        my ($self, $prop_name, $tt, $t, $onerror) = @_;
4504    
4505        my %prop_value;
4506    
4507        my $sign = 1;
4508        if ($t->{type} == MINUS_TOKEN) {
4509          $t = $tt->get_next_token;
4510          $sign = -1;
4511        }
4512    
4513        if ($t->{type} == DIMENSION_TOKEN) {
4514          my $value = $t->{number} * $sign;
4515          my $unit = lc $t->{value}; ## TODO: case
4516          $t = $tt->get_next_token;
4517          if ($length_unit->{$unit}) {
4518            $prop_value{'background-position-x'} = ['DIMENSION', $value, $unit];
4519            $prop_value{'background-position-y'} = ['PERCENTAGE', 50];
4520          } else {
4521            $onerror->(type => "syntax error:'$prop_name'",
4522                       level => $self->{must_level},
4523                       uri => \$self->{href},
4524                       token => $t);
4525            return ($t, undef);
4526          }
4527        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4528          my $value = $t->{number} * $sign;
4529          $t = $tt->get_next_token;
4530          $prop_value{'background-position-x'} = ['PERCENTAGE', $value];
4531          $prop_value{'background-position-y'} = ['PERCENTAGE', 50];
4532        } elsif ($t->{type} == NUMBER_TOKEN and
4533                 ($self->{unitless_px} or $t->{number} == 0)) {
4534          my $value = $t->{number} * $sign;
4535          $t = $tt->get_next_token;
4536          $prop_value{'background-position-x'} = ['DIMENSION', $value, 'px'];
4537          $prop_value{'background-position-y'} = ['PERCENTAGE', 50];
4538        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
4539          my $prop_value = lc $t->{value}; ## TODO: case folding
4540          $t = $tt->get_next_token;
4541          if ({left => 1, center => 1, right => 1}->{$prop_value}) {
4542            $prop_value{'background-position-x'} = ['KEYWORD', $prop_value];
4543            $prop_value{'background-position-y'} = ['KEYWORD', 'center'];
4544          } elsif ($prop_value eq 'top' or $prop_value eq 'bottom') {
4545            $prop_value{'background-position-y'} = ['KEYWORD', $prop_value];
4546    
4547            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4548            if ($t->{type} == IDENT_TOKEN) {
4549              my $prop_value = lc $t->{value}; ## TODO: case folding
4550              if ({left => 1, center => 1, right => 1}->{$prop_value}) {
4551                $prop_value{'background-position-x'} = ['KEYWORD', $prop_value];
4552                $t = $tt->get_next_token;
4553                return ($t, \%prop_value);
4554              }
4555            }
4556            $prop_value{'background-position-x'} = ['KEYWORD', 'center'];
4557            return ($t, \%prop_value);
4558          } elsif ($prop_value eq 'inherit') {
4559            $prop_value{'background-position-x'} = ['INHERIT'];
4560            $prop_value{'background-position-y'} = ['INHERIT'];
4561            return ($t, \%prop_value);
4562          } else {
4563            $onerror->(type => "syntax error:'$prop_name'",
4564                       level => $self->{must_level},
4565                       uri => \$self->{href},
4566                       token => $t);
4567            return ($t, undef);
4568          }
4569        } else {
4570          $onerror->(type => "syntax error:'$prop_name'",
4571                     level => $self->{must_level},
4572                     uri => \$self->{href},
4573                     token => $t);
4574          return ($t, undef);
4575        }
4576    
4577        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4578        $sign = 1;
4579        if ($t->{type} == MINUS_TOKEN) {
4580          $t = $tt->get_next_token;
4581          $sign = -1;
4582        }
4583    
4584        if ($t->{type} == DIMENSION_TOKEN) {
4585          my $value = $t->{number} * $sign;
4586          my $unit = lc $t->{value}; ## TODO: case
4587          $t = $tt->get_next_token;
4588          if ($length_unit->{$unit}) {
4589            $prop_value{'background-position-y'} = ['DIMENSION', $value, $unit];
4590          } else {
4591            $onerror->(type => "syntax error:'$prop_name'",
4592                       level => $self->{must_level},
4593                       uri => \$self->{href},
4594                       token => $t);
4595            return ($t, undef);
4596          }
4597        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4598          my $value = $t->{number} * $sign;
4599          $t = $tt->get_next_token;
4600          $prop_value{'background-position-y'} = ['PERCENTAGE', $value];
4601        } elsif ($t->{type} == NUMBER_TOKEN and
4602                 ($self->{unitless_px} or $t->{number} == 0)) {
4603          my $value = $t->{number} * $sign;
4604          $t = $tt->get_next_token;
4605          $prop_value{'background-position-y'} = ['DIMENSION', $value, 'px'];
4606        } elsif ($t->{type} == IDENT_TOKEN) {
4607          my $value = lc $t->{value}; ## TODO: case
4608          if ({top => 1, center => 1, bottom => 1}->{$value}) {
4609            $prop_value{'background-position-y'} = ['KEYWORD', $value];
4610            $t = $tt->get_next_token;
4611          }
4612        } else {
4613          if ($sign < 0) {
4614            $onerror->(type => "syntax error:'$prop_name'",
4615                       level => $self->{must_level},
4616                       uri => \$self->{href},
4617                       token => $t);
4618            return ($t, undef);
4619          }
4620          return ($t, \%prop_value);
4621        }
4622    
4623        return ($t, \%prop_value);
4624      },
4625      serialize => sub {
4626        my ($self, $prop_name, $value) = @_;
4627        
4628        local $Error::Depth = $Error::Depth + 1;
4629        my $x = $self->background_position_x;
4630        my $y = $self->background_position_y;
4631        return $x . ' ' . $y if length $x and length $y;
4632        return '';
4633      },
4634      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
4635    };
4636    $Attr->{background_position} = $Prop->{'background-position'};
4637    
4638    $Prop->{background} = {
4639      css => 'background',
4640      dom => 'background',
4641      parse => sub {
4642        my ($self, $prop_name, $tt, $t, $onerror) = @_;
4643        my %prop_value;
4644        B: for (1..5) {
4645          my $sign = 1;
4646          if ($t->{type} == MINUS_TOKEN) {
4647            $sign = -1;
4648            $t = $tt->get_next_token;
4649          }
4650    
4651          if ($sign > 0 and $t->{type} == IDENT_TOKEN) {
4652            my $value = lc $t->{value}; ## TODO: case
4653            if ($Prop->{'background-repeat'}->{keyword}->{$value} and
4654                $self->{prop_value}->{'background-repeat'}->{$value} and
4655                not defined $prop_value{'background-repeat'}) {
4656              $prop_value{'background-repeat'} = ['KEYWORD', $value];
4657              $t = $tt->get_next_token;
4658            } elsif ($Prop->{'background-attachment'}->{keyword}->{$value} and
4659                     $self->{prop_value}->{'background-attachment'}->{$value} and
4660                     not defined $prop_value{'background-attachment'}) {
4661              $prop_value{'background-attachment'} = ['KEYWORD', $value];
4662              $t = $tt->get_next_token;
4663            } elsif ($value eq 'none' and
4664                     not defined $prop_value{'background-image'}) {
4665              $prop_value{'background-image'} = ['KEYWORD', $value];
4666              $t = $tt->get_next_token;
4667            } elsif ({left => 1, center => 1, right => 1}->{$value} and
4668                     not defined $prop_value{'background-position-x'}) {
4669              $prop_value{'background-position-x'} = ['KEYWORD', $value];
4670              $t = $tt->get_next_token;
4671              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4672              my $sign = 1;
4673              if ($t->{type} == MINUS_TOKEN) {
4674                $sign = -1;
4675                $t = $tt->get_next_token;
4676              }
4677              if ($sign > 0 and $t->{type} == IDENT_TOKEN) {
4678                my $value = lc $t->{value}; ## TODO: case
4679                if ({top => 1, bottom => 1, center => 1}->{$value}) {
4680                  $prop_value{'background-position-y'} = ['KEYWORD', $value];
4681                  $t = $tt->get_next_token;
4682                } elsif ($prop_value{'background-position-x'}->[1] eq 'center' and
4683                         $value eq 'left' or $value eq 'right') {
4684                  $prop_value{'background-position-y'} = ['KEYWORD', 'center'];
4685                  $prop_value{'background-position-x'} = ['KEYWORD', $value];
4686                  $t = $tt->get_next_token;
4687                } else {
4688                  $prop_value{'background-position-y'} = ['KEYWORD', 'center'];
4689                }
4690              } elsif ($t->{type} == DIMENSION_TOKEN) {
4691                my $value = $t->{number} * $sign;
4692                my $unit = lc $t->{value}; ## TODO: case
4693                $t = $tt->get_next_token;
4694                if ($length_unit->{$unit}) {
4695                  $prop_value{'background-position-y'}
4696                      = ['DIMENSION', $value, $unit];
4697                } else {
4698                  $onerror->(type => "syntax error:'$prop_name'",
4699                             level => $self->{must_level},
4700                             uri => \$self->{href},
4701                             token => $t);
4702                  last B;
4703                }
4704              } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4705                my $value = $t->{number} * $sign;
4706                $t = $tt->get_next_token;
4707                $prop_value{'background-position-y'} = ['PERCENTAGE', $value];
4708              } elsif ($t->{type} == NUMBER_TOKEN and
4709                       ($self->{unitless_px} or $t->{number} == 0)) {
4710                my $value = $t->{number} * $sign;
4711                $t = $tt->get_next_token;
4712                $prop_value{'background-position-y'} = ['DIMENSION', $value, 'px'];
4713              } elsif ($sign < 0) {
4714                $onerror->(type => "syntax error:'$prop_name'",
4715                           level => $self->{must_level},
4716                           uri => \$self->{href},
4717                           token => $t);
4718                last B;
4719              }
4720            } elsif (($value eq 'top' or $value eq 'bottom') and
4721                     not defined $prop_value{'background-position-y'}) {
4722              $prop_value{'background-position-y'} = ['KEYWORD', $value];
4723              $t = $tt->get_next_token;
4724              $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4725              if ($t->{type} == IDENT_TOKEN and ## TODO: case
4726                  {left => 1, center => 1, right => 1}->{lc $t->{value}}) {
4727                $prop_value{'background-position-x'} = ['KEYWORD', $value];
4728                $t = $tt->get_next_token;
4729              } else {
4730                $prop_value{'background-position-x'} = ['KEYWORD', 'center'];
4731              }
4732            } elsif ($value eq 'inherit' and not keys %prop_value) {
4733              $prop_value{'background-color'} =
4734              $prop_value{'background-image'} =
4735              $prop_value{'background-repeat'} =
4736              $prop_value{'background-attachment'} =
4737              $prop_value{'background-position-x'} =
4738              $prop_value{'background-position-y'} = ['INHERIT'];
4739              $t = $tt->get_next_token;
4740              return ($t, \%prop_value);
4741            } elsif (not defined $prop_value{'background-color'} or
4742                     not keys %prop_value) {
4743              ($t, my $pv) = $parse_color->($self, 'background', $tt, $t,
4744                                            $onerror);
4745              if (defined $pv) {
4746                $prop_value{'background-color'} = $pv->{background};
4747              } else {
4748                ## NOTE: An error should already be raiased.
4749                return ($t, undef);
4750              }
4751            }
4752          } elsif (($t->{type} == DIMENSION_TOKEN or
4753                    $t->{type} == PERCENTAGE_TOKEN or
4754                    ($t->{type} == NUMBER_TOKEN and
4755                     $t->{unitless_px} or $t->{number} == 0)) and
4756                   not defined $prop_value{'background-position-x'}) {
4757            if ($t->{type} == DIMENSION_TOKEN) {
4758              my $value = $t->{number} * $sign;
4759              my $unit = lc $t->{value}; ## TODO: case
4760              $t = $tt->get_next_token;
4761              if ($length_unit->{$unit}) {
4762                $prop_value{'background-position-x'}
4763                    = ['DIMENSION', $value, $unit];
4764              } else {
4765                $onerror->(type => "syntax error:'$prop_name'",
4766                           level => $self->{must_level},
4767                           uri => \$self->{href},
4768                           token => $t);
4769                last B;
4770              }
4771            } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4772              my $value = $t->{number} * $sign;
4773              $t = $tt->get_next_token;
4774              $prop_value{'background-position-x'} = ['PERCENTAGE', $value];
4775            } elsif ($t->{type} == NUMBER_TOKEN and
4776                     ($self->{unitless_px} or $t->{number} == 0)) {
4777              my $value = $t->{number} * $sign;
4778              $t = $tt->get_next_token;
4779              $prop_value{'background-position-x'} = ['DIMENSION', $value, 'px'];
4780            } else {
4781              ## NOTE: Should not be happened.
4782              last B;
4783            }
4784            
4785            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4786            if ($t->{type} == MINUS_TOKEN) {
4787              $sign = -1;
4788              $t = $tt->get_next_token;
4789            } else {
4790              $sign = 1;
4791            }
4792    
4793            if ($t->{type} == DIMENSION_TOKEN) {
4794              my $value = $t->{number} * $sign;
4795              my $unit = lc $t->{value}; ## TODO: case
4796              $t = $tt->get_next_token;
4797              if ($length_unit->{$unit}) {
4798                $prop_value{'background-position-y'}
4799                    = ['DIMENSION', $value, $unit];
4800              } else {
4801                $onerror->(type => "syntax error:'$prop_name'",
4802                           level => $self->{must_level},
4803                           uri => \$self->{href},
4804                           token => $t);
4805                last B;
4806              }
4807            } elsif ($t->{type} == PERCENTAGE_TOKEN) {
4808              my $value = $t->{number} * $sign;
4809              $t = $tt->get_next_token;
4810              $prop_value{'background-position-y'} = ['PERCENTAGE', $value];
4811            } elsif ($t->{type} == NUMBER_TOKEN and
4812                     ($self->{unitless_px} or $t->{number} == 0)) {
4813              my $value = $t->{number} * $sign;
4814              $t = $tt->get_next_token;
4815              $prop_value{'background-position-y'} = ['DIMENSION', $value, 'px'];
4816            } elsif ($t->{type} == IDENT_TOKEN) {
4817              my $value = lc $t->{value}; ## TODO: case
4818              if ({top => 1, center => 1, bottom => 1}->{$value}) {
4819                $prop_value{'background-position-y'} = ['KEYWORD', $value];
4820                $t = $tt->get_next_token;
4821              } else {
4822                $prop_value{'background-position-y'} = ['PERCENTAGE', 50];
4823              }
4824            } else {
4825              if ($sign > 0) {
4826                $prop_value{'background-position-y'} = ['PERCENTAGE', 50];
4827              } else {
4828                $onerror->(type => "syntax error:'$prop_name'",
4829                           level => $self->{must_level},
4830                           uri => \$self->{href},
4831                           token => $t);
4832              }
4833            }
4834          } elsif ($t->{type} == URI_TOKEN and
4835                   not defined $prop_value{'background-image'}) {
4836            $prop_value{'background-image'} = ['URI', $t->{value}];
4837            $t = $tt->get_next_token;
4838          } else {
4839            if (keys %prop_value and $sign > 0) {
4840              last B;
4841            } else {
4842              $onerror->(type => "syntax error:'$prop_name'",
4843                         level => $self->{must_level},
4844                         uri => \$self->{href},
4845                         token => $t);
4846            }
4847          }
4848    
4849          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4850        } # B
4851    
4852        $prop_value{$_} ||= $Prop->{$_}->{initial}
4853            for qw/background-image background-attachment background-repeat
4854                   background-color background-position-x background-position-y/;
4855    
4856        return ($t, \%prop_value);
4857      },
4858      serialize => sub {
4859        my ($self, $prop_name, $value) = @_;
4860        
4861        local $Error::Depth = $Error::Depth + 1;
4862        my $color = $self->background_color;
4863        return '' unless length $color;
4864        my $image = $self->background_image;
4865        return '' unless length $image;
4866        my $repeat = $self->background_repeat;
4867        return '' unless length $repeat;
4868        my $attachment = $self->background_attachment;
4869        return '' unless length $attachment;
4870        my $position = $self->background_position;
4871        return '' unless length $position;
4872    
4873        my @v;
4874        push @v, $color unless $color eq 'transparent';
4875        push @v, $image unless $image eq 'none';
4876        push @v, $repeat unless $repeat eq 'repeat';
4877        push @v, $attachment unless $attachment eq 'scroll';
4878        push @v, $position unless $position eq '0% 0%';
4879        if (@v) {
4880          return join ' ', @v;
4881        } else {
4882          return 'transparent none repeat scroll 0% 0%';
4883        }
4884      },
4885      serialize_multiple => $Prop->{'background-color'}->{serialize_multiple},
4886    };
4887    $Attr->{background} = $Prop->{background};
4888    
4889    $Prop->{font} = {
4890      css => 'font',
4891      dom => 'font',
4892      parse => sub {
4893        my ($self, $prop_name, $tt, $t, $onerror) = @_;
4894    
4895        my %prop_value;
4896    
4897        A: for (1..3) {
4898          if ($t->{type} == IDENT_TOKEN) {
4899            my $value = lc $t->{value}; ## TODO: case
4900            if ($value eq 'normal') {
4901              $t = $tt->get_next_token;
4902            } elsif ($Prop->{'font-style'}->{keyword}->{$value} and
4903                     $self->{prop_value}->{'font-style'}->{$value} and
4904                     not defined $prop_value{'font-style'}) {
4905              $prop_value{'font-style'} = ['KEYWORD', $value];
4906              $t = $tt->get_next_token;
4907            } elsif ($Prop->{'font-variant'}->{keyword}->{$value} and
4908                     $self->{prop_value}->{'font-variant'}->{$value} and
4909                     not defined $prop_value{'font-variant'}) {
4910              $prop_value{'font-variant'} = ['KEYWORD', $value];
4911              $t = $tt->get_next_token;
4912            } elsif ({normal => 1, bold => 1,
4913                      bolder => 1, lighter => 1}->{$value} and
4914                     not defined $prop_value{'font-weight'}) {
4915              $prop_value{'font-weight'} = ['KEYWORD', $value];
4916              $t = $tt->get_next_token;
4917            } elsif ($value eq 'inherit' and 0 == keys %prop_value) {
4918              $t = $tt->get_next_token;
4919              return ($t, {'font-style' => ['INHERIT'],
4920                           'font-variant' => ['INHERIT'],
4921                           'font-weight' => ['INHERIT'],
4922                           'font-size' => ['INHERIT'],
4923                           'line-height' => ['INHERIT'],
4924                           'font-family' => ['INHERIT']});
4925            } elsif ({
4926                      caption => 1, icon => 1, menu => 1,
4927                      'message-box' => 1, 'small-caption' => 1, 'status-bar' => 1,
4928                     }->{$value} and 0 == keys %prop_value) {
4929              $t = $tt->get_next_token;
4930              return ($t, $self->{get_system_font}->($self, $value, {
4931                'font-style' => $Prop->{'font-style'}->{initial},
4932                'font-variant' => $Prop->{'font-variant'}->{initial},
4933                'font-weight' => $Prop->{'font-weight'}->{initial},
4934                'font-size' => $Prop->{'font-size'}->{initial},
4935                'line-height' => $Prop->{'line-height'}->{initial},
4936                'font-family' => ['FONT', ['KEYWORD', '-manakai-'.$value]],
4937              }));
4938            } else {
4939              if (keys %prop_value) {
4940                last A;
4941              } else {
4942                $onerror->(type => "syntax error:'$prop_name'",
4943                           level => $self->{must_level},
4944                           uri => \$self->{href},
4945                           token => $t);
4946                return ($t, undef);
4947              }
4948            }
4949          } elsif ($t->{type} == NUMBER_TOKEN) {
4950            if ({100 => 1, 200 => 1, 300 => 1, 400 => 1, 500 => 1,
4951                 600 => 1, 700 => 1, 800 => 1, 900 => 1}->{$t->{value}}) {
4952              $prop_value{'font-weight'} = ['NUMBER', $t->{value}];
4953              $t = $tt->get_next_token;
4954            } else {
4955              last A;
4956            }
4957          }
4958    
4959          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4960        } # A
4961        
4962        for (qw/font-style font-variant font-weight/) {
4963          $prop_value{$_} = $Prop->{$_}->{initial} unless defined $prop_value{$_};
4964        }
4965          
4966        ($t, my $pv) = $Prop->{'font-size'}->{parse}
4967            ->($self, 'font', $tt, $t, $onerror);
4968        return ($t, undef) unless defined $pv;
4969        if ($pv->{font}->[0] eq 'INHERIT') {
4970          $onerror->(type => "syntax error:'$prop_name'",
4971                     level => $self->{must_level},
4972                     uri => \$self->{href},
4973                     token => $t);
4974          return ($t, undef);
4975        }
4976        $prop_value{'font-size'} = $pv->{font};
4977    
4978        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4979        if ($t->{type} == DELIM_TOKEN and $t->{value} eq '/') {
4980          $t = $tt->get_next_token;
4981          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4982          ($t, my $pv) = $Prop->{'line-height'}->{parse}
4983              ->($self, 'font', $tt, $t, $onerror);
4984          return ($t, undef) unless defined $pv;
4985          if ($pv->{font}->[0] eq 'INHERIT') {
4986            $onerror->(type => "syntax error:'$prop_name'",
4987                       level => $self->{must_level},
4988                       uri => \$self->{href},
4989                       token => $t);
4990            return ($t, undef);
4991          }
4992          $prop_value{'line-height'} = $pv->{font};
4993          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
4994        } else {
4995          $prop_value{'line-height'} = $Prop->{'line-height'}->{initial};
4996        }
4997    
4998        undef $pv;
4999        ($t, $pv) = $Prop->{'font-family'}->{parse}
5000            ->($self, 'font', $tt, $t, $onerror);
5001        return ($t, undef) unless defined $pv;
5002        $prop_value{'font-family'} = $pv->{font};
5003    
5004        return ($t, \%prop_value);
5005      },
5006      serialize => sub {
5007        my ($self, $prop_name, $value) = @_;
5008        
5009        local $Error::Depth = $Error::Depth + 1;
5010        my $style = $self->font_style;
5011        my $i = $self->get_property_priority ('font-style');
5012        return '' unless length $style;
5013        my $variant = $self->font_variant;
5014        return '' unless length $variant;
5015        return '' if $i ne $self->get_property_priority ('font-variant');
5016        my $weight = $self->font_weight;
5017        return '' unless length $weight;
5018        return '' if $i ne $self->get_property_priority ('font-weight');
5019        my $size = $self->font_size;
5020        return '' unless length $size;
5021        return '' if $i ne $self->get_property_priority ('font-size');
5022        my $height = $self->line_height;
5023        return '' unless length $height;
5024        return '' if $i ne $self->get_property_priority ('line-height');
5025        my $family = $self->font_family;
5026        return '' unless length $family;
5027        return '' if $i ne $self->get_property_priority ('font-family');
5028        
5029        my @v;
5030        push @v, $style unless $style eq 'normal';
5031        push @v, $variant unless $variant eq 'normal';
5032        push @v, $weight unless $weight eq 'normal';
5033        push @v, $size.($height eq 'normal' ? '' : '/'.$height);
5034        push @v, $family;
5035        push @v, '! '.$i if length $i;
5036        return join ' ', @v;
5037      },
5038    };
5039    $Attr->{font} = $Prop->{font};
5040    
5041    $Prop->{'border-width'} = {
5042      css => 'border-width',
5043      dom => 'border_width',
5044      parse => sub {
5045        my ($self, $prop_name, $tt, $t, $onerror) = @_;
5046    
5047        my %prop_value;
5048    
5049        my $sign = 1;
5050        if ($t->{type} == MINUS_TOKEN) {
5051          $t = $tt->get_next_token;
5052          $sign = -1;
5053        }
5054    
5055        if ($t->{type} == DIMENSION_TOKEN) {
5056          my $value = $t->{number} * $sign;
5057          my $unit = lc $t->{value}; ## TODO: case
5058          $t = $tt->get_next_token;
5059          if ($length_unit->{$unit} and $value >= 0) {
5060            $prop_value{'border-top-width'} = ['DIMENSION', $value, $unit];
5061          } else {
5062            $onerror->(type => "syntax error:'$prop_name'",
5063                       level => $self->{must_level},
5064                       uri => \$self->{href},
5065                       token => $t);
5066            return ($t, undef);
5067          }
5068        } elsif ($t->{type} == NUMBER_TOKEN and
5069                 ($self->{unitless_px} or $t->{number} == 0)) {
5070          my $value = $t->{number} * $sign;
5071          $t = $tt->get_next_token;
5072          $prop_value{'border-top-width'} = ['DIMENSION', $value, 'px'];
5073          unless ($value >= 0) {
5074            $onerror->(type => "syntax error:'$prop_name'",
5075                       level => $self->{must_level},
5076                       uri => \$self->{href},
5077                       token => $t);
5078            return ($t, undef);
5079          }
5080        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
5081          my $prop_value = lc $t->{value}; ## TODO: case folding
5082          $t = $tt->get_next_token;
5083          if ({thin => 1, medium => 1, thick => 1}->{$prop_value}) {
5084            $prop_value{'border-top-width'} = ['KEYWORD', $prop_value];
5085          } elsif ($prop_value eq 'inherit') {
5086            $prop_value{'border-top-width'} = ['INHERIT'];
5087            $prop_value{'border-right-width'} = $prop_value{'border-top-width'};
5088            $prop_value{'border-bottom-width'} = $prop_value{'border-top-width'};
5089            $prop_value{'border-left-width'} = $prop_value{'border-right-width'};
5090            return ($t, \%prop_value);
5091          } else {
5092            $onerror->(type => "syntax error:'$prop_name'",
5093                       level => $self->{must_level},
5094                       uri => \$self->{href},
5095                       token => $t);
5096            return ($t, undef);
5097          }
5098        } else {
5099          $onerror->(type => "syntax error:'$prop_name'",
5100                     level => $self->{must_level},
5101                     uri => \$self->{href},
5102                     token => $t);
5103          return ($t, undef);
5104        }
5105        $prop_value{'border-right-width'} = $prop_value{'border-top-width'};
5106        $prop_value{'border-bottom-width'} = $prop_value{'border-top-width'};
5107        $prop_value{'border-left-width'} = $prop_value{'border-right-width'};
5108    
5109        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
5110        $sign = 1;
5111        if ($t->{type} == MINUS_TOKEN) {
5112          $t = $tt->get_next_token;
5113          $sign = -1;
5114        }
5115    
5116        if ($t->{type} == DIMENSION_TOKEN) {
5117          my $value = $t->{number} * $sign;
5118          my $unit = lc $t->{value}; ## TODO: case
5119          $t = $tt->get_next_token;
5120          if ($length_unit->{$unit} and $value >= 0) {
5121            $prop_value{'border-right-width'} = ['DIMENSION', $value, $unit];
5122          } else {
5123            $onerror->(type => "syntax error:'$prop_name'",
5124                       level => $self->{must_level},
5125                       uri => \$self->{href},
5126                       token => $t);
5127            return ($t, undef);
5128          }
5129        } elsif ($t->{type} == NUMBER_TOKEN and
5130                 ($self->{unitless_px} or $t->{number} == 0)) {
5131          my $value = $t->{number} * $sign;
5132          $t = $tt->get_next_token;
5133          $prop_value{'border-right-width'} = ['DIMENSION', $value, 'px'];
5134          unless ($value >= 0) {
5135            $onerror->(type => "syntax error:'$prop_name'",
5136                       level => $self->{must_level},
5137                       uri => \$self->{href},
5138                       token => $t);
5139            return ($t, undef);
5140          }
5141        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
5142          my $prop_value = lc $t->{value}; ## TODO: case
5143          if ({thin => 1, medium => 1, thick => 1}->{$prop_value}) {
5144            $prop_value{'border-right-width'} = ['KEYWORD', $prop_value];
5145            $t = $tt->get_next_token;
5146          }
5147        } else {
5148          if ($sign < 0) {
5149            $onerror->(type => "syntax error:'$prop_name'",
5150                       level => $self->{must_level},
5151                       uri => \$self->{href},
5152                       token => $t);
5153            return ($t, undef);
5154          }
5155          return ($t, \%prop_value);
5156        }
5157        $prop_value{'border-left-width'} = $prop_value{'border-right-width'};
5158    
5159        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
5160        $sign = 1;
5161        if ($t->{type} == MINUS_TOKEN) {
5162          $t = $tt->get_next_token;
5163          $sign = -1;
5164        }
5165    
5166        if ($t->{type} == DIMENSION_TOKEN) {
5167          my $value = $t->{number} * $sign;
5168          my $unit = lc $t->{value}; ## TODO: case
5169          $t = $tt->get_next_token;
5170          if ($length_unit->{$unit} and $value >= 0) {
5171            $prop_value{'border-bottom-width'} = ['DIMENSION', $value, $unit];
5172          } else {
5173            $onerror->(type => "syntax error:'$prop_name'",
5174                       level => $self->{must_level},
5175                       uri => \$self->{href},
5176                       token => $t);
5177            return ($t, undef);
5178          }
5179        } elsif ($t->{type} == NUMBER_TOKEN and
5180                 ($self->{unitless_px} or $t->{number} == 0)) {
5181          my $value = $t->{number} * $sign;
5182          $t = $tt->get_next_token;
5183          $prop_value{'border-bottom-width'} = ['DIMENSION', $value, 'px'];
5184          unless ($value >= 0) {
5185            $onerror->(type => "syntax error:'$prop_name'",
5186                       level => $self->{must_level},
5187                       uri => \$self->{href},
5188                       token => $t);
5189            return ($t, undef);
5190          }
5191        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
5192          my $prop_value = lc $t->{value}; ## TODO: case
5193          if ({thin => 1, medium => 1, thick => 1}->{$prop_value}) {
5194            $prop_value{'border-bottom-width'} = ['KEYWORD', $prop_value];
5195            $t = $tt->get_next_token;
5196          }
5197        } else {
5198          if ($sign < 0) {
5199            $onerror->(type => "syntax error:'$prop_name'",
5200                       level => $self->{must_level},
5201                       uri => \$self->{href},
5202                       token => $t);
5203            return ($t, undef);
5204          }
5205          return ($t, \%prop_value);
5206        }
5207    
5208        $t = $tt->get_next_token while $t->{type} == S_TOKEN;
5209        $sign = 1;
5210        if ($t->{type} == MINUS_TOKEN) {
5211          $t = $tt->get_next_token;
5212          $sign = -1;
5213        }
5214    
5215        if ($t->{type} == DIMENSION_TOKEN) {
5216          my $value = $t->{number} * $sign;
5217          my $unit = lc $t->{value}; ## TODO: case
5218          $t = $tt->get_next_token;
5219          if ($length_unit->{$unit} and $value >= 0) {
5220            $prop_value{'border-left-width'} = ['DIMENSION', $value, $unit];
5221          } else {
5222            $onerror->(type => "syntax error:'$prop_name'",
5223                       level => $self->{must_level},
5224                       uri => \$self->{href},
5225                       token => $t);
5226            return ($t, undef);
5227          }
5228        } elsif ($t->{type} == NUMBER_TOKEN and
5229                 ($self->{unitless_px} or $t->{number} == 0)) {
5230          my $value = $t->{number} * $sign;
5231          $t = $tt->get_next_token;
5232          $prop_value{'border-left-width'} = ['DIMENSION', $value, 'px'];
5233          unless ($value >= 0) {
5234            $onerror->(type => "syntax error:'$prop_name'",
5235                       level => $self->{must_level},
5236                       uri => \$self->{href},
5237                       token => $t);
5238            return ($t, undef);
5239          }
5240        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
5241          my $prop_value = lc $t->{value}; ## TODO: case
5242          if ({thin => 1, medium => 1, thick => 1}->{$prop_value}) {
5243            $prop_value{'border-left-width'} = ['KEYWORD', $prop_value];
5244            $t = $tt->get_next_token;
5245          }
5246        } else {
5247          if ($sign < 0) {
5248            $onerror->(type => "syntax error:'$prop_name'",
5249                       level => $self->{must_level},
5250                       uri => \$self->{href},
5251                       token => $t);
5252            return ($t, undef);
5253          }
5254          return ($t, \%prop_value);
5255        }
5256    
5257        return ($t, \%prop_value);
5258      },
5259      serialize => sub {
5260        my ($self, $prop_name, $value) = @_;
5261        
5262        local $Error::Depth = $Error::Depth + 1;
5263        my @v;
5264        push @v, $self->border_top_width;
5265        return '' unless length $v[-1];
5266        push @v, $self->border_right_width;
5267        return '' unless length $v[-1];
5268        push @v, $self->border_bottom_width;
5269        return '' unless length $v[-1];
5270        push @v, $self->border_left_width;
5271        return '' unless length $v[-1];
5272    
5273        pop @v if $v[1] eq $v[3];
5274        pop @v if $v[0] eq $v[2];
5275        pop @v if $v[0] eq $v[1];
5276        return join ' ', @v;
5277      },
5278      serialize_multiple => $Prop->{'border-top-color'}->{serialize_multiple},
5279    };
5280    $Attr->{border_width} = $Prop->{'border-width'};
5281    
5282  $Prop->{'list-style'} = {  $Prop->{'list-style'} = {
5283    css => 'list-style',    css => 'list-style',
5284    dom => 'list_style',    dom => 'list_style',
# Line 1416  $Prop->{'list-style'} = { Line 5297  $Prop->{'list-style'} = {
5297            $none++;            $none++;
5298          } elsif ($Prop->{'list-style-type'}->{keyword}->{$prop_value}) {          } elsif ($Prop->{'list-style-type'}->{keyword}->{$prop_value}) {
5299            if (exists $prop_value{'list-style-type'}) {            if (exists $prop_value{'list-style-type'}) {
5300              $onerror->(type => q[syntax error:duplicate:'list-style-type':].              $onerror->(type => "duplication:'list-style-type'",
                            $prop_name,  
5301                         level => $self->{must_level},                         level => $self->{must_level},
5302                           uri => \$self->{href},
5303                         token => $t);                         token => $t);
5304              return ($t, undef);              return ($t, undef);
5305            } else {            } else {
# Line 1426  $Prop->{'list-style'} = { Line 5307  $Prop->{'list-style'} = {
5307            }            }
5308          } elsif ($Prop->{'list-style-position'}->{keyword}->{$prop_value}) {          } elsif ($Prop->{'list-style-position'}->{keyword}->{$prop_value}) {
5309            if (exists $prop_value{'list-style-position'}) {            if (exists $prop_value{'list-style-position'}) {
5310              $onerror->(type => q[syntax error:duplicate:'list-style-position':].              $onerror->(type => "duplication:'list-style-position'",
                            $prop_name,  
5311                         level => $self->{must_level},                         level => $self->{must_level},
5312                           uri => \$self->{href},
5313                         token => $t);                         token => $t);
5314              return ($t, undef);              return ($t, undef);
5315            }            }
# Line 1441  $Prop->{'list-style'} = { Line 5322  $Prop->{'list-style'} = {
5322            last F;            last F;
5323          } else {          } else {
5324            if ($f == 1) {            if ($f == 1) {
5325              $onerror->(type => 'syntax error:'.$prop_name,              $onerror->(type => "syntax error:'$prop_name'",
5326                         level => $self->{must_level},                         level => $self->{must_level},
5327                           uri => \$self->{href},
5328                         token => $t);                         token => $t);
5329              return ($t, undef);              return ($t, undef);
5330            } else {            } else {
# Line 1451  $Prop->{'list-style'} = { Line 5333  $Prop->{'list-style'} = {
5333          }          }
5334        } elsif ($t->{type} == URI_TOKEN) {        } elsif ($t->{type} == URI_TOKEN) {
5335          if (exists $prop_value{'list-style-image'}) {          if (exists $prop_value{'list-style-image'}) {
5336            $onerror->(type => q[syntax error:duplicate:'list-style-image':].            $onerror->(type => "duplication:'list-style-image'",
5337                           $prop_name,                       uri => \$self->{href},
5338                       level => $self->{must_level},                       level => $self->{must_level},
5339                       token => $t);                       token => $t);
5340            return ($t, undef);            return ($t, undef);
# Line 1463  $Prop->{'list-style'} = { Line 5345  $Prop->{'list-style'} = {
5345          $t = $tt->get_next_token;          $t = $tt->get_next_token;
5346        } else {        } else {
5347          if ($f == 1) {          if ($f == 1) {
5348            $onerror->(type => 'syntax error:keyword:'.$prop_name,            $onerror->(type => "syntax error:'$prop_name'",
5349                       level => $self->{must_level},                       level => $self->{must_level},
5350                         uri => \$self->{href},
5351                       token => $t);                       token => $t);
5352            return ($t, undef);            return ($t, undef);
5353          } else {          } else {
# Line 1479  $Prop->{'list-style'} = { Line 5362  $Prop->{'list-style'} = {
5362      if ($none == 1) {      if ($none == 1) {
5363        if (exists $prop_value{'list-style-type'}) {        if (exists $prop_value{'list-style-type'}) {
5364          if (exists $prop_value{'list-style-image'}) {          if (exists $prop_value{'list-style-image'}) {
5365            $onerror->(type => q[syntax error:duplicate:'list-style-image':].            $onerror->(type => "duplication:'list-style-image'",
5366                           $prop_name,                       uri => \$self->{href},
5367                       level => $self->{must_level},                       level => $self->{must_level},
5368                       token => $t);                       token => $t);
5369            return ($t, undef);            return ($t, undef);
# Line 1494  $Prop->{'list-style'} = { Line 5377  $Prop->{'list-style'} = {
5377        }        }
5378      } elsif ($none == 2) {      } elsif ($none == 2) {
5379        if (exists $prop_value{'list-style-type'}) {        if (exists $prop_value{'list-style-type'}) {
5380          $onerror->(type => q[syntax error:duplicate:'list-style-type':].          $onerror->(type => "duplication:'list-style-type'",
5381                         $prop_name,                     uri => \$self->{href},
5382                     level => $self->{must_level},                     level => $self->{must_level},
5383                     token => $t);                     token => $t);
5384          return ($t, undef);          return ($t, undef);
5385        }        }
5386        if (exists $prop_value{'list-style-image'}) {        if (exists $prop_value{'list-style-image'}) {
5387          $onerror->(type => q[syntax error:duplicate:'list-style-image':].          $onerror->(type => "duplication:'list-style-image'",
5388                         $prop_name,                     uri => \$self->{href},
5389                     level => $self->{must_level},                     level => $self->{must_level},
5390                     token => $t);                     token => $t);
5391          return ($t, undef);          return ($t, undef);
# Line 1511  $Prop->{'list-style'} = { Line 5394  $Prop->{'list-style'} = {
5394        $prop_value{'list-style-type'} = ['KEYWORD', 'none'];        $prop_value{'list-style-type'} = ['KEYWORD', 'none'];
5395        $prop_value{'list-style-image'} = ['KEYWORD', 'none'];        $prop_value{'list-style-image'} = ['KEYWORD', 'none'];
5396      } elsif ($none == 3) {      } elsif ($none == 3) {
5397        $onerror->(type => q[syntax error:duplicate:'list-style-type':].        $onerror->(type => "duplication:'list-style-type'",
5398                       $prop_name,                   uri => \$self->{href},
5399                   level => $self->{must_level},                   level => $self->{must_level},
5400                   token => $t);                   token => $t);
5401        return ($t, undef);        return ($t, undef);
# Line 1534  $Prop->{'list-style'} = { Line 5417  $Prop->{'list-style'} = {
5417  };  };
5418  $Attr->{list_style} = $Prop->{'list-style'};  $Attr->{list_style} = $Prop->{'list-style'};
5419    
5420    ## NOTE: Future version of the implementation will change the way to
5421    ## store the parsed value to support CSS 3 properties.
5422    $Prop->{'text-decoration'} = {
5423      css => 'text-decoration',
5424      dom => 'text_decoration',
5425      key => 'text_decoration',
5426      parse => sub {
5427        my ($self, $prop_name, $tt, $t, $onerror) = @_;
5428    
5429        my $value = ['DECORATION']; # , underline, overline, line-through, blink
5430    
5431        if ($t->{type} == IDENT_TOKEN) {
5432          my $v = lc $t->{value}; ## TODO: case
5433          $t = $tt->get_next_token;
5434          if ($v eq 'inherit') {
5435            return ($t, {$prop_name => ['INHERIT']});
5436          } elsif ($v eq 'none') {
5437            return ($t, {$prop_name => $value});
5438          } elsif ($v eq 'underline' and
5439                   $self->{prop_value}->{$prop_name}->{$v}) {
5440            $value->[1] = 1;
5441          } elsif ($v eq 'overline' and
5442                   $self->{prop_value}->{$prop_name}->{$v}) {
5443            $value->[2] = 1;
5444          } elsif ($v eq 'line-through' and
5445                   $self->{prop_value}->{$prop_name}->{$v}) {
5446            $value->[3] = 1;
5447          } elsif ($v eq 'blink' and
5448                   $self->{prop_value}->{$prop_name}->{$v}) {
5449            $value->[4] = 1;
5450          } else {
5451            $onerror->(type => "syntax error:'$prop_name'",
5452                       level => $self->{must_level},
5453                       uri => \$self->{href},
5454                       token => $t);
5455            return ($t, undef);
5456          }
5457        }
5458    
5459        F: {
5460          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
5461          last F unless $t->{type} == IDENT_TOKEN;
5462    
5463          my $v = lc $t->{value}; ## TODO: case
5464          $t = $tt->get_next_token;
5465          if ($v eq 'underline' and
5466              $self->{prop_value}->{$prop_name}->{$v}) {
5467            $value->[1] = 1;
5468          } elsif ($v eq 'overline' and
5469                   $self->{prop_value}->{$prop_name}->{$v}) {
5470            $value->[1] = 2;
5471          } elsif ($v eq 'line-through' and
5472                   $self->{prop_value}->{$prop_name}->{$v}) {
5473            $value->[1] = 3;
5474          } elsif ($v eq 'blink' and
5475                   $self->{prop_value}->{$prop_name}->{$v}) {
5476            $value->[1] = 4;
5477          } else {
5478            last F;
5479          }
5480    
5481          redo F;
5482        } # F
5483    
5484        return ($t, {$prop_name => $value});
5485      },
5486      serialize => $default_serializer,
5487      initial => ["KEYWORD", "none"],
5488      #inherited => 0,
5489      compute => $compute_as_specified,
5490    };
5491    $Attr->{text_decoration} = $Prop->{'text-decoration'};
5492    $Key->{text_decoration} = $Prop->{'text-decoration'};
5493    
5494  1;  1;
5495  ## $Date$  ## $Date$

Legend:
Removed from v.1.15  
changed lines
  Added in v.1.42

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24