/[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.7 by wakaba, Mon Dec 31 09:09:23 2007 UTC revision 1.18 by wakaba, Thu Jan 3 08:37:22 2008 UTC
# Line 7  sub new ($) { Line 7  sub new ($) {
7    my $self = bless {onerror => sub { }, must_level => 'm',    my $self = bless {onerror => sub { }, must_level => 'm',
8                      message_level => 'w',                      message_level => 'w',
9                      unsupported_level => 'unsupported'}, shift;                      unsupported_level => 'unsupported'}, shift;
10      # $self->{base_uri}
11      # $self->{unitless_px} = 1/0
12    
13    return $self;    return $self;
14  } # new  } # new
# Line 75  sub parse_char_string ($$) { Line 77  sub parse_char_string ($$) {
77    require Message::DOM::CSSRule;    require Message::DOM::CSSRule;
78    require Message::DOM::CSSStyleDeclaration;    require Message::DOM::CSSStyleDeclaration;
79    
80      $self->{base_uri} = $self->{href} unless defined $self->{base_uri};
81    
82    my $state = BEFORE_STATEMENT_STATE;    my $state = BEFORE_STATEMENT_STATE;
83    my $t = $tt->get_next_token;    my $t = $tt->get_next_token;
84    
# Line 122  sub parse_char_string ($$) { Line 126  sub parse_char_string ($$) {
126                  push @$current_rules,                  push @$current_rules,
127                      Message::DOM::CSSNamespaceRule->____new ($prefix, $uri);                      Message::DOM::CSSNamespaceRule->____new ($prefix, $uri);
128                  undef $charset_allowed;                  undef $charset_allowed;
                 undef $namespace_allowed;  
129                } else {                } else {
130                  $onerror->(type => 'at:namespace:not allowed',                  $onerror->(type => 'at:namespace:not allowed',
131                             level => $self->{must_level},                             level => $self->{must_level},
# Line 421  sub parse_char_string ($$) { Line 424  sub parse_char_string ($$) {
424    } # S    } # S
425    
426    my $ss = Message::DOM::CSSStyleSheet->____new    my $ss = Message::DOM::CSSStyleSheet->____new
427        (css_rules => $open_rules->[0],        (manakai_base_uri => $self->{base_uri},
428           css_rules => $open_rules->[0],
429         ## TODO: href         ## TODO: href
430         ## TODO: owner_node         ## TODO: owner_node
431         ## TODO: media         ## TODO: media
# Line 430  sub parse_char_string ($$) { Line 434  sub parse_char_string ($$) {
434    return $ss;    return $ss;
435  } # parse_char_string  } # parse_char_string
436    
437    my $compute_as_specified = sub ($$$$) {
438      #my ($self, $element, $prop_name, $specified_value) = @_;
439      return $_[3];
440    }; # $compute_as_specified
441    
442    my $default_serializer = sub {
443      my ($self, $prop_name, $value) = @_;
444      if ($value->[0] eq 'NUMBER' or $value->[0] eq 'WEIGHT') {
445        ## TODO: What we currently do for 'font-weight' is different from
446        ## any browser for lighter/bolder cases.  We need to fix this, but
447        ## how?
448        return $value->[1]; ## TODO: big or small number cases?
449      } elsif ($value->[0] eq 'DIMENSION') {
450        return $value->[1] . $value->[2]; ## NOTE: This is what browsers do.
451      } elsif ($value->[0] eq 'KEYWORD') {
452        return $value->[1];
453      } elsif ($value->[0] eq 'URI') {
454        ## NOTE: This is what browsers do.
455        return 'url('.$value->[1].')';
456      } elsif ($value->[0] eq 'INHERIT') {
457        return 'inherit';
458      } elsif ($value->[0] eq 'DECORATION') {
459        my @v = ();
460        push @v, 'underline' if $value->[1];
461        push @v, 'overline' if $value->[2];
462        push @v, 'line-through' if $value->[3];
463        push @v, 'blink' if $value->[4];
464        return 'none' unless @v;
465        return join ' ', @v;
466      } else {
467        return undef;
468      }
469    }; # $default_serializer
470    
471  $Prop->{color} = {  $Prop->{color} = {
472    css => 'color',    css => 'color',
473    dom => 'color',    dom => 'color',
# Line 462  $Prop->{color} = { Line 500  $Prop->{color} = {
500        return undef;        return undef;
501      }      }
502    },    },
503      initial => ["KEYWORD", "-manakai-initial-color"], ## NOTE: UA-dependent in CSS 2.1.
504      inherited => 1,
505      compute => $compute_as_specified,
506  };  };
507  $Attr->{color} = $Prop->{color};  $Attr->{color} = $Prop->{color};
508  $Key->{color} = $Prop->{color};  $Key->{color} = $Prop->{color};
# Line 476  my $one_keyword_parser = sub { Line 517  my $one_keyword_parser = sub {
517          $self->{prop_value}->{$prop_name}->{$prop_value}) {          $self->{prop_value}->{$prop_name}->{$prop_value}) {
518        return ($t, {$prop_name => ["KEYWORD", $prop_value]});        return ($t, {$prop_name => ["KEYWORD", $prop_value]});
519      } elsif ($prop_value eq 'inherit') {      } elsif ($prop_value eq 'inherit') {
520        return ($t, {$prop_name => ["KEYWORD", $prop_value]});        return ($t, {$prop_name => ['INHERIT']});
521      }      }
522    }    }
523        
# Line 486  my $one_keyword_parser = sub { Line 527  my $one_keyword_parser = sub {
527    return ($t, undef);    return ($t, undef);
528  };  };
529    
 my $one_keyword_serializer = sub {  
   my ($self, $prop_name, $value) = @_;  
   if ($value->[0] eq 'KEYWORD') {  
     return $value->[1];  
   } else {  
     return undef;  
   }  
 };  
   
530  $Prop->{display} = {  $Prop->{display} = {
531    css => 'display',    css => 'display',
532    dom => 'display',    dom => 'display',
533    key => 'display',    key => 'display',
534    parse => $one_keyword_parser,    parse => $one_keyword_parser,
535    serialize => $one_keyword_serializer,    serialize => $default_serializer,
536    keyword => {    keyword => {
537      block => 1, inline => 1, 'inline-block' => 1, 'inline-table' => 1,      block => 1, inline => 1, 'inline-block' => 1, 'inline-table' => 1,
538      'list-item' => 1, none => 1,      'list-item' => 1, none => 1,
# Line 508  $Prop->{display} = { Line 540  $Prop->{display} = {
540      'table-column-group' => 1, 'table-header-group' => 1,      'table-column-group' => 1, 'table-header-group' => 1,
541      'table-footer-group' => 1, 'table-row' => 1, 'table-row-group' => 1,      'table-footer-group' => 1, 'table-row' => 1, 'table-row-group' => 1,
542    },    },
543      initial => ["KEYWORD", "inline"],
544      #inherited => 0,
545      compute => sub {
546        my ($self, $element, $prop_name, $specified_value) = @_;
547        ## NOTE: CSS 2.1 Section 9.7.
548    
549        ## WARNING: |compute| for 'float' property invoke this CODE
550        ## in some case.  Careless modification might cause a infinite loop.
551    
552        if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
553          if ($specified_value->[1] eq 'none') {
554            ## Case 1 [CSS 2.1]
555            return $specified_value;
556          } else {
557            my $position = $self->get_computed_value ($element, 'position');
558            if ($position->[0] eq 'KEYWORD' and
559                ($position->[1] eq 'absolute' or
560                 $position->[1] eq 'fixed')) {
561              ## Case 2 [CSS 2.1]
562              #
563            } else {
564              my $float = $self->get_computed_value ($element, 'float');
565              if ($float->[0] eq 'KEYWORD' and $float->[1] ne 'none') {
566                ## Caes 3 [CSS 2.1]
567                #
568              } elsif (not defined $element->manakai_parent_element) {
569                ## Case 4 [CSS 2.1]
570                #
571              } else {
572                ## Case 5 [CSS 2.1]
573                return $specified_value;
574              }
575            }
576            
577            return ["KEYWORD",
578                    {
579                     'inline-table' => 'table',
580                     inline => 'block',
581                     'run-in' => 'block',
582                     'table-row-group' => 'block',
583                     'table-column' => 'block',
584                     'table-column-group' => 'block',
585                     'table-header-group' => 'block',
586                     'table-footer-group' => 'block',
587                     'table-row' => 'block',
588                     'table-cell' => 'block',
589                     'table-caption' => 'block',
590                     'inline-block' => 'block',
591                    }->{$specified_value->[1]} || $specified_value->[1]];
592          }
593        } else {
594          return $specified_value; ## Maybe an error of the implementation.
595        }
596      },
597  };  };
598  $Attr->{display} = $Prop->{display};  $Attr->{display} = $Prop->{display};
599  $Key->{display} = $Prop->{display};  $Key->{display} = $Prop->{display};
# Line 517  $Prop->{position} = { Line 603  $Prop->{position} = {
603    dom => 'position',    dom => 'position',
604    key => 'position',    key => 'position',
605    parse => $one_keyword_parser,    parse => $one_keyword_parser,
606    serialize => $one_keyword_serializer,    serialize => $default_serializer,
607    keyword => {    keyword => {
608      static => 1, relative => 1, absolute => 1, fixed => 1,      static => 1, relative => 1, absolute => 1, fixed => 1,
609    },    },
610      initial => ["KEYWORD", "static"],
611      #inherited => 0,
612      compute => $compute_as_specified,
613  };  };
614  $Attr->{position} = $Prop->{position};  $Attr->{position} = $Prop->{position};
615  $Key->{position} = $Prop->{position};  $Key->{position} = $Prop->{position};
# Line 530  $Prop->{float} = { Line 619  $Prop->{float} = {
619    dom => 'css_float',    dom => 'css_float',
620    key => 'float',    key => 'float',
621    parse => $one_keyword_parser,    parse => $one_keyword_parser,
622    serialize => $one_keyword_serializer,    serialize => $default_serializer,
623    keyword => {    keyword => {
624      left => 1, right => 1, none => 1,      left => 1, right => 1, none => 1,
625    },    },
626      initial => ["KEYWORD", "none"],
627      #inherited => 0,
628      compute => sub {
629        my ($self, $element, $prop_name, $specified_value) = @_;
630        ## NOTE: CSS 2.1 Section 9.7.
631    
632        ## WARNING: |compute| for 'display' property invoke this CODE
633        ## in some case.  Careless modification might cause a infinite loop.
634        
635        if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
636          if ($specified_value->[1] eq 'none') {
637            ## Case 1 [CSS 2.1]
638            return $specified_value;
639          } else {
640            my $position = $self->get_computed_value ($element, 'position');
641            if ($position->[0] eq 'KEYWORD' and
642                ($position->[1] eq 'absolute' or
643                 $position->[1] eq 'fixed')) {
644              ## Case 2 [CSS 2.1]
645              return ["KEYWORD", "none"];
646            }
647          }
648        }
649    
650        ## ISSUE: CSS 2.1 section 9.7 and 9.5.1 ('float' definition) disagree
651        ## on computed value of 'float' property.
652        
653        ## Case 3, 4, and 5 [CSS 2.1]
654        return $specified_value;
655      },
656  };  };
657  $Attr->{css_float} = $Prop->{float};  $Attr->{css_float} = $Prop->{float};
658  $Attr->{style_float} = $Prop->{float}; ## NOTE: IEism  $Attr->{style_float} = $Prop->{float}; ## NOTE: IEism
# Line 544  $Prop->{clear} = { Line 663  $Prop->{clear} = {
663    dom => 'clear',    dom => 'clear',
664    key => 'clear',    key => 'clear',
665    parse => $one_keyword_parser,    parse => $one_keyword_parser,
666    serialize => $one_keyword_serializer,    serialize => $default_serializer,
667    keyword => {    keyword => {
668      left => 1, right => 1, none => 1, both => 1,      left => 1, right => 1, none => 1, both => 1,
669    },    },
670      initial => ["KEYWORD", "none"],
671      #inherited => 0,
672      compute => $compute_as_specified,
673  };  };
674  $Attr->{clear} = $Prop->{clear};  $Attr->{clear} = $Prop->{clear};
675  $Key->{clear} = $Prop->{clear};  $Key->{clear} = $Prop->{clear};
# Line 557  $Prop->{direction} = { Line 679  $Prop->{direction} = {
679    dom => 'direction',    dom => 'direction',
680    key => 'direction',    key => 'direction',
681    parse => $one_keyword_parser,    parse => $one_keyword_parser,
682    serialize => $one_keyword_serializer,    serialize => $default_serializer,
683    keyword => {    keyword => {
684      ltr => 1, rtl => 1,      ltr => 1, rtl => 1,
685    },    },
686      initial => ["KEYWORD", "ltr"],
687      inherited => 1,
688      compute => $compute_as_specified,
689  };  };
690  $Attr->{direction} = $Prop->{direction};  $Attr->{direction} = $Prop->{direction};
691  $Key->{direction} = $Prop->{direction};  $Key->{direction} = $Prop->{direction};
# Line 570  $Prop->{'unicode-bidi'} = { Line 695  $Prop->{'unicode-bidi'} = {
695    dom => 'unicode_bidi',    dom => 'unicode_bidi',
696    key => 'unicode_bidi',    key => 'unicode_bidi',
697    parse => $one_keyword_parser,    parse => $one_keyword_parser,
698    serialize => $one_keyword_serializer,    serialize => $default_serializer,
699    keyword => {    keyword => {
700      normal => 1, embed => 1, 'bidi-override' => 1,      normal => 1, embed => 1, 'bidi-override' => 1,
701    },    },
702      initial => ["KEYWORD", "normal"],
703      #inherited => 0,
704      compute => $compute_as_specified,
705  };  };
706  $Attr->{unicode_bidi} = $Prop->{'unicode-bidi'};  $Attr->{unicode_bidi} = $Prop->{'unicode-bidi'};
707  $Key->{unicode_bidi} = $Prop->{'unicode-bidi'};  $Key->{unicode_bidi} = $Prop->{'unicode-bidi'};
708    
709    $Prop->{overflow} = {
710      css => 'overflow',
711      dom => 'overflow',
712      key => 'overflow',
713      parse => $one_keyword_parser,
714      serialize => $default_serializer,
715      keyword => {
716        visible => 1, hidden => 1, scroll => 1, auto => 1,
717      },
718      initial => ["KEYWORD", "visible"],
719      #inherited => 0,
720      compute => $compute_as_specified,
721    };
722    $Attr->{overflow} = $Prop->{overflow};
723    $Key->{overflow} = $Prop->{overflow};
724    
725    $Prop->{visibility} = {
726      css => 'visibility',
727      dom => 'visibility',
728      key => 'visibility',
729      parse => $one_keyword_parser,
730      serialize => $default_serializer,
731      keyword => {
732        visible => 1, hidden => 1, collapse => 1,
733      },
734      initial => ["KEYWORD", "visible"],
735      #inherited => 0,
736      compute => $compute_as_specified,
737    };
738    $Attr->{visibility} = $Prop->{visibility};
739    $Key->{visibility} = $Prop->{visibility};
740    
741    $Prop->{'list-style-type'} = {
742      css => 'list-style-type',
743      dom => 'list_style_type',
744      key => 'list_style_type',
745      parse => $one_keyword_parser,
746      serialize => $default_serializer,
747      keyword => {
748        qw/
749          disc 1 circle 1 square 1 decimal 1 decimal-leading-zero 1
750          lower-roman 1 upper-roman 1 lower-greek 1 lower-latin 1
751          upper-latin 1 armenian 1 georgian 1 lower-alpha 1 upper-alpha 1
752          none 1
753        /,
754      },
755      initial => ["KEYWORD", 'disc'],
756      inherited => 1,
757      compute => $compute_as_specified,
758    };
759    $Attr->{list_style_type} = $Prop->{'list-style-type'};
760    $Key->{list_style_type} = $Prop->{'list-style-type'};
761    
762    $Prop->{'list-style-position'} = {
763      css => 'list-style-position',
764      dom => 'list_style_position',
765      key => 'list_style_position',
766      parse => $one_keyword_parser,
767      serialize => $default_serializer,
768      keyword => {
769        inside => 1, outside => 1,
770      },
771      initial => ["KEYWORD", 'outside'],
772      inherited => 1,
773      compute => $compute_as_specified,
774    };
775    $Attr->{list_style_position} = $Prop->{'list-style-position'};
776    $Key->{list_style_position} = $Prop->{'list-style-position'};
777    
778    $Prop->{'page-break-before'} = {
779      css => 'page-break-before',
780      dom => 'page_break_before',
781      key => 'page_break_before',
782      parse => $one_keyword_parser,
783      serialize => $default_serializer,
784      keyword => {
785        auto => 1, always => 1, avoid => 1, left => 1, right => 1,
786      },
787      initial => ["KEYWORD", 'auto'],
788      #inherited => 0,
789      compute => $compute_as_specified,
790    };
791    $Attr->{page_break_before} = $Prop->{'page-break-before'};
792    $Key->{page_break_before} = $Prop->{'page-break-before'};
793    
794    $Prop->{'page-break-after'} = {
795      css => 'page-break-after',
796      dom => 'page_break_after',
797      key => 'page_break_after',
798      parse => $one_keyword_parser,
799      serialize => $default_serializer,
800      keyword => {
801        auto => 1, always => 1, avoid => 1, left => 1, right => 1,
802      },
803      initial => ["KEYWORD", 'auto'],
804      #inherited => 0,
805      compute => $compute_as_specified,
806    };
807    $Attr->{page_break_after} = $Prop->{'page-break-after'};
808    $Key->{page_break_after} = $Prop->{'page-break-after'};
809    
810    $Prop->{'page-break-inside'} = {
811      css => 'page-break-inside',
812      dom => 'page_break_inside',
813      key => 'page_break_inside',
814      parse => $one_keyword_parser,
815      serialize => $default_serializer,
816      keyword => {
817        auto => 1, avoid => 1,
818      },
819      initial => ["KEYWORD", 'auto'],
820      inherited => 1,
821      compute => $compute_as_specified,
822    };
823    $Attr->{page_break_inside} = $Prop->{'page-break-inside'};
824    $Key->{page_break_inside} = $Prop->{'page-break-inside'};
825    
826    $Prop->{'background-repeat'} = {
827      css => 'background-repeat',
828      dom => 'background_repeat',
829      key => 'background_repeat',
830      parse => $one_keyword_parser,
831      serialize => $default_serializer,
832      keyword => {
833        repeat => 1, 'repeat-x' => 1, 'repeat-y' => 1, 'no-repeat' => 1,
834      },
835      initial => ["KEYWORD", 'repeat'],
836      #inherited => 0,
837      compute => $compute_as_specified,
838    };
839    $Attr->{background_repeat} = $Prop->{'background-repeat'};
840    $Key->{backgroud_repeat} = $Prop->{'background-repeat'};
841    
842    $Prop->{'background-attachment'} = {
843      css => 'background-attachment',
844      dom => 'background_attachment',
845      key => 'background_attachment',
846      parse => $one_keyword_parser,
847      serialize => $default_serializer,
848      keyword => {
849        scroll => 1, fixed => 1,
850      },
851      initial => ["KEYWORD", 'scroll'],
852      #inherited => 0,
853      compute => $compute_as_specified,
854    };
855    $Attr->{background_attachment} = $Prop->{'background-attachment'};
856    $Key->{backgroud_attachment} = $Prop->{'background-attachment'};
857    
858    $Prop->{'font-style'} = {
859      css => 'font-style',
860      dom => 'font_style',
861      key => 'font_style',
862      parse => $one_keyword_parser,
863      serialize => $default_serializer,
864      keyword => {
865        normal => 1, italic => 1, oblique => 1,
866      },
867      initial => ["KEYWORD", 'normal'],
868      inherited => 1,
869      compute => $compute_as_specified,
870    };
871    $Attr->{font_style} = $Prop->{'font-style'};
872    $Key->{font_style} = $Prop->{'font-style'};
873    
874    $Prop->{'font-variant'} = {
875      css => 'font-variant',
876      dom => 'font_variant',
877      key => 'font_variant',
878      parse => $one_keyword_parser,
879      serialize => $default_serializer,
880      keyword => {
881        normal => 1, 'small-caps' => 1,
882      },
883      initial => ["KEYWORD", 'normal'],
884      inherited => 1,
885      compute => $compute_as_specified,
886    };
887    $Attr->{font_variant} = $Prop->{'font-variant'};
888    $Key->{font_variant} = $Prop->{'font-variant'};
889    
890    $Prop->{'text-align'} = {
891      css => 'text-align',
892      dom => 'text_align',
893      key => 'text_align',
894      parse => $one_keyword_parser,
895      serialize => $default_serializer,
896      keyword => {
897        left => 1, right => 1, center => 1, justify => 1, ## CSS 2
898        begin => 1, end => 1, ## CSS 3
899      },
900      initial => ["KEYWORD", 'begin'],
901      inherited => 1,
902      compute => $compute_as_specified,
903    };
904    $Attr->{text_align} = $Prop->{'text-align'};
905    $Key->{text_align} = $Prop->{'text-align'};
906    
907    $Prop->{'text-transform'} = {
908      css => 'text-transform',
909      dom => 'text_transform',
910      key => 'text_transform',
911      parse => $one_keyword_parser,
912      serialize => $default_serializer,
913      keyword => {
914        capitalize => 1, uppercase => 1, lowercase => 1, none => 1,
915      },
916      initial => ["KEYWORD", 'none'],
917      inherited => 1,
918      compute => $compute_as_specified,
919    };
920    $Attr->{text_transform} = $Prop->{'text-transform'};
921    $Key->{text_transform} = $Prop->{'text-transform'};
922    
923    $Prop->{'white-space'} = {
924      css => 'white-space',
925      dom => 'white_space',
926      key => 'white_space',
927      parse => $one_keyword_parser,
928      serialize => $default_serializer,
929      keyword => {
930        normal => 1, pre => 1, nowrap => 1, 'pre-wrap' => 1, 'pre-line' => 1,
931      },
932      initial => ["KEYWORD", 'normal'],
933      inherited => 1,
934      compute => $compute_as_specified,
935    };
936    $Attr->{white_space} = $Prop->{'white-space'};
937    $Key->{white_space} = $Prop->{'white-space'};
938    
939    $Prop->{'caption-side'} = {
940      css => 'caption-side',
941      dom => 'caption_side',
942      key => 'caption_side',
943      parse => $one_keyword_parser,
944      serialize => $default_serializer,
945      keyword => {
946        top => 1, bottom => 1,
947      },
948      initial => ['KEYWORD', 'top'],
949      inherited => 1,
950      compute => $compute_as_specified,
951    };
952    $Attr->{caption_side} = $Prop->{'caption-side'};
953    $Key->{caption_side} = $Prop->{'caption-side'};
954    
955    $Prop->{'table-layout'} = {
956      css => 'table-layout',
957      dom => 'table_layout',
958      key => 'table_layout',
959      parse => $one_keyword_parser,
960      serialize => $default_serializer,
961      keyword => {
962        auto => 1, fixed => 1,
963      },
964      initial => ['KEYWORD', 'auto'],
965      #inherited => 0,
966      compute => $compute_as_specified,
967    };
968    $Attr->{table_layout} = $Prop->{'table-layout'};
969    $Key->{table_layout} = $Prop->{'table-layout'};
970    
971    $Prop->{'border-collapse'} = {
972      css => 'border-collapse',
973      dom => 'border_collapse',
974      key => 'border_collapse',
975      parse => $one_keyword_parser,
976      serialize => $default_serializer,
977      keyword => {
978        collapse => 1, separate => 1,
979      },
980      initial => ['KEYWORD', 'separate'],
981      inherited => 1,
982      compute => $compute_as_specified,
983    };
984    $Attr->{border_collapse} = $Prop->{'border-collapse'};
985    $Key->{border_collapse} = $Prop->{'border-collapse'};
986    
987    $Prop->{'empty-cells'} = {
988      css => 'empty-cells',
989      dom => 'empty_cells',
990      key => 'empty_cells',
991      parse => $one_keyword_parser,
992      serialize => $default_serializer,
993      keyword => {
994        show => 1, hide => 1,
995      },
996      initial => ['KEYWORD', 'show'],
997      inherited => 1,
998      compute => $compute_as_specified,
999    };
1000    $Attr->{empty_cells} = $Prop->{'empty-cells'};
1001    $Key->{empty_cells} = $Prop->{'empty-cells'};
1002    
1003    $Prop->{'z-index'} = {
1004      css => 'z-index',
1005      dom => 'z_index',
1006      key => 'z_index',
1007      parse => sub {
1008        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1009    
1010        my $sign = 1;
1011        if ($t->{type} == MINUS_TOKEN) {
1012          $sign = -1;
1013          $t = $tt->get_next_token;
1014        }
1015    
1016        if ($t->{type} == NUMBER_TOKEN) {
1017          ## ISSUE: See <http://suika.fam.cx/gate/2005/sw/z-index> for
1018          ## browser compatibility issue.
1019          my $value = $t->{number};
1020          $t = $tt->get_next_token;
1021          return ($t, {$prop_name => ["NUMBER", $sign * int ($value / 1)]});
1022        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
1023          my $value = lc $t->{value}; ## TODO: case
1024          $t = $tt->get_next_token;
1025          if ($value eq 'auto') {
1026            ## NOTE: |z-index| is the default value and therefore it must be
1027            ## supported anyway.
1028            return ($t, {$prop_name => ["KEYWORD", 'auto']});
1029          } elsif ($value eq 'inherit') {
1030            return ($t, {$prop_name => ['INHERIT']});
1031          }
1032        }
1033        
1034        $onerror->(type => 'syntax error:'.$prop_name,
1035                   level => $self->{must_level},
1036                   token => $t);
1037        return ($t, undef);
1038      },
1039      serialize => $default_serializer,
1040      initial => ['KEYWORD', 'auto'],
1041      #inherited => 0,
1042      compute => $compute_as_specified,
1043    };
1044    $Attr->{z_index} = $Prop->{'z-index'};
1045    $Key->{z_index} = $Prop->{'z-index'};
1046    
1047    $Prop->{orphans} = {
1048      css => 'orphans',
1049      dom => 'orphans',
1050      key => 'orphans',
1051      parse => sub {
1052        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1053    
1054        my $sign = 1;
1055        if ($t->{type} == MINUS_TOKEN) {
1056          $t = $tt->get_next_token;
1057          $sign = -1;
1058        }
1059    
1060        if ($t->{type} == NUMBER_TOKEN) {
1061          ## ISSUE: See <http://suika.fam.cx/gate/2005/sw/orphans> and
1062          ## <http://suika.fam.cx/gate/2005/sw/widows> for
1063          ## browser compatibility issue.
1064          my $value = $t->{number};
1065          $t = $tt->get_next_token;
1066          return ($t, {$prop_name => ["NUMBER", $sign * int ($value / 1)]});
1067        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
1068          my $value = lc $t->{value}; ## TODO: case
1069          $t = $tt->get_next_token;
1070          if ($value eq 'inherit') {
1071            return ($t, {$prop_name => ['INHERIT']});
1072          }
1073        }
1074        
1075        $onerror->(type => 'syntax error:'.$prop_name,
1076                   level => $self->{must_level},
1077                   token => $t);
1078        return ($t, undef);
1079      },
1080      serialize => $default_serializer,
1081      initial => ['NUMBER', 2],
1082      inherited => 1,
1083      compute => $compute_as_specified,
1084    };
1085    $Attr->{orphans} = $Prop->{orphans};
1086    $Key->{orphans} = $Prop->{orphans};
1087    
1088    $Prop->{widows} = {
1089      css => 'widows',
1090      dom => 'widows',
1091      key => 'widows',
1092      parse => $Prop->{orphans}->{parse},
1093      serialize => $default_serializer,
1094      initial => ['NUMBER', 2],
1095      inherited => 1,
1096      compute => $compute_as_specified,
1097    };
1098    $Attr->{widows} = $Prop->{widows};
1099    $Key->{widows} = $Prop->{widows};
1100    
1101    $Prop->{'font-size'} = {
1102      css => 'font-size',
1103      dom => 'font_size',
1104      key => 'font_size',
1105      parse => sub {
1106        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1107    
1108        my $sign = 1;
1109        if ($t->{type} == MINUS_TOKEN) {
1110          $t = $tt->get_next_token;
1111          $sign = -1;
1112        }
1113    
1114        if ($t->{type} == DIMENSION_TOKEN) {
1115          my $value = $t->{number} * $sign;
1116          my $unit = lc $t->{value}; ## TODO: case
1117          $t = $tt->get_next_token;
1118          if ({in => 1, cm => 1, mm => 1, pt => 1, pc => 1,
1119               em => 1, ex => 1, pt => 1}->{$unit} and $value >= 0) {
1120            return ($t, {$prop_name => ['DIMENSION', $value, $unit]});
1121          }
1122        } elsif ($t->{type} == PERCENTAGE_TOKEN) {
1123          my $value = $t->{number} * $sign;
1124          $t = $tt->get_next_token;
1125          return ($t, {$prop_name => ['PERCENTAGE', $value]}) if $value >= 0;
1126        } elsif ($self->{unitless_px} and $t->{type} == NUMBER_TOKEN) {
1127          my $value = $t->{number} * $sign;
1128          $t = $tt->get_next_token;
1129          return ($t, {$prop_name => ['DIMENSION', $value, 'px']}) if $value >= 0;
1130        } elsif ($sign > 0 and $t->{type} == IDENT_TOKEN) {
1131          my $value = lc $t->{value}; ## TODO: case
1132          $t = $tt->get_next_token;
1133          if ({
1134               'xx-small' => 1, 'x-small' => 1, small => 1, medium => 1,
1135               large => 1, 'x-large' => 1, 'xx-large' => 1,
1136               '-manakai-xxx-large' => 1, # -webkit-xxx-large
1137               larger => 1, smaller => 1,
1138              }->{$value}) {
1139            return ($t, {$prop_name => ['KEYWORD', $value]});        
1140          } elsif ($value eq 'inherit') {
1141            return ($t, {$prop_name => ['INHERIT']});
1142          }
1143        }
1144        
1145        $onerror->(type => 'syntax error:'.$prop_name,
1146                   level => $self->{must_level},
1147                   token => $t);
1148        return ($t, undef);
1149      },
1150      serialize => $default_serializer,
1151      initial => ['KEYWORD', 'medium'],
1152      inherited => 1,
1153      compute => sub {
1154        my ($self, $element, $prop_name, $specified_value) = @_;
1155        
1156        if (defined $specified_value) {
1157          if ($specified_value->[0] eq 'DIMENSION') {
1158            my $unit = $specified_value->[2];
1159            my $value = $specified_value->[1];
1160    
1161            if ($unit eq 'em' or $unit eq 'ex') {
1162              $value *= 0.5 if $unit eq 'ex';
1163              ## TODO: Preferred way to determine the |ex| size is defined
1164              ## in CSS 2.1.
1165    
1166              my $parent_element = $element->manakai_parent_element;
1167              if (defined $parent_element) {
1168                $value *= $self->get_computed_value ($parent_element, $prop_name)
1169                    ->[1];
1170              } else {
1171                $value *= $self->{font_size}->[3]; # medium
1172              }
1173              $unit = 'px';
1174            } elsif ({in => 1, cm => 1, mm => 1, pt => 1, pc => 1}->{$unit}) {
1175              ($value *= 12, $unit = 'pc') if $unit eq 'pc';
1176              ($value /= 72, $unit = 'in') if $unit eq 'pt';
1177              ($value *= 2.54, $unit = 'cm') if $unit eq 'in';
1178              ($value *= 10, $unit = 'mm') if $unit eq 'cm';
1179              ($value /= 0.26, $unit = 'px') if $unit eq 'mm';
1180            }
1181    
1182            return ['DIMENSION', $value, $unit];
1183          } elsif ($specified_value->[0] eq 'PERCENTAGE') {
1184            my $parent_element = $element->manakai_parent_element;
1185            my $parent_cv;
1186            if (defined $parent_element) {
1187              $parent_cv = $self->get_computed_value
1188                  ($parent_element, $prop_name);
1189            } else {
1190              $parent_cv = [undef, $self->{font_size}->[3]];
1191            }
1192            return ['DIMENSION', $parent_cv->[1] * $specified_value->[1] / 100,
1193                    'px'];
1194          } elsif ($specified_value->[0] eq 'KEYWORD') {
1195            if ($specified_value->[1] eq 'larger') {
1196              my $parent_element = $element->manakai_parent_element;
1197              if (defined $parent_element) {
1198                my $parent_cv = $self->get_computed_value
1199                    ($parent_element, $prop_name);
1200                return ['DIMENSION',
1201                        $self->{get_larger_font_size}->($self, $parent_cv->[1]),
1202                        'px'];
1203              } else { ## 'larger' relative to 'medium', initial of 'font-size'
1204                return ['DIMENSION', $self->{font_size}->[4], 'px'];
1205              }
1206            } elsif ($specified_value->[1] eq 'smaller') {
1207              my $parent_element = $element->manakai_parent_element;
1208              if (defined $parent_element) {
1209                my $parent_cv = $self->get_computed_value
1210                    ($parent_element, $prop_name);
1211                return ['DIMENSION',
1212                        $self->{get_smaller_font_size}->($self, $parent_cv->[1]),
1213                        'px'];
1214              } else { ## 'smaller' relative to 'medium', initial of 'font-size'
1215                return ['DIMENSION', $self->{font_size}->[2], 'px'];
1216              }
1217            } else {
1218              return ['DIMENSION', $self->{font_size}->[{
1219                'xx-small' => 0,
1220                'x-small' => 1,
1221                small => 2,
1222                medium => 3,
1223                large => 4,
1224                'x-large' => 5,
1225                'xx-large' => 6,
1226                '-manakai-xxx-large' => 7,
1227              }->{$specified_value->[1]}], 'px'];
1228            }
1229          }
1230        }
1231        
1232        return $specified_value;
1233      },
1234    };
1235    $Attr->{font_size} = $Prop->{'font-size'};
1236    $Key->{font_size} = $Prop->{'font-size'};
1237    
1238    $Prop->{'font-weight'} = {
1239      css => 'font-weight',
1240      dom => 'font_weight',
1241      key => 'font_weight',
1242      parse => sub {
1243        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1244    
1245        if ($t->{type} == NUMBER_TOKEN) {
1246          ## ISSUE: See <http://suika.fam.cx/gate/2005/sw/font-weight> for
1247          ## browser compatibility issue.
1248          my $value = $t->{number};
1249          $t = $tt->get_next_token;
1250          if ($value % 100 == 0 and 100 <= $value and $value <= 900) {
1251            return ($t, {$prop_name => ['WEIGHT', $value, 0]});
1252          }
1253        } elsif ($t->{type} == IDENT_TOKEN) {
1254          my $value = lc $t->{value}; ## TODO: case
1255          $t = $tt->get_next_token;
1256          if ({
1257               normal => 1, bold => 1, bolder => 1, lighter => 1,
1258              }->{$value}) {
1259            return ($t, {$prop_name => ['KEYWORD', $value]});
1260          } elsif ($value eq 'inherit') {
1261            return ($t, {$prop_name => ['INHERIT']});
1262          }
1263        }
1264        
1265        $onerror->(type => 'syntax error:'.$prop_name,
1266                   level => $self->{must_level},
1267                   token => $t);
1268        return ($t, undef);
1269      },
1270      serialize => $default_serializer,
1271      initial => ['KEYWORD', 'normal'],
1272      inherited => 1,
1273      compute => sub {
1274        my ($self, $element, $prop_name, $specified_value) = @_;
1275    
1276        if (defined $specified_value and $specified_value->[0] eq 'KEYWORD') {
1277          if ($specified_value->[1] eq 'normal') {
1278            return ['WEIGHT', 400, 0];
1279          } elsif ($specified_value->[1] eq 'bold') {
1280            return ['WEIGHT', 700, 0];
1281          } elsif ($specified_value->[1] eq 'bolder') {
1282            my $parent_element = $element->manakai_parent_element;
1283            if (defined $parent_element) {
1284              my $parent_value = $self->get_cascaded_value
1285                  ($parent_element, $prop_name); ## NOTE: What Firefox does.
1286              return ['WEIGHT', $parent_value->[1], $parent_value->[2] + 1];
1287            } else {
1288              return ['WEIGHT', 400, 1];
1289            }
1290          } elsif ($specified_value->[1] eq 'lighter') {
1291            my $parent_element = $element->manakai_parent_element;
1292            if (defined $parent_element) {
1293              my $parent_value = $self->get_cascaded_value
1294                  ($parent_element, $prop_name); ## NOTE: What Firefox does.
1295              return ['WEIGHT', $parent_value->[1], $parent_value->[2] - 1];
1296            } else {
1297              return ['WEIGHT', 400, 1];
1298            }
1299          }
1300        #} elsif (defined $specified_value and $specified_value->[0] eq 'WEIGHT') {
1301          #
1302        }
1303    
1304        return $specified_value;
1305      },
1306    };
1307    $Attr->{font_weight} = $Prop->{'font-weight'};
1308    $Key->{font_weight} = $Prop->{'font-weight'};
1309    
1310    my $uri_or_none_parser = sub {
1311        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1312    
1313        if ($t->{type} == URI_TOKEN) {
1314          my $value = $t->{value};
1315          $t = $tt->get_next_token;
1316          return ($t, {$prop_name => ['URI', $value, \($self->{base_uri})]});
1317        } elsif ($t->{type} == IDENT_TOKEN) {
1318          my $value = lc $t->{value}; ## TODO: case
1319          $t = $tt->get_next_token;
1320          if ($value eq 'none') {
1321            ## NOTE: |none| is the default value and therefore it must be
1322            ## supported anyway.
1323            return ($t, {$prop_name => ["KEYWORD", 'none']});
1324          } elsif ($value eq 'inherit') {
1325            return ($t, {$prop_name => ['INHERIT']});
1326          }
1327        ## NOTE: None of Firefox2, WinIE6, and Opera9 support this case.
1328        #} elsif ($t->{type} == URI_INVALID_TOKEN) {
1329        #  my $value = $t->{value};
1330        #  $t = $tt->get_next_token;
1331        #  if ($t->{type} == EOF_TOKEN) {
1332        #    $onerror->(type => 'syntax error:eof:'.$prop_name,
1333        #               level => $self->{must_level},
1334        #               token => $t);
1335        #    
1336        #    return ($t, {$prop_name => ['URI', $value, \($self->{base_uri})]});
1337        #  }
1338        }
1339        
1340        $onerror->(type => 'syntax error:'.$prop_name,
1341                   level => $self->{must_level},
1342                   token => $t);
1343        return ($t, undef);
1344    }; # $uri_or_none_parser
1345    
1346    my $compute_uri_or_none = sub {
1347        my ($self, $element, $prop_name, $specified_value) = @_;
1348        
1349        if (defined $specified_value and
1350            $specified_value->[0] eq 'URI' and
1351            defined $specified_value->[2]) {
1352          require Message::DOM::DOMImplementation;
1353          return ['URI',
1354                  Message::DOM::DOMImplementation->create_uri_reference
1355                      ($specified_value->[1])
1356                      ->get_absolute_reference (${$specified_value->[2]})
1357                      ->get_uri_reference,
1358                  $specified_value->[2]];
1359        }
1360    
1361        return $specified_value;
1362    }; # $compute_uri_or_none
1363    
1364    $Prop->{'list-style-image'} = {
1365      css => 'list-style-image',
1366      dom => 'list_style_image',
1367      key => 'list_style_image',
1368      parse => $uri_or_none_parser,
1369      serialize => $default_serializer,
1370      initial => ['KEYWORD', 'none'],
1371      inherited => 1,
1372      compute => $compute_uri_or_none,
1373    };
1374    $Attr->{list_style_image} = $Prop->{'list-style-image'};
1375    $Key->{list_style_image} = $Prop->{'list-style-image'};
1376    
1377    $Prop->{'background-image'} = {
1378      css => 'background-image',
1379      dom => 'background_image',
1380      key => 'background_image',
1381      parse => $uri_or_none_parser,
1382      serialize => $default_serializer,
1383      initial => ['KEYWORD', 'none'],
1384      #inherited => 0,
1385      compute => $compute_uri_or_none,
1386    };
1387    $Attr->{background_image} = $Prop->{'background-image'};
1388    $Key->{background_image} = $Prop->{'background-image'};
1389    
1390  my $border_style_keyword = {  my $border_style_keyword = {
1391    none => 1, hidden => 1, dotted => 1, dashed => 1, solid => 1,    none => 1, hidden => 1, dotted => 1, dashed => 1, solid => 1,
1392    double => 1, groove => 1, ridge => 1, inset => 1, outset => 1,    double => 1, groove => 1, ridge => 1, inset => 1, outset => 1,
# Line 588  $Prop->{'border-top-style'} = { Line 1397  $Prop->{'border-top-style'} = {
1397    dom => 'border_top_style',    dom => 'border_top_style',
1398    key => 'border_top_style',    key => 'border_top_style',
1399    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1400    serialize => $one_keyword_serializer,    serialize => $default_serializer,
1401    keyword => $border_style_keyword,    keyword => $border_style_keyword,
1402      initial => ["KEYWORD", "none"],
1403      #inherited => 0,
1404      compute => $compute_as_specified,
1405  };  };
1406  $Attr->{border_top_style} = $Prop->{'border-top-style'};  $Attr->{border_top_style} = $Prop->{'border-top-style'};
1407  $Key->{border_top_style} = $Prop->{'border-top-style'};  $Key->{border_top_style} = $Prop->{'border-top-style'};
# Line 599  $Prop->{'border-right-style'} = { Line 1411  $Prop->{'border-right-style'} = {
1411    dom => 'border_right_style',    dom => 'border_right_style',
1412    key => 'border_right_style',    key => 'border_right_style',
1413    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1414    serialize => $one_keyword_serializer,    serialize => $default_serializer,
1415    keyword => $border_style_keyword,    keyword => $border_style_keyword,
1416      initial => ["KEYWORD", "none"],
1417      #inherited => 0,
1418      compute => $compute_as_specified,
1419  };  };
1420  $Attr->{border_right_style} = $Prop->{'border-right-style'};  $Attr->{border_right_style} = $Prop->{'border-right-style'};
1421  $Key->{border_right_style} = $Prop->{'border-right-style'};  $Key->{border_right_style} = $Prop->{'border-right-style'};
# Line 610  $Prop->{'border-bottom-style'} = { Line 1425  $Prop->{'border-bottom-style'} = {
1425    dom => 'border_bottom_style',    dom => 'border_bottom_style',
1426    key => 'border_bottom_style',    key => 'border_bottom_style',
1427    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1428    serialize => $one_keyword_serializer,    serialize => $default_serializer,
1429    keyword => $border_style_keyword,    keyword => $border_style_keyword,
1430      initial => ["KEYWORD", "none"],
1431      #inherited => 0,
1432      compute => $compute_as_specified,
1433  };  };
1434  $Attr->{border_bottom_style} = $Prop->{'border-bottom-style'};  $Attr->{border_bottom_style} = $Prop->{'border-bottom-style'};
1435  $Key->{border_bottom_style} = $Prop->{'border-bottom-style'};  $Key->{border_bottom_style} = $Prop->{'border-bottom-style'};
# Line 621  $Prop->{'border-left-style'} = { Line 1439  $Prop->{'border-left-style'} = {
1439    dom => 'border_left_style',    dom => 'border_left_style',
1440    key => 'border_left_style',    key => 'border_left_style',
1441    parse => $one_keyword_parser,    parse => $one_keyword_parser,
1442    serialize => $one_keyword_serializer,    serialize => $default_serializer,
1443    keyword => $border_style_keyword,    keyword => $border_style_keyword,
1444      initial => ["KEYWORD", "none"],
1445      #inherited => 0,
1446      compute => $compute_as_specified,
1447  };  };
1448  $Attr->{border_left_style} = $Prop->{'border-left-style'};  $Attr->{border_left_style} = $Prop->{'border-left-style'};
1449  $Key->{border_left_style} = $Prop->{'border-left-style'};  $Key->{border_left_style} = $Prop->{'border-left-style'};
1450    
1451    $Prop->{'outline-style'} = {
1452      css => 'outline-style',
1453      dom => 'outline_style',
1454      key => 'outline_style',
1455      parse => $one_keyword_parser,
1456      serialize => $default_serializer,
1457      keyword => $border_style_keyword,
1458      initial => ['KEYWORD', 'none'],
1459      #inherited => 0,
1460      compute => $compute_as_specified,
1461    };
1462    $Attr->{outline_style} = $Prop->{'outline-style'};
1463    $Key->{outline_style} = $Prop->{'outline-style'};
1464    
1465    $Prop->{'font-family'} = {
1466      css => 'font-family',
1467      dom => 'font_family',
1468      key => 'font_family',
1469      parse => sub {
1470        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1471    
1472        ## NOTE: See <http://suika.fam.cx/gate/2005/sw/font-family> for
1473        ## how chaotic browsers are!
1474    
1475        my @prop_value;
1476    
1477        my $font_name = '';
1478        my $may_be_generic = 1;
1479        my $may_be_inherit = 1;
1480        my $has_s = 0;
1481        F: {
1482          if ($t->{type} == IDENT_TOKEN) {
1483            undef $may_be_inherit if $has_s or length $font_name;
1484            undef $may_be_generic if $has_s or length $font_name;
1485            $font_name .= ' ' if $has_s;
1486            $font_name .= $t->{value};
1487            undef $has_s;
1488            $t = $tt->get_next_token;
1489          } elsif ($t->{type} == STRING_TOKEN) {
1490            $font_name .= ' ' if $has_s;
1491            $font_name .= $t->{value};
1492            undef $may_be_inherit;
1493            undef $may_be_generic;
1494            undef $has_s;
1495            $t = $tt->get_next_token;
1496          } elsif ($t->{type} == COMMA_TOKEN) {
1497            if ($may_be_generic and
1498                {
1499                  serif => 1, 'sans-serif' => 1, cursive => 1,
1500                  fantasy => 1, monospace => 1, '-manakai-default' => 1,
1501                }->{lc $font_name}) { ## TODO: case
1502              push @prop_value, ['KEYWORD', $font_name];
1503            } elsif (not $may_be_generic or length $font_name) {
1504              push @prop_value, ["STRING", $font_name];
1505            }
1506            undef $may_be_inherit;
1507            $may_be_generic = 1;
1508            undef $has_s;
1509            $font_name = '';
1510            $t = $tt->get_next_token;
1511            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
1512          } elsif ($t->{type} == S_TOKEN) {
1513            $has_s = 1;
1514            $t = $tt->get_next_token;
1515          } else {
1516            if ($may_be_generic and
1517                {
1518                  serif => 1, 'sans-serif' => 1, cursive => 1,
1519                  fantasy => 1, monospace => 1, '-manakai-default' => 1,
1520                }->{lc $font_name}) { ## TODO: case
1521              push @prop_value, ['KEYWORD', $font_name];
1522            } elsif (not $may_be_generic or length $font_name) {
1523              push @prop_value, ['STRING', $font_name];
1524            } else {
1525              $onerror->(type => 'syntax error:'.$prop_name,
1526                         level => $self->{must_level},
1527                         token => $t);
1528              return ($t, undef);
1529            }
1530            last F;
1531          }
1532          redo F;
1533        } # F
1534    
1535        if ($may_be_inherit and
1536            @prop_value == 1 and
1537            $prop_value[0]->[0] eq 'STRING' and
1538            lc $prop_value[0]->[1] eq 'inherit') { ## TODO: case
1539          return ($t, {$prop_name => ['INHERIT']});
1540        } else {
1541          unshift @prop_value, 'FONT';
1542          return ($t, {$prop_name => \@prop_value});
1543        }
1544      },
1545      serialize => sub {
1546        my ($self, $prop_name, $value) = @_;
1547    
1548        if ($value->[0] eq 'FONT') {
1549          return join ', ', map {
1550            if ($_->[0] eq 'STRING') {
1551              '"'.$_->[1].'"'; ## NOTE: This is what Firefox does.
1552            } elsif ($_->[0] eq 'KEYWORD') {
1553              $_->[1]; ## NOTE: This is what Firefox does.
1554            } else {
1555              ## NOTE: This should be an error.
1556              '""';
1557            }
1558          } @$value[1..$#$value];
1559        } elsif ($value->[0] eq 'INHERIT') {
1560          return 'inherit';
1561        } else {
1562          return undef;
1563        }
1564      },
1565      initial => ['FONT', ['KEYWORD', '-manakai-default']],
1566      inherited => 1,
1567      compute => $compute_as_specified,
1568    };
1569    $Attr->{font_family} = $Prop->{'font-family'};
1570    $Key->{font_family} = $Prop->{'font-family'};
1571    
1572    $Prop->{cursor} = {
1573      css => 'cursor',
1574      dom => 'cursor',
1575      key => 'cursor',
1576      parse => sub {
1577        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1578    
1579        ## NOTE: See <http://suika.fam.cx/gate/2005/sw/cursor> for browser
1580        ## compatibility issues.
1581    
1582        my @prop_value = ('CURSOR');
1583    
1584        F: {
1585          if ($t->{type} == IDENT_TOKEN) {
1586            my $v = lc $t->{value}; ## TODO: case
1587            $t = $tt->get_next_token;
1588            if ($Prop->{$prop_name}->{keyword}->{$v}) {
1589              push @prop_value, ['KEYWORD', $v];
1590              last F;
1591            } elsif ($v eq 'inherit' and @prop_value == 1) {
1592              return ($t, {$prop_name => ['INHERIT']});
1593            } else {
1594              $onerror->(type => 'syntax error:'.$prop_name,
1595                         level => $self->{must_level},
1596                         token => $t);
1597              return ($t, undef);
1598            }
1599          } elsif ($t->{type} == URI_TOKEN) {
1600            push @prop_value, ['URI', $t->{value}, \($self->{base_uri})];
1601            $t = $tt->get_next_token;
1602          } else {
1603            $onerror->(type => 'syntax error:'.$prop_name,
1604                       level => $self->{must_level},
1605                       token => $t);
1606            return ($t, undef);
1607          }
1608        
1609          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
1610          if ($t->{type} == COMMA_TOKEN) {
1611            $t = $tt->get_next_token;
1612            $t = $tt->get_next_token while $t->{type} == S_TOKEN;
1613            redo F;
1614          }
1615        } # F
1616    
1617        return ($t, {$prop_name => \@prop_value});
1618      },
1619      serialize => sub {
1620        my ($self, $prop_name, $value) = @_;
1621    
1622        if ($value->[0] eq 'CURSOR') {
1623          return join ', ', map {
1624            if ($_->[0] eq 'URI') {
1625              'url('.$_->[1].')'; ## NOTE: This is what Firefox does.
1626            } elsif ($_->[0] eq 'KEYWORD') {
1627              $_->[1];
1628            } else {
1629              ## NOTE: This should be an error.
1630              '""';
1631            }
1632          } @$value[1..$#$value];
1633        } elsif ($value->[0] eq 'INHERIT') {
1634          return 'inherit';
1635        } else {
1636          return undef;
1637        }
1638      },
1639      keyword => {
1640        auto => 1, crosshair => 1, default => 1, pointer => 1, move => 1,
1641        'e-resize' => 1, 'ne-resize' => 1, 'nw-resize' => 1, 'n-resize' => 1,
1642        'n-resize' => 1, 'se-resize' => 1, 'sw-resize' => 1, 's-resize' => 1,
1643        'w-resize' => 1, text => 1, wait => 1, help => 1, progress => 1,
1644      },
1645      initial => ['CURSOR', ['KEYWORD', 'auto']],
1646      inherited => 1,
1647      compute => sub {
1648        my ($self, $element, $prop_name, $specified_value) = @_;
1649    
1650        if (defined $specified_value and $specified_value->[0] eq 'CURSOR') {
1651          my @new_value = ('CURSOR');
1652          for my $value (@$specified_value[1..$#$specified_value]) {
1653            if ($value->[0] eq 'URI') {
1654              if (defined $value->[2]) {
1655                require Message::DOM::DOMImplementation;
1656                push @new_value, ['URI',
1657                                  Message::DOM::DOMImplementation
1658                                      ->create_uri_reference ($value->[1])
1659                                      ->get_absolute_reference (${$value->[2]})
1660                                      ->get_uri_reference,
1661                                  $value->[2]];
1662              } else {
1663                push @new_value, $value;
1664              }
1665            } else {
1666              push @new_value, $value;
1667            }
1668          }
1669          return \@new_value;
1670        }
1671    
1672        return $specified_value;
1673      },
1674    };
1675    $Attr->{cursor} = $Prop->{cursor};
1676    $Key->{cursor} = $Prop->{cursor};
1677    
1678  $Prop->{'border-style'} = {  $Prop->{'border-style'} = {
1679    css => 'border-style',    css => 'border-style',
1680    dom => 'border_style',    dom => 'border_style',
# Line 642  $Prop->{'border-style'} = { Line 1690  $Prop->{'border-style'} = {
1690            $self->{prop_value}->{'border-top-style'}->{$prop_value}) {            $self->{prop_value}->{'border-top-style'}->{$prop_value}) {
1691          $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];          $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];
1692        } elsif ($prop_value eq 'inherit') {        } elsif ($prop_value eq 'inherit') {
1693          $prop_value{'border-top-style'} = ["KEYWORD", $prop_value];          $prop_value{'border-top-style'} = ["INHERIT"];
1694          $has_inherit = 1;          $has_inherit = 1;
1695        } else {        } else {
1696          $onerror->(type => 'syntax error:keyword:'.$prop_name,          $onerror->(type => 'syntax error:keyword:'.$prop_name,
# Line 731  $Prop->{'border-style'} = { Line 1779  $Prop->{'border-style'} = {
1779  };  };
1780  $Attr->{border_style} = $Prop->{'border-style'};  $Attr->{border_style} = $Prop->{'border-style'};
1781    
1782    $Prop->{'list-style'} = {
1783      css => 'list-style',
1784      dom => 'list_style',
1785      parse => sub {
1786        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1787    
1788        my %prop_value;
1789        my $none = 0;
1790    
1791        F: for my $f (1..3) {
1792          if ($t->{type} == IDENT_TOKEN) {
1793            my $prop_value = lc $t->{value}; ## TODO: case folding
1794            $t = $tt->get_next_token;
1795    
1796            if ($prop_value eq 'none') {
1797              $none++;
1798            } elsif ($Prop->{'list-style-type'}->{keyword}->{$prop_value}) {
1799              if (exists $prop_value{'list-style-type'}) {
1800                $onerror->(type => q[syntax error:duplicate:'list-style-type':].
1801                               $prop_name,
1802                           level => $self->{must_level},
1803                           token => $t);
1804                return ($t, undef);
1805              } else {
1806                $prop_value{'list-style-type'} = ['KEYWORD', $prop_value];
1807              }
1808            } elsif ($Prop->{'list-style-position'}->{keyword}->{$prop_value}) {
1809              if (exists $prop_value{'list-style-position'}) {
1810                $onerror->(type => q[syntax error:duplicate:'list-style-position':].
1811                               $prop_name,
1812                           level => $self->{must_level},
1813                           token => $t);
1814                return ($t, undef);
1815              }
1816    
1817              $prop_value{'list-style-position'} = ['KEYWORD', $prop_value];
1818            } elsif ($f == 1 and $prop_value eq 'inherit') {
1819              $prop_value{'list-style-type'} = ["INHERIT"];
1820              $prop_value{'list-style-position'} = ["INHERIT"];
1821              $prop_value{'list-style-image'} = ["INHERIT"];
1822              last F;
1823            } else {
1824              if ($f == 1) {
1825                $onerror->(type => 'syntax error:'.$prop_name,
1826                           level => $self->{must_level},
1827                           token => $t);
1828                return ($t, undef);
1829              } else {
1830                last F;
1831              }
1832            }
1833          } elsif ($t->{type} == URI_TOKEN) {
1834            if (exists $prop_value{'list-style-image'}) {
1835              $onerror->(type => q[syntax error:duplicate:'list-style-image':].
1836                             $prop_name,
1837                         level => $self->{must_level},
1838                         token => $t);
1839              return ($t, undef);
1840            }
1841            
1842            $prop_value{'list-style-image'}
1843                = ['URI', $t->{value}, \($self->{base_uri})];
1844            $t = $tt->get_next_token;
1845          } else {
1846            if ($f == 1) {
1847              $onerror->(type => 'syntax error:keyword:'.$prop_name,
1848                         level => $self->{must_level},
1849                         token => $t);
1850              return ($t, undef);
1851            } else {
1852              last F;
1853            }
1854          }
1855    
1856          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
1857        } # F
1858        ## NOTE: No browser support |list-style: url(xxx|{EOF}.
1859    
1860        if ($none == 1) {
1861          if (exists $prop_value{'list-style-type'}) {
1862            if (exists $prop_value{'list-style-image'}) {
1863              $onerror->(type => q[syntax error:duplicate:'list-style-image':].
1864                             $prop_name,
1865                         level => $self->{must_level},
1866                         token => $t);
1867              return ($t, undef);
1868            } else {
1869              $prop_value{'list-style-image'} = ['KEYWORD', 'none'];
1870            }
1871          } else {
1872            $prop_value{'list-style-type'} = ['KEYWORD', 'none'];
1873            $prop_value{'list-style-image'} = ['KEYWORD', 'none']
1874                unless exists $prop_value{'list-style-image'};
1875          }
1876        } elsif ($none == 2) {
1877          if (exists $prop_value{'list-style-type'}) {
1878            $onerror->(type => q[syntax error:duplicate:'list-style-type':].
1879                           $prop_name,
1880                       level => $self->{must_level},
1881                       token => $t);
1882            return ($t, undef);
1883          }
1884          if (exists $prop_value{'list-style-image'}) {
1885            $onerror->(type => q[syntax error:duplicate:'list-style-image':].
1886                           $prop_name,
1887                       level => $self->{must_level},
1888                       token => $t);
1889            return ($t, undef);
1890          }
1891          
1892          $prop_value{'list-style-type'} = ['KEYWORD', 'none'];
1893          $prop_value{'list-style-image'} = ['KEYWORD', 'none'];
1894        } elsif ($none == 3) {
1895          $onerror->(type => q[syntax error:duplicate:'list-style-type':].
1896                         $prop_name,
1897                     level => $self->{must_level},
1898                     token => $t);
1899          return ($t, undef);
1900        }
1901    
1902        for (qw/list-style-type list-style-position list-style-image/) {
1903          $prop_value{$_} = $Prop->{$_}->{initial} unless exists $prop_value{$_};
1904        }
1905    
1906        return ($t, \%prop_value);
1907      },
1908      serialize => sub {
1909        my ($self, $prop_name, $value) = @_;
1910        
1911        local $Error::Depth = $Error::Depth + 1;
1912        return $self->list_style_type . ' ' . $self->list_style_position .
1913            ' ' . $self->list_style_image;
1914      },
1915    };
1916    $Attr->{list_style} = $Prop->{'list-style'};
1917    
1918    ## NOTE: Future version of the implementation will change the way to
1919    ## store the parsed value to support CSS 3 properties.
1920    $Prop->{'text-decoration'} = {
1921      css => 'text-decoration',
1922      dom => 'text_decoration',
1923      key => 'text_decoration',
1924      parse => sub {
1925        my ($self, $prop_name, $tt, $t, $onerror) = @_;
1926    
1927        my $value = ['DECORATION']; # , underline, overline, line-through, blink
1928    
1929        if ($t->{type} == IDENT_TOKEN) {
1930          my $v = lc $t->{value}; ## TODO: case
1931          $t = $tt->get_next_token;
1932          if ($v eq 'inherit') {
1933            return ($t, {$prop_name => ['INHERIT']});
1934          } elsif ($v eq 'none') {
1935            return ($t, {$prop_name => $value});
1936          } elsif ($v eq 'underline' and
1937                   $self->{prop_value}->{$prop_name}->{$v}) {
1938            $value->[1] = 1;
1939          } elsif ($v eq 'overline' and
1940                   $self->{prop_value}->{$prop_name}->{$v}) {
1941            $value->[2] = 1;
1942          } elsif ($v eq 'line-through' and
1943                   $self->{prop_value}->{$prop_name}->{$v}) {
1944            $value->[3] = 1;
1945          } elsif ($v eq 'blink' and
1946                   $self->{prop_value}->{$prop_name}->{$v}) {
1947            $value->[4] = 1;
1948          } else {
1949            $onerror->(type => 'syntax error:'.$prop_name,
1950                       level => $self->{must_level},
1951                       token => $t);
1952            return ($t, undef);
1953          }
1954        }
1955    
1956        F: {
1957          $t = $tt->get_next_token while $t->{type} == S_TOKEN;
1958          last F unless $t->{type} == IDENT_TOKEN;
1959    
1960          my $v = lc $t->{value}; ## TODO: case
1961          $t = $tt->get_next_token;
1962          if ($v eq 'underline' and
1963              $self->{prop_value}->{$prop_name}->{$v}) {
1964            $value->[1] = 1;
1965          } elsif ($v eq 'overline' and
1966                   $self->{prop_value}->{$prop_name}->{$v}) {
1967            $value->[1] = 2;
1968          } elsif ($v eq 'line-through' and
1969                   $self->{prop_value}->{$prop_name}->{$v}) {
1970            $value->[1] = 3;
1971          } elsif ($v eq 'blink' and
1972                   $self->{prop_value}->{$prop_name}->{$v}) {
1973            $value->[1] = 4;
1974          } else {
1975            last F;
1976          }
1977    
1978          redo F;
1979        } # F
1980    
1981        return ($t, {$prop_name => $value});
1982      },
1983      serialize => $default_serializer,
1984      initial => ["KEYWORD", "none"],
1985      #inherited => 0,
1986      compute => $compute_as_specified,
1987    };
1988    $Attr->{text_decoration} = $Prop->{'text-decoration'};
1989    $Key->{text_decoration} = $Prop->{'text-decoration'};
1990    
1991  1;  1;
1992  ## $Date$  ## $Date$

Legend:
Removed from v.1.7  
changed lines
  Added in v.1.18

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24