/[suikacvs]/markup/html/whatpm/Whatpm/HTML.pm.src
Suika

Diff of /markup/html/whatpm/Whatpm/HTML.pm.src

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

revision 1.193 by wakaba, Sat Oct 4 04:06:33 2008 UTC revision 1.237 by wakaba, Sun Sep 6 08:29:32 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
 sub ADDRESS_EL () { 0b10 }  
 sub BODY_EL () { 0b100 }  
 sub BUTTON_EL () { 0b1000 }  
 sub CAPTION_EL () { 0b10000 }  
 sub DD_EL () { 0b100000 }  
 sub DIV_EL () { 0b1000000 }  
 sub DT_EL () { 0b10000000 }  
 sub FORM_EL () { 0b100000000 }  
 sub FORMATTING_EL () { 0b1000000000 }  
 sub FRAMESET_EL () { 0b10000000000 }  
 sub HEADING_EL () { 0b100000000000 }  
 sub HTML_EL () { 0b1000000000000 }  
 sub LI_EL () { 0b10000000000000 }  
 sub NOBR_EL () { 0b100000000000000 }  
 sub OPTION_EL () { 0b1000000000000000 }  
 sub OPTGROUP_EL () { 0b10000000000000000 }  
 sub P_EL () { 0b100000000000000000 }  
 sub SELECT_EL () { 0b1000000000000000000 }  
 sub TABLE_EL () { 0b10000000000000000000 }  
 sub TABLE_CELL_EL () { 0b100000000000000000000 }  
 sub TABLE_ROW_EL () { 0b1000000000000000000000 }  
 sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  
 sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
36    
37  ## NOTE: Used in "generate implied end tags" algorithm.  ## Bits 12-15
38  ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL  sub SPECIAL_EL () { 0b1_000000000000000 }
39  ## is used in "generate implied end tags" implementation (search for the  sub SCOPING_EL () { 0b1_00000000000000 }
40  ## function mae).  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub END_TAG_OPTIONAL_EL () {  sub PHRASING_EL () { 0b1_000000000000 }
42    DD_EL |  
43    DT_EL |  ## Bits 10-11
44    LI_EL |  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45    P_EL |  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46    RUBY_COMPONENT_EL  
47  }  ## Bits 6-9
48    sub TABLE_SCOPING_EL () { 0b1_000000000 }
49    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51    sub TABLE_ROWS_EL () { 0b1_000000 }
52    
53    ## Bit 5
54    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55    
56  ## NOTE: Used in </body> and EOF algorithms.  ## NOTE: Used in </body> and EOF algorithms.
57  sub ALL_END_TAG_OPTIONAL_EL () {  ## Bit 4
58    DD_EL |  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  sub SCOPING_EL () {  ## NOTE: Used in "generate implied end tags" algorithm.
61    BUTTON_EL |  ## NOTE: There is a code where a modified version of
62    CAPTION_EL |  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63    HTML_EL |  ## implementation (search for the algorithm name).
64    TABLE_EL |  ## Bit 3
65    TABLE_CELL_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
66    MISC_SCOPING_EL  
67    ## Bits 0-2
68    
69    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    sub FORM_EL () { SPECIAL_EL | 0b001 }
71    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    FORM_EL |  }
125    FRAMESET_EL |  sub TABLE_EL () {
126    HEADING_EL |    SCOPING_EL |
127    OPTION_EL |    TABLE_ROWS_EL |
128    OPTGROUP_EL |    TABLE_SCOPING_EL |
129    SELECT_EL |    0b001
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
130  }  }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 158  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
   eventsource => MISC_SPECIAL_EL,  
182    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
183    figure => MISC_SPECIAL_EL,    figure => MISC_SPECIAL_EL,
184    font => FORMATTING_EL,    font => FORMATTING_EL,
# Line 183  my $el_category = { Line 194  my $el_category = {
194    h6 => HEADING_EL,    h6 => HEADING_EL,
195    head => MISC_SPECIAL_EL,    head => MISC_SPECIAL_EL,
196    header => MISC_SPECIAL_EL,    header => MISC_SPECIAL_EL,
197      hgroup => MISC_SPECIAL_EL,
198    hr => MISC_SPECIAL_EL,    hr => MISC_SPECIAL_EL,
199    html => HTML_EL,    html => HTML_EL,
200    i => FORMATTING_EL,    i => FORMATTING_EL,
# Line 191  my $el_category = { Line 203  my $el_category = {
203    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
204    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
205    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
206      ## XXX keygen? (Whether a void element is in Special or not does not
207      ## affect to the processing, however.)
208    li => LI_EL,    li => LI_EL,
209    link => MISC_SPECIAL_EL,    link => MISC_SPECIAL_EL,
210    listing => MISC_SPECIAL_EL,    listing => MISC_SPECIAL_EL,
# Line 198  my $el_category = { Line 212  my $el_category = {
212    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
213    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
214    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
215    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
216    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
217    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
218    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 235  my $el_category = { Line 249  my $el_category = {
249    u => FORMATTING_EL,    u => FORMATTING_EL,
250    ul => MISC_SPECIAL_EL,    ul => MISC_SPECIAL_EL,
251    wbr => MISC_SPECIAL_EL,    wbr => MISC_SPECIAL_EL,
252      xmp => MISC_SPECIAL_EL,
253  };  };
254    
255  my $el_category_f = {  my $el_category_f = {
256    $MML_NS => {    $MML_NS => {
257      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
258      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
261      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
262      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263    },    },
264    $SVG_NS => {    $SVG_NS => {
265      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
266      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
267      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
268    },    },
269    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
270  };  };
# Line 336  my $foreign_attr_xname = { Line 351  my $foreign_attr_xname = {
351    
352  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
353    
 my $charref_map = {  
   0x0D => 0x000A,  
   0x80 => 0x20AC,  
   0x81 => 0xFFFD,  
   0x82 => 0x201A,  
   0x83 => 0x0192,  
   0x84 => 0x201E,  
   0x85 => 0x2026,  
   0x86 => 0x2020,  
   0x87 => 0x2021,  
   0x88 => 0x02C6,  
   0x89 => 0x2030,  
   0x8A => 0x0160,  
   0x8B => 0x2039,  
   0x8C => 0x0152,  
   0x8D => 0xFFFD,  
   0x8E => 0x017D,  
   0x8F => 0xFFFD,  
   0x90 => 0xFFFD,  
   0x91 => 0x2018,  
   0x92 => 0x2019,  
   0x93 => 0x201C,  
   0x94 => 0x201D,  
   0x95 => 0x2022,  
   0x96 => 0x2013,  
   0x97 => 0x2014,  
   0x98 => 0x02DC,  
   0x99 => 0x2122,  
   0x9A => 0x0161,  
   0x9B => 0x203A,  
   0x9C => 0x0153,  
   0x9D => 0xFFFD,  
   0x9E => 0x017E,  
   0x9F => 0x0178,  
 }; # $charref_map  
 $charref_map->{$_} = 0xFFFD  
     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,  
         0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF  
         0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,  
         0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,  
         0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,  
         0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,  
         0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;  
   
354  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
355  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
356    
# Line 486  sub parse_byte_stream ($$$$;$$) { Line 457  sub parse_byte_stream ($$$$;$$) {
457      if (defined $charset_name) {      if (defined $charset_name) {
458        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
459    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
460        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
461        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
462            ($byte_stream);            ($byte_stream);
# Line 558  sub parse_byte_stream ($$$$;$$) { Line 528  sub parse_byte_stream ($$$$;$$) {
528            
529      if ($char_stream) { # if supported      if ($char_stream) { # if supported
530        ## "Change the encoding" algorithm:        ## "Change the encoding" algorithm:
   
       ## Step 1      
       if ($charset->{category} &  
           Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {  
         $charset = Message::Charset::Info->get_by_html_name ('utf-8');  
         ($char_stream, $e_status) = $charset->get_decode_handle  
             ($byte_stream,  
              byte_buffer => \ $buffer->{buffer});  
       }  
       $charset_name = $charset->get_iana_name;  
531                
532        ## Step 2        ## Step 1
533        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
534            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
535          !!!parse-error (type => 'charset label:matching',          !!!parse-error (type => 'charset label:matching',
# Line 579  sub parse_byte_stream ($$$$;$$) { Line 539  sub parse_byte_stream ($$$$;$$) {
539          return;          return;
540        }        }
541    
542          ## Step 2 (HTML5 revision 3205)
543          if (defined $self->{input_encoding} and
544              Message::Charset::Info->get_by_html_name ($self->{input_encoding})
545              ->{category} & Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
546            $self->{confident} = 1;
547            return;
548          }
549    
550          ## Step 3
551          if ($charset->{category} &
552              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
553            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
554            ($char_stream, $e_status) = $charset->get_decode_handle
555                ($byte_stream,
556                 byte_buffer => \ $buffer->{buffer});
557          }
558          $charset_name = $charset->get_iana_name;
559    
560        !!!parse-error (type => 'charset label detected',        !!!parse-error (type => 'charset label detected',
561                        text => $self->{input_encoding},                        text => $self->{input_encoding},
562                        value => $charset_name,                        value => $charset_name,
563                        level => $self->{level}->{warn},                        level => $self->{level}->{warn},
564                        token => $token);                        token => $token);
565                
566        ## Step 3        ## Step 4
567        # if (can) {        # if (can) {
568          ## change the encoding on the fly.          ## change the encoding on the fly.
569          #$self->{confident} = 1;          #$self->{confident} = 1;
570          #return;          #return;
571        # }        # }
572                
573        ## Step 4        ## Step 5
574        throw Whatpm::HTML::RestartParser ();        throw Whatpm::HTML::RestartParser ();
575      }      }
576    }; # $self->{change_encoding}    }; # $self->{change_encoding}
# Line 672  sub parse_char_stream ($$$;$$) { Line 650  sub parse_char_stream ($$$;$$) {
650    
651    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
652    
653      ## Confidence: irrelevant.
654    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
655    
656    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
657        if defined $self->{input_encoding};        if defined $self->{input_encoding};
658  ## TODO: |{input_encoding}| is needless?  ## TODO: |{input_encoding}| is needless?
# Line 833  sub new ($) { Line 813  sub new ($) {
813    return $self;    return $self;
814  } # new  } # new
815    
816  sub CM_ENTITY () { 0b001 } # & markup in data  ## Insertion modes
 sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)  
 sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)  
   
 sub PLAINTEXT_CONTENT_MODEL () { 0 }  
 sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }  
 sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  
 sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  
   
 sub DATA_STATE () { 0 }  
 #sub ENTITY_DATA_STATE () { 1 }  
 sub TAG_OPEN_STATE () { 2 }  
 sub CLOSE_TAG_OPEN_STATE () { 3 }  
 sub TAG_NAME_STATE () { 4 }  
 sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }  
 sub ATTRIBUTE_NAME_STATE () { 6 }  
 sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }  
 sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }  
 sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  
 sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  
 sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  
 #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  
 sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  
 sub COMMENT_START_STATE () { 14 }  
 sub COMMENT_START_DASH_STATE () { 15 }  
 sub COMMENT_STATE () { 16 }  
 sub COMMENT_END_STATE () { 17 }  
 sub COMMENT_END_DASH_STATE () { 18 }  
 sub BOGUS_COMMENT_STATE () { 19 }  
 sub DOCTYPE_STATE () { 20 }  
 sub BEFORE_DOCTYPE_NAME_STATE () { 21 }  
 sub DOCTYPE_NAME_STATE () { 22 }  
 sub AFTER_DOCTYPE_NAME_STATE () { 23 }  
 sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }  
 sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }  
 sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }  
 sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }  
 sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }  
 sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }  
 sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }  
 sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  
 sub BOGUS_DOCTYPE_STATE () { 32 }  
 sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  
 sub SELF_CLOSING_START_TAG_STATE () { 34 }  
 sub CDATA_SECTION_STATE () { 35 }  
 sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec  
 sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec  
 sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec  
 sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec  
 sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec  
 sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec  
 sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec  
 sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec  
 ## NOTE: "Entity data state", "entity in attribute value state", and  
 ## "consume a character reference" algorithm are jointly implemented  
 ## using the following six states:  
 sub ENTITY_STATE () { 44 }  
 sub ENTITY_HASH_STATE () { 45 }  
 sub NCR_NUM_STATE () { 46 }  
 sub HEXREF_X_STATE () { 47 }  
 sub HEXREF_HEX_STATE () { 48 }  
 sub ENTITY_NAME_STATE () { 49 }  
 sub PCDATA_STATE () { 50 } # "data state" in the spec  
   
 sub DOCTYPE_TOKEN () { 1 }  
 sub COMMENT_TOKEN () { 2 }  
 sub START_TAG_TOKEN () { 3 }  
 sub END_TAG_TOKEN () { 4 }  
 sub END_OF_FILE_TOKEN () { 5 }  
 sub CHARACTER_TOKEN () { 6 }  
817    
818  sub AFTER_HTML_IMS () { 0b100 }  sub AFTER_HTML_IMS () { 0b100 }
819  sub HEAD_IMS ()       { 0b1000 }  sub HEAD_IMS ()       { 0b1000 }
# Line 913  sub ROW_IMS ()        { 0b10000000 } Line 824  sub ROW_IMS ()        { 0b10000000 }
824  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
825  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
826  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
827  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
828      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
829      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
830      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
831    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
832        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
833        ## combined with the original insertion mode.  In thie parser,
834        ## they are stored together in the bit-or'ed form.
835    
836    sub IM_MASK () { 0b11111111111 }
837    
838  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
839    
# Line 943  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 860  sub IN_SELECT_IM () { SELECT_IMS | 0b01
860  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
861  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
862    
 ## Implementations MUST act as if state machine in the spec  
   
 sub _initialize_tokenizer ($) {  
   my $self = shift;  
   $self->{state} = DATA_STATE; # MUST  
   #$self->{s_kwd}; # state keyword - initialized when used  
   #$self->{entity__value}; # initialized when used  
   #$self->{entity__match}; # initialized when used  
   $self->{content_model} = PCDATA_CONTENT_MODEL; # be  
   undef $self->{ct}; # current token  
   undef $self->{ca}; # current attribute  
   undef $self->{last_stag_name}; # last emitted start tag name  
   #$self->{prev_state}; # initialized when used  
   delete $self->{self_closing};  
   $self->{char_buffer} = '';  
   $self->{char_buffer_pos} = 0;  
   $self->{nc} = -1; # next input character  
   #$self->{next_nc}  
   !!!next-input-character;  
   $self->{token} = [];  
   # $self->{escape}  
 } # _initialize_tokenizer  
   
 ## A token has:  
 ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,  
 ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  
 ##   ->{name} (DOCTYPE_TOKEN)  
 ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##   ->{pubid} (DOCTYPE_TOKEN)  
 ##   ->{sysid} (DOCTYPE_TOKEN)  
 ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  
 ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##        ->{name}  
 ##        ->{value}  
 ##        ->{has_reference} == 1 or 0  
 ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  
 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.  
 ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  
 ##     while the token is pushed back to the stack.  
   
 ## Emitted token MUST immediately be handled by the tree construction state.  
   
 ## Before each step, UA MAY check to see if either one of the scripts in  
 ## "list of scripts that will execute as soon as possible" or the first  
 ## script in the "list of scripts that will execute asynchronously",  
 ## has completed loading.  If one has, then it MUST be executed  
 ## and removed from the list.  
   
 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)  
 ## (This requirement was dropped from HTML5 spec, unfortunately.)  
   
 my $is_space = {  
   0x0009 => 1, # CHARACTER TABULATION (HT)  
   0x000A => 1, # LINE FEED (LF)  
   #0x000B => 0, # LINE TABULATION (VT)  
   0x000C => 1, # FORM FEED (FF)  
   #0x000D => 1, # CARRIAGE RETURN (CR)  
   0x0020 => 1, # SPACE (SP)  
 };  
   
 sub _get_next_token ($) {  
   my $self = shift;  
   
   if ($self->{self_closing}) {  
     !!!parse-error (type => 'nestc', token => $self->{ct});  
     ## NOTE: The |self_closing| flag is only set by start tag token.  
     ## In addition, when a start tag token is emitted, it is always set to  
     ## |ct|.  
     delete $self->{self_closing};  
   }  
   
   if (@{$self->{token}}) {  
     $self->{self_closing} = $self->{token}->[0]->{self_closing};  
     return shift @{$self->{token}};  
   }  
   
   A: {  
     if ($self->{state} == PCDATA_STATE) {  
       ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.  
   
       if ($self->{nc} == 0x0026) { # &  
         !!!cp (0.1);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity data state".  In this implementation, the tokenizer  
         ## is switched to the |ENTITY_STATE|, which is an implementation  
         ## of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = DATA_STATE;  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003C) { # <  
         !!!cp (0.2);  
         $self->{state} = TAG_OPEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (0.3);  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (0.4);  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       $self->{read_until}->($token->{data}, q[<&], length $token->{data});  
   
       ## Stay in the state.  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == DATA_STATE) {  
       $self->{s_kwd} = '' unless defined $self->{s_kwd};  
       if ($self->{nc} == 0x0026) { # &  
         $self->{s_kwd} = '';  
         if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA  
             not $self->{escape}) {  
           !!!cp (1);  
           ## NOTE: In the spec, the tokenizer is switched to the  
           ## "entity data state".  In this implementation, the tokenizer  
           ## is switched to the |ENTITY_STATE|, which is an implementation  
           ## of the "consume a character reference" algorithm.  
           $self->{entity_add} = -1;  
           $self->{prev_state} = DATA_STATE;  
           $self->{state} = ENTITY_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (2);  
           #  
         }  
       } elsif ($self->{nc} == 0x002D) { # -  
         if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
           $self->{s_kwd} .= '-';  
             
           if ($self->{s_kwd} eq '<!--') {  
             !!!cp (3);  
             $self->{escape} = 1; # unless $self->{escape};  
             $self->{s_kwd} = '--';  
             #  
           } elsif ($self->{s_kwd} eq '---') {  
             !!!cp (4);  
             $self->{s_kwd} = '--';  
             #  
           } else {  
             !!!cp (5);  
             #  
           }  
         }  
           
         #  
       } elsif ($self->{nc} == 0x0021) { # !  
         if (length $self->{s_kwd}) {  
           !!!cp (5.1);  
           $self->{s_kwd} .= '!';  
           #  
         } else {  
           !!!cp (5.2);  
           #$self->{s_kwd} = '';  
           #  
         }  
         #  
       } elsif ($self->{nc} == 0x003C) { # <  
         if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA  
             (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA  
              not $self->{escape})) {  
           !!!cp (6);  
           $self->{state} = TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (7);  
           $self->{s_kwd} = '';  
           #  
         }  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{escape} and  
             ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA  
           if ($self->{s_kwd} eq '--') {  
             !!!cp (8);  
             delete $self->{escape};  
           } else {  
             !!!cp (9);  
           }  
         } else {  
           !!!cp (10);  
         }  
           
         $self->{s_kwd} = '';  
         #  
       } elsif ($self->{nc} == -1) {  
         !!!cp (11);  
         $self->{s_kwd} = '';  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (12);  
         $self->{s_kwd} = '';  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       if ($self->{read_until}->($token->{data}, q[-!<>&],  
                                 length $token->{data})) {  
         $self->{s_kwd} = '';  
       }  
   
       ## Stay in the data state.  
       if ($self->{content_model} == PCDATA_CONTENT_MODEL) {  
         !!!cp (13);  
         $self->{state} = PCDATA_STATE;  
       } else {  
         !!!cp (14);  
         ## Stay in the state.  
       }  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == TAG_OPEN_STATE) {  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if ($self->{nc} == 0x002F) { # /  
           !!!cp (15);  
           !!!next-input-character;  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           redo A;  
         } elsif ($self->{nc} == 0x0021) { # !  
           !!!cp (15.1);  
           $self->{s_kwd} = '<' unless $self->{escape};  
           #  
         } else {  
           !!!cp (16);  
           #  
         }  
   
         ## reconsume  
         $self->{state} = DATA_STATE;  
         !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA  
         if ($self->{nc} == 0x0021) { # !  
           !!!cp (17);  
           $self->{state} = MARKUP_DECLARATION_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x002F) { # /  
           !!!cp (18);  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0041 <= $self->{nc} and  
                  $self->{nc} <= 0x005A) { # A..Z  
           !!!cp (19);  
           $self->{ct}  
             = {type => START_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $self->{line_prev},  
                column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0061 <= $self->{nc} and  
                  $self->{nc} <= 0x007A) { # a..z  
           !!!cp (20);  
           $self->{ct} = {type => START_TAG_TOKEN,  
                                     tag_name => chr ($self->{nc}),  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x003E) { # >  
           !!!cp (21);  
           !!!parse-error (type => 'empty start tag',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<>',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         } elsif ($self->{nc} == 0x003F) { # ?  
           !!!cp (22);  
           !!!parse-error (type => 'pio',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = BOGUS_COMMENT_STATE;  
           $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev},  
                                    };  
           ## $self->{nc} is intentionally left as is  
           redo A;  
         } else {  
           !!!cp (23);  
           !!!parse-error (type => 'bare stago',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           ## reconsume  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         }  
       } else {  
         die "$0: $self->{content_model} in tag open";  
       }  
     } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {  
       ## NOTE: The "close tag open state" in the spec is implemented as  
       ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if (defined $self->{last_stag_name}) {  
           $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;  
           $self->{s_kwd} = '';  
           ## Reconsume.  
           redo A;  
         } else {  
           ## No start tag token has ever been emitted  
           ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.  
           !!!cp (28);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                     line => $l, column => $c,  
                    });  
           redo A;  
         }  
       }  
   
       if (0x0041 <= $self->{nc} and  
           $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (29);  
         $self->{ct}  
             = {type => END_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x007A) { # a..z  
         !!!cp (30);  
         $self->{ct} = {type => END_TAG_TOKEN,  
                                   tag_name => chr ($self->{nc}),  
                                   line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (31);  
         !!!parse-error (type => 'empty end tag',  
                         line => $self->{line_prev}, ## "<" in "</>"  
                         column => $self->{column_prev} - 1);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (32);  
         !!!parse-error (type => 'bare etago');  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                   line => $l, column => $c,  
                  });  
   
         redo A;  
       } else {  
         !!!cp (33);  
         !!!parse-error (type => 'bogus end tag');  
         $self->{state} = BOGUS_COMMENT_STATE;  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev}, # "<" of "</"  
                                   column => $self->{column_prev} - 1,  
                                  };  
         ## NOTE: $self->{nc} is intentionally left as is.  
         ## Although the "anything else" case of the spec not explicitly  
         ## states that the next input character is to be reconsumed,  
         ## it will be included to the |data| of the comment token  
         ## generated from the bogus end tag, as defined in the  
         ## "bogus comment state" entry.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {  
       my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;  
       if (length $ch) {  
         my $CH = $ch;  
         $ch =~ tr/a-z/A-Z/;  
         my $nch = chr $self->{nc};  
         if ($nch eq $ch or $nch eq $CH) {  
           !!!cp (24);  
           ## Stay in the state.  
           $self->{s_kwd} .= $nch;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (25);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         }  
       } else { # after "<{tag-name}"  
         unless ($is_space->{$self->{nc}} or  
                 {  
                  0x003E => 1, # >  
                  0x002F => 1, # /  
                  -1 => 1, # EOF  
                 }->{$self->{nc}}) {  
           !!!cp (26);  
           ## Reconsume.  
           $self->{state} = DATA_STATE;  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (27);  
           $self->{ct}  
               = {type => END_TAG_TOKEN,  
                  tag_name => $self->{last_stag_name},  
                  line => $self->{line_prev},  
                  column => $self->{column_prev} - 1 - length $self->{s_kwd}};  
           $self->{state} = TAG_NAME_STATE;  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == TAG_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (34);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (35);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This should never be reached.  
           #  !!! cp (36);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (37);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (38);  
         $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);  
           # start tag or end tag  
         ## Stay in this state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (39);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This state should never be reached.  
           #  !!! cp (40);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (41);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (42);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (44);  
         $self->{ct}->{tag_name} .= chr $self->{nc};  
           # start tag or end tag  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (45);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (46);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (47);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (48);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (49);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (50);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (52);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (53);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (54);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (55);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (56);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {  
       my $before_leave = sub {  
         if (exists $self->{ct}->{attributes} # start tag or end tag  
             ->{$self->{ca}->{name}}) { # MUST  
           !!!cp (57);  
           !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});  
           ## Discard $self->{ca} # MUST  
         } else {  
           !!!cp (58);  
           $self->{ct}->{attributes}->{$self->{ca}->{name}}  
             = $self->{ca};  
         }  
       }; # $before_leave  
   
       if ($is_space->{$self->{nc}}) {  
         !!!cp (59);  
         $before_leave->();  
         $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (60);  
         $before_leave->();  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (61);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp (62);  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!parse-error (type => 'end tag attribute');  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (63);  
         $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (64);  
         $before_leave->();  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (66);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (67);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (68);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (69);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (70);  
         }  
         $self->{ca}->{name} .= chr ($self->{nc});  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (71);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (72);  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (73);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (74);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (75);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (76);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (77);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (79);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (80);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (81);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (78);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (82);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;          
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (83);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (84);  
         $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (85);  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         ## reconsume  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (86);  
         $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!parse-error (type => 'empty unquoted attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (87);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (88);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (89);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (90);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (91);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (92);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x003D) { # =  
           !!!cp (93);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (94);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (95);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (96);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{prev_state} = $self->{state};  
         $self->{entity_add} = 0x0022; # "  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (97);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (98);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (99);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (100);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (101);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (102);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = 0x0027; # '  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (103);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (104);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (105);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (106);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q['&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (107);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (108);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (109);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (110);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (111);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (112);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (113);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (114);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (115);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (116);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["'=& >],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (118);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (119);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (120);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (121);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (122);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (122.3);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (122.1);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (122.2);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.1');  
         !!!parse-error (type => 'no space between attributes');  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp ('124.2');  
           !!!parse-error (type => 'nestc', token => $self->{ct});  
           ## TODO: Different type than slash in start tag  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp ('124.4');  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp ('124.5');  
           }  
           ## TODO: Test |<title></title/>|  
         } else {  
           !!!cp ('124.3');  
           $self->{self_closing} = 1;  
         }  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (124.7);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (124.5);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (124.6);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.4');  
         !!!parse-error (type => 'nestc');  
         ## TODO: This error type is wrong.  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_COMMENT_STATE) {  
       ## (only happen if PCDATA state)  
   
       ## NOTE: Unlike spec's "bogus comment state", this implementation  
       ## consumes characters one-by-one basis.  
         
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (124);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (125);  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } else {  
         !!!cp (126);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[>],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {  
       ## (only happen if PCDATA state)  
         
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (133);  
         $self->{state} = MD_HYPHEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0044 or # D  
                $self->{nc} == 0x0064) { # d  
         ## ASCII case-insensitive.  
         !!!cp (130);  
         $self->{state} = MD_DOCTYPE_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and  
                $self->{open_elements}->[-1]->[1] & FOREIGN_EL and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.4);                  
         $self->{state} = MD_CDATA_STATE;  
         $self->{s_kwd} = '[';  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (136);  
       }  
   
       !!!parse-error (type => 'bogus comment',  
                       line => $self->{line_prev},  
                       column => $self->{column_prev} - 1);  
       ## Reconsume.  
       $self->{state} = BOGUS_COMMENT_STATE;  
       $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                 line => $self->{line_prev},  
                                 column => $self->{column_prev} - 1,  
                                };  
       redo A;  
     } elsif ($self->{state} == MD_HYPHEN_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (127);  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         $self->{state} = COMMENT_START_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (128);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => '-',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_DOCTYPE_STATE) {  
       ## ASCII case-insensitive.  
       if ($self->{nc} == [  
             undef,  
             0x004F, # O  
             0x0043, # C  
             0x0054, # T  
             0x0059, # Y  
             0x0050, # P  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x006F, # o  
             0x0063, # c  
             0x0074, # t  
             0x0079, # y  
             0x0070, # p  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (131);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 6 and  
                ($self->{nc} == 0x0045 or # E  
                 $self->{nc} == 0x0065)) { # e  
         !!!cp (129);  
         $self->{state} = DOCTYPE_STATE;  
         $self->{ct} = {type => DOCTYPE_TOKEN,  
                                   quirks => 1,  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7,  
                                  };  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (132);          
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_CDATA_STATE) {  
       if ($self->{nc} == {  
             '[' => 0x0043, # C  
             '[C' => 0x0044, # D  
             '[CD' => 0x0041, # A  
             '[CDA' => 0x0054, # T  
             '[CDAT' => 0x0041, # A  
           }->{$self->{s_kwd}}) {  
         !!!cp (135.1);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{s_kwd} eq '[CDATA' and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.2);  
         $self->{ct} = {type => CHARACTER_TOKEN,  
                                   data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7};  
         $self->{state} = CDATA_SECTION_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (135.3);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (137);  
         $self->{state} = COMMENT_START_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (138);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (139);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (140);  
         $self->{ct}->{data} # comment  
             .= chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (141);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (142);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (143);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (144);  
         $self->{ct}->{data} # comment  
             .= '-' . chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (145);  
         $self->{state} = COMMENT_END_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (146);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (147);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[-],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (148);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (149);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (150);  
         $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (151);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == 0x002D) { # -  
         !!!cp (152);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '-'; # comment  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (153);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (154);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (155);  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (156);  
         !!!parse-error (type => 'no space before DOCTYPE name');  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (157);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (158);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (159);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } else {  
         !!!cp (160);  
         $self->{ct}->{name} = chr $self->{nc};  
         delete $self->{ct}->{quirks};  
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_NAME_STATE) {  
 ## ISSUE: Redundant "First," in the spec.  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (161);  
         $self->{state} = AFTER_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (162);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (163);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (164);  
         $self->{ct}->{name}  
           .= chr ($self->{nc}); # DOCTYPE  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (165);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (166);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (167);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == 0x0050 or # P  
                $self->{nc} == 0x0070) { # p  
         $self->{state} = PUBLIC_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0053 or # S  
                $self->{nc} == 0x0073) { # s  
         $self->{state} = SYSTEM_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (180);  
         !!!parse-error (type => 'string after DOCTYPE name');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == PUBLIC_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0055, # U  
             0x0042, # B  
             0x004C, # L  
             0x0049, # I  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0075, # u  
             0x0062, # b  
             0x006C, # l  
             0x0069, # i  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (175);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x0043 or # C  
                 $self->{nc} == 0x0063)) { # c  
         !!!cp (168);  
         $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (169);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == SYSTEM_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0059, # Y  
             0x0053, # S  
             0x0054, # T  
             0x0045, # E  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0079, # y  
             0x0073, # s  
             0x0074, # t  
             0x0065, # e  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (170);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x004D or # M  
                 $self->{nc} == 0x006D)) { # m  
         !!!cp (171);  
         $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (172);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (181);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0022) { # "  
         !!!cp (182);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0027) { # '  
         !!!cp (183);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x003E) { # >  
         !!!cp (184);  
         !!!parse-error (type => 'no PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (185);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (186);  
         !!!parse-error (type => 'string after PUBLIC');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (187);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (188);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (189);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (190);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q[">],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (191);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (192);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (193);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (194);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q['>],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (195);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (196);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (197);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (198);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (199);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (200);  
         !!!parse-error (type => 'string after PUBLIC literal');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (201);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (202);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (203);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (204);  
         !!!parse-error (type => 'no SYSTEM literal');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (205);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (206);  
         !!!parse-error (type => 'string after SYSTEM');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (207);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (208);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (209);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (210);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q[">],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (211);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (212);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (213);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (214);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q['>],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (215);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (216);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (217);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (218);  
         !!!parse-error (type => 'string after SYSTEM literal');  
         #$self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (219);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (220);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (221);  
         my $s = '';  
         $self->{read_until}->($s, q[>], 0);  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_STATE) {  
       ## NOTE: "CDATA section state" in the state is jointly implemented  
       ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,  
       ## and |CDATA_SECTION_MSE2_STATE|.  
         
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.1);  
         $self->{state} = CDATA_SECTION_MSE1_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.2);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.3);  
           ## No token to emit. $self->{ct} is discarded.  
         }          
         redo A;  
       } else {  
         !!!cp (221.4);  
         $self->{ct}->{data} .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{data},  
                               q<]>,  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
   
       ## ISSUE: "text tokens" in spec.  
     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {  
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.5);  
         $self->{state} = CDATA_SECTION_MSE2_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.6);  
         $self->{ct}->{data} .= ']';  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.7);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.8);  
           ## No token to emit. $self->{ct} is discarded.  
         }  
         redo A;  
       } elsif ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.9); # character  
         $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.11);  
         $self->{ct}->{data} .= ']]'; # character  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_STATE) {  
       if ($is_space->{$self->{nc}} or  
           {  
             0x003C => 1, 0x0026 => 1, -1 => 1, # <, &  
             $self->{entity_add} => 1,  
           }->{$self->{nc}}) {  
         !!!cp (1001);  
         ## Don't consume  
         ## No error  
         ## Return nothing.  
         #  
       } elsif ($self->{nc} == 0x0023) { # #  
         !!!cp (999);  
         $self->{state} = ENTITY_HASH_STATE;  
         $self->{s_kwd} = '#';  
         !!!next-input-character;  
         redo A;  
       } elsif ((0x0041 <= $self->{nc} and  
                 $self->{nc} <= 0x005A) or # A..Z  
                (0x0061 <= $self->{nc} and  
                 $self->{nc} <= 0x007A)) { # a..z  
         !!!cp (998);  
         require Whatpm::_NamedEntityList;  
         $self->{state} = ENTITY_NAME_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         $self->{entity__value} = $self->{s_kwd};  
         $self->{entity__match} = 0;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (1027);  
         !!!parse-error (type => 'bare ero');  
         ## Return nothing.  
         #  
       }  
   
       ## NOTE: No character is consumed by the "consume a character  
       ## reference" algorithm.  In other word, there is an "&" character  
       ## that does not introduce a character reference, which would be  
       ## appended to the parent element or the attribute value in later  
       ## process of the tokenizer.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (997);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } else {  
         !!!cp (996);  
         $self->{ca}->{value} .= '&';  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_HASH_STATE) {  
       if ($self->{nc} == 0x0078 or # x  
           $self->{nc} == 0x0058) { # X  
         !!!cp (995);  
         $self->{state} = HEXREF_X_STATE;  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0030 <= $self->{nc} and  
                $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (994);  
         $self->{state} = NCR_NUM_STATE;  
         $self->{s_kwd} = $self->{nc} - 0x0030;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare nero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" is appended to the parent element or the attribute  
         ## value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1019);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&#',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1,  
                    });  
           redo A;  
         } else {  
           !!!cp (993);  
           $self->{ca}->{value} .= '&#';  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == NCR_NUM_STATE) {  
       if (0x0030 <= $self->{nc} and  
           $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (1012);  
         $self->{s_kwd} *= 10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
           
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1013);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1014);  
         !!!parse-error (type => 'no refc');  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1015);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1016);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (992);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (991);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == HEXREF_X_STATE) {  
       if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or  
           (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or  
           (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {  
         # 0..9, A..F, a..f  
         !!!cp (990);  
         $self->{state} = HEXREF_HEX_STATE;  
         $self->{s_kwd} = 0;  
         ## Reconsume.  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare hcro',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" followed by "X" or "x" is appended to the parent  
         ## element or the attribute value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1005);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (989);  
           $self->{ca}->{value} .= '&' . $self->{s_kwd};  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == HEXREF_HEX_STATE) {  
       if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {  
         # 0..9  
         !!!cp (1002);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x0066) { # a..f  
         !!!cp (1003);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0060 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x0046) { # A..F  
         !!!cp (1004);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0040 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1006);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1007);  
         !!!parse-error (type => 'no refc',  
                         line => $self->{line},  
                         column => $self->{column});  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1008);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1009);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (988);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (987);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_NAME_STATE) {  
       if (length $self->{s_kwd} < 30 and  
           ## NOTE: Some number greater than the maximum length of entity name  
           ((0x0041 <= $self->{nc} and # a  
             $self->{nc} <= 0x005A) or # x  
            (0x0061 <= $self->{nc} and # a  
             $self->{nc} <= 0x007A) or # z  
            (0x0030 <= $self->{nc} and # 0  
             $self->{nc} <= 0x0039) or # 9  
            $self->{nc} == 0x003B)) { # ;  
         our $EntityChar;  
         $self->{s_kwd} .= chr $self->{nc};  
         if (defined $EntityChar->{$self->{s_kwd}}) {  
           if ($self->{nc} == 0x003B) { # ;  
             !!!cp (1020);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = 1;  
             !!!next-input-character;  
             #  
           } else {  
             !!!cp (1021);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = -1;  
             ## Stay in the state.  
             !!!next-input-character;  
             redo A;  
           }  
         } else {  
           !!!cp (1022);  
           $self->{entity__value} .= chr $self->{nc};  
           $self->{entity__match} *= 2;  
           ## Stay in the state.  
           !!!next-input-character;  
           redo A;  
         }  
       }  
   
       my $data;  
       my $has_ref;  
       if ($self->{entity__match} > 0) {  
         !!!cp (1023);  
         $data = $self->{entity__value};  
         $has_ref = 1;  
         #  
       } elsif ($self->{entity__match} < 0) {  
         !!!parse-error (type => 'no refc');  
         if ($self->{prev_state} != DATA_STATE and # in attribute  
             $self->{entity__match} < -1) {  
           !!!cp (1024);  
           $data = '&' . $self->{s_kwd};  
           #  
         } else {  
           !!!cp (1025);  
           $data = $self->{entity__value};  
           $has_ref = 1;  
           #  
         }  
       } else {  
         !!!cp (1026);  
         !!!parse-error (type => 'bare ero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - length $self->{s_kwd});  
         $data = '&' . $self->{s_kwd};  
         #  
       }  
     
       ## NOTE: In these cases, when a character reference is found,  
       ## it is consumed and a character token is returned, or, otherwise,  
       ## nothing is consumed and returned, according to the spec algorithm.  
       ## In this implementation, anything that has been examined by the  
       ## tokenizer is appended to the parent element or the attribute value  
       ## as string, either literal string when no character reference or  
       ## entity-replaced string otherwise, in this stage, since any characters  
       ## that would not be consumed are appended in the data state or in an  
       ## appropriate attribute value state anyway.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (986);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN,  
                   data => $data,  
                   line => $self->{line_prev},  
                   column => $self->{column_prev} + 1 - length $self->{s_kwd},  
                  });  
         redo A;  
       } else {  
         !!!cp (985);  
         $self->{ca}->{value} .= $data;  
         $self->{ca}->{has_reference} = 1 if $has_ref;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } else {  
       die "$0: $self->{state}: Unknown state";  
     }  
   } # A    
   
   die "$0: _get_next_token: unexpected case";  
 } # _get_next_token  
   
863  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
864    my $self = shift;    my $self = shift;
865    ## NOTE: $self->{document} MUST be specified before this method is called    ## NOTE: $self->{document} MUST be specified before this method is called
# Line 3467  sub _construct_tree ($) { Line 888  sub _construct_tree ($) {
888    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
889    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
890    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
891        
892    !!!next-token;    !!!next-token;
893    
894    undef $self->{form_element};    undef $self->{form_element};
895    undef $self->{head_element};    undef $self->{head_element};
896      undef $self->{head_element_inserted};
897    $self->{open_elements} = [];    $self->{open_elements} = [];
898    undef $self->{inner_html_node};    undef $self->{inner_html_node};
899      undef $self->{ignore_newline};
900    
901    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
902    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3497  sub _tree_construction_initial ($) { Line 916  sub _tree_construction_initial ($) {
916    
917    INITIAL: {    INITIAL: {
918      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
919        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
920        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
921        ## language.        ## another language.  (We don't support such mode switchings; it
922          ## is nonsense to do anything different from what browsers do.)
923        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
924        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
925        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        my $doctype = $self->{document}->create_document_type_definition
926        if (not defined $token->{name} or # <!DOCTYPE>            ($doctype_name);
927            defined $token->{sysid}) {  
928          $doctype_name =~ tr/A-Z/a-z/; # ASCII case-insensitive
929          if ($doctype_name ne 'html') {
930          !!!cp ('t1');          !!!cp ('t1');
931          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
932        } elsif ($doctype_name ne 'HTML') {        } elsif (defined $token->{pubid}) {
933          !!!cp ('t2');          !!!cp ('t2');
934            ## XXX Obsolete permitted DOCTYPEs
935          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
936        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{sysid}) {
937          if ($token->{pubid} eq 'XSLT-compat') {          if ($token->{sysid} eq 'about:legacy-compat') {
938            !!!cp ('t1.2');            !!!cp ('t1.2'); ## <!DOCTYPE HTML SYSTEM "about:legacy-compat">
939            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
940                            level => $self->{level}->{should});                            level => $self->{level}->{should});
941          } else {          } else {
942            !!!parse-error (type => 'not HTML5', token => $token);            !!!parse-error (type => 'not HTML5', token => $token);
943          }          }
944        } else {        } else { ## <!DOCTYPE HTML>
945          !!!cp ('t3');          !!!cp ('t3');
946          #          #
947        }        }
948                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
949        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
950        ## are empty strings, so that we don't set any value in missing cases.        ## are empty strings, so that we don't set any value in missing cases.
951        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
952        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
953    
954        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
955        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
956          ## string, while |null| is an allowed value for the attribute
957          ## according to DOM3 Core.
958        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
959                
960        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'html') {
961          !!!cp ('t4');          !!!cp ('t4');
962          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
963        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{pubid}) {
# Line 3783  sub _tree_construction_root_element ($) Line 1207  sub _tree_construction_root_element ($)
1207      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
1208      !!!ack-later;      !!!ack-later;
1209      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
1210    } # B    } # B
1211    
1212    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3820  sub _reset_insertion_mode ($) { Line 1242  sub _reset_insertion_mode ($) {
1242          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1243          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1244          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1245        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1246          if ($last) {          if ($last) {
1247            !!!cp ('t28.2');            !!!cp ('t28.2');
1248            #            #
# Line 3849  sub _reset_insertion_mode ($) { Line 1271  sub _reset_insertion_mode ($) {
1271        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1272                
1273        ## Step 15        ## Step 15
1274        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1275          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1276            !!!cp ('t29');            !!!cp ('t29');
1277            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3981  sub _tree_construction_main ($) { Line 1403  sub _tree_construction_main ($) {
1403    
1404      ## Step 1      ## Step 1
1405      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1406      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1407    
1408      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1409      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1410      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1411    
1412      ## Step 4      ## Step 3, 4
1413      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!nack ('t40.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
1414    
1415      ## Step 7      !!!nack ('t40.1');
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
1416      !!!next-token;      !!!next-token;
1417    }; # $parse_rcdata    }; # $parse_rcdata
1418    
1419    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1420        ## Step 1
1421      my $script_el;      my $script_el;
1422      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1423    
1424        ## Step 2
1425      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1426    
1427        ## Step 3
1428        ## TODO: Mark as "already executed", if ...
1429    
1430        ## Step 4 (HTML5 revision 2702)
1431        $insert->($script_el);
1432        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1433    
1434        ## Step 5
1435      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1436      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!nack ('t45.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) {  
       !!!cp ('t45');  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       !!!cp ('t46');  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
1437    
1438      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1439          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       !!!cp ('t47');  
       ## Ignore the token  
     } else {  
       !!!cp ('t48');  
       !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       !!!cp ('t49');  
       ## TODO: mark as "already executed"  
     } else {  
       !!!cp ('t50');  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
1440    
1441        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
1442      !!!next-token;      !!!next-token;
1443    }; # $script_start_tag    }; # $script_start_tag
1444    
1445    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1446    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag (OBSOLETE; unused).
1447      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1448    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1449    
1450    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4169  sub _tree_construction_main ($) { Line 1529  sub _tree_construction_main ($) {
1529            !!!cp ('t59');            !!!cp ('t59');
1530            $furthest_block = $node;            $furthest_block = $node;
1531            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1532              ## NOTE: The topmost (eldest) node.
1533          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1534            !!!cp ('t60');            !!!cp ('t60');
1535            last OE;            last OE;
# Line 4252  sub _tree_construction_main ($) { Line 1613  sub _tree_construction_main ($) {
1613                
1614        ## Step 8        ## Step 8
1615        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
1616            ## Foster parenting.
1617          my $foster_parent_element;          my $foster_parent_element;
1618          my $next_sibling;          my $next_sibling;
1619          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1620            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1621                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;              !!!cp ('t65.2');
1622                               if (defined $parent and $parent->node_type == 1) {              $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1623                                 !!!cp ('t65.1');              $next_sibling = $self->{open_elements}->[$_]->[0];
1624                                 $foster_parent_element = $parent;              undef $next_sibling
1625                                 $next_sibling = $self->{open_elements}->[$_]->[0];                  unless $next_sibling->parent_node eq $foster_parent_element;
1626                               } else {              last OE;
1627                                 !!!cp ('t65.2');            }
1628                                 $foster_parent_element          } # OE
1629                                   = $self->{open_elements}->[$_ - 1]->[0];          $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1630                               }  
                              last OE;  
                            }  
                          } # OE  
                          $foster_parent_element = $self->{open_elements}->[0]->[0]  
                            unless defined $foster_parent_element;  
1631          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
1632          $open_tables->[-1]->[1] = 1; # tainted          $open_tables->[-1]->[1] = 1; # tainted
1633        } else {        } else {
# Line 4315  sub _tree_construction_main ($) { Line 1672  sub _tree_construction_main ($) {
1672            $i = $_;            $i = $_;
1673          }          }
1674        } # OE        } # OE
1675        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1676                
1677        ## Step 14        ## Step 14
1678        redo FET;        redo FET;
# Line 4326  sub _tree_construction_main ($) { Line 1683  sub _tree_construction_main ($) {
1683      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
1684    }; # $insert_to_current    }; # $insert_to_current
1685    
1686      ## Foster parenting.  Note that there are three "foster parenting"
1687      ## code in the parser: for elements (this one), for texts, and for
1688      ## elements in the AAA code.
1689    my $insert_to_foster = sub {    my $insert_to_foster = sub {
1690      my $child = shift;      my $child = shift;
1691      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
# Line 4333  sub _tree_construction_main ($) { Line 1693  sub _tree_construction_main ($) {
1693        my $foster_parent_element;        my $foster_parent_element;
1694        my $next_sibling;        my $next_sibling;
1695        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1696          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1697                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;            !!!cp ('t71');
1698                               if (defined $parent and $parent->node_type == 1) {            $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1699                                 !!!cp ('t70');            $next_sibling = $self->{open_elements}->[$_]->[0];
1700                                 $foster_parent_element = $parent;            undef $next_sibling
1701                                 $next_sibling = $self->{open_elements}->[$_]->[0];                unless $next_sibling->parent_node eq $foster_parent_element;
1702                               } else {            last OE;
1703                                 !!!cp ('t71');          }
1704                                 $foster_parent_element        } # OE
1705                                   = $self->{open_elements}->[$_ - 1]->[0];        $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1706                               }  
1707                               last OE;        $foster_parent_element->insert_before ($child, $next_sibling);
                            }  
                          } # OE  
                          $foster_parent_element = $self->{open_elements}->[0]->[0]  
                            unless defined $foster_parent_element;  
                          $foster_parent_element->insert_before  
                            ($child, $next_sibling);  
1708        $open_tables->[-1]->[1] = 1; # tainted        $open_tables->[-1]->[1] = 1; # tainted
1709      } else {      } else {
1710        !!!cp ('t72');        !!!cp ('t72');
# Line 4358  sub _tree_construction_main ($) { Line 1712  sub _tree_construction_main ($) {
1712      }      }
1713    }; # $insert_to_foster    }; # $insert_to_foster
1714    
1715      ## NOTE: Insert a character (MUST): When a character is inserted, if
1716      ## the last node that was inserted by the parser is a Text node and
1717      ## the character has to be inserted after that node, then the
1718      ## character is appended to the Text node.  However, if any other
1719      ## node is inserted by the parser, then a new Text node is created
1720      ## and the character is appended as that Text node.  If I'm not
1721      ## wrong, for a parser with scripting disabled, there are only two
1722      ## cases where this occurs.  One is the case where an element node
1723      ## is inserted to the |head| element.  This is covered by using the
1724      ## |$self->{head_element_inserted}| flag.  Another is the case where
1725      ## an element or comment is inserted into the |table| subtree while
1726      ## foster parenting happens.  This is covered by using the [2] flag
1727      ## of the |$open_tables| structure.  All other cases are handled
1728      ## simply by calling |manakai_append_text| method.
1729    
1730      ## TODO: |<body><script>document.write("a<br>");
1731      ## document.body.removeChild (document.body.lastChild);
1732      ## document.write ("b")</script>|
1733    
1734    B: while (1) {    B: while (1) {
1735    
1736        ## The "in table text" insertion mode.
1737        if ($self->{insertion_mode} & TABLE_IMS and
1738            not $self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
1739            not $self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1740          C: {
1741            my $s;
1742            if ($token->{type} == CHARACTER_TOKEN) {
1743              !!!cp ('t194');
1744              $self->{pending_chars} ||= [];
1745              push @{$self->{pending_chars}}, $token;
1746              !!!next-token;
1747              next B;
1748            } else {
1749              if ($self->{pending_chars}) {
1750                $s = join '', map { $_->{data} } @{$self->{pending_chars}};
1751                delete $self->{pending_chars};
1752                if ($s =~ /[^\x09\x0A\x0C\x0D\x20]/) {
1753                  !!!cp ('t195');
1754                  #
1755                } else {
1756                  !!!cp ('t195.1');
1757                  #$self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1758                  $self->{open_elements}->[-1]->[0]->append_child
1759                      ($self->{document}->create_text_node ($s));
1760                  last C;
1761                }
1762              } else {
1763                !!!cp ('t195.2');
1764                last C;
1765              }
1766            }
1767    
1768            ## Foster parenting.
1769            !!!parse-error (type => 'in table:#text', token => $token);
1770    
1771            ## NOTE: As if in body, but insert into the foster parent element.
1772            $reconstruct_active_formatting_elements->($insert_to_foster);
1773                
1774            if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
1775              # MUST
1776              my $foster_parent_element;
1777              my $next_sibling;
1778              OE: for (reverse 0..$#{$self->{open_elements}}) {
1779                if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1780                  !!!cp ('t197');
1781                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1782                  $next_sibling = $self->{open_elements}->[$_]->[0];
1783                  undef $next_sibling
1784                    unless $next_sibling->parent_node eq $foster_parent_element;
1785                  last OE;
1786                }
1787              } # OE
1788              $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1789    
1790              !!!cp ('t199');
1791              $foster_parent_element->insert_before
1792                  ($self->{document}->create_text_node ($s), $next_sibling);
1793    
1794              $open_tables->[-1]->[1] = 1; # tainted
1795              $open_tables->[-1]->[2] = 1; # ~node inserted
1796            } else {
1797              ## NOTE: Fragment case or in a foster parent'ed element
1798              ## (e.g. |<table><span>a|).  In fragment case, whether the
1799              ## character is appended to existing node or a new node is
1800              ## created is irrelevant, since the foster parent'ed nodes
1801              ## are discarded and fragment parsing does not invoke any
1802              ## script.
1803              !!!cp ('t200');
1804              $self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1805            }
1806          } # C
1807        } # TABLE_IMS
1808    
1809      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1810        !!!cp ('t73');        !!!cp ('t73');
1811        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
# Line 4405  sub _tree_construction_main ($) { Line 1852  sub _tree_construction_main ($) {
1852        } else {        } else {
1853          !!!cp ('t87');          !!!cp ('t87');
1854          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1855            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1856        }        }
1857        !!!next-token;        !!!next-token;
1858        next B;        next B;
1859        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1860          if ($token->{type} == CHARACTER_TOKEN) {
1861            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1862            delete $self->{ignore_newline};
1863    
1864            if (length $token->{data}) {
1865              !!!cp ('t43');
1866              $self->{open_elements}->[-1]->[0]->manakai_append_text
1867                  ($token->{data});
1868            } else {
1869              !!!cp ('t43.1');
1870            }
1871            !!!next-token;
1872            next B;
1873          } elsif ($token->{type} == END_TAG_TOKEN) {
1874            delete $self->{ignore_newline};
1875    
1876            if ($token->{tag_name} eq 'script') {
1877              !!!cp ('t50');
1878              
1879              ## Para 1-2
1880              my $script = pop @{$self->{open_elements}};
1881              
1882              ## Para 3
1883              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1884    
1885              ## Para 4
1886              ## TODO: $old_insertion_point = $current_insertion_point;
1887              ## TODO: $current_insertion_point = just before $self->{nc};
1888    
1889              ## Para 5
1890              ## TODO: Run the $script->[0].
1891    
1892              ## Para 6
1893              ## TODO: $current_insertion_point = $old_insertion_point;
1894    
1895              ## Para 7
1896              ## TODO: if ($pending_external_script) {
1897                ## TODO: ...
1898              ## TODO: }
1899    
1900              !!!next-token;
1901              next B;
1902            } else {
1903              !!!cp ('t42');
1904    
1905              pop @{$self->{open_elements}};
1906    
1907              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1908              !!!next-token;
1909              next B;
1910            }
1911          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1912            delete $self->{ignore_newline};
1913    
1914            !!!cp ('t44');
1915            !!!parse-error (type => 'not closed',
1916                            text => $self->{open_elements}->[-1]->[0]
1917                                ->manakai_local_name,
1918                            token => $token);
1919    
1920            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1921            #  ## TODO: Mark as "already executed"
1922            #}
1923    
1924            pop @{$self->{open_elements}};
1925    
1926            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1927            ## Reprocess.
1928            next B;
1929          } else {
1930            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1931          }
1932      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1933        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1934          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4419  sub _tree_construction_main ($) { Line 1940  sub _tree_construction_main ($) {
1940               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1941              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1942              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1943               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1944            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1945            !!!cp ('t87.2');            !!!cp ('t87.2');
1946            #            #
1947          } elsif ({          } elsif ({
1948                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1949                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1950                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1951                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1952                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1953                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1954                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1955                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1956                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1957                     ($token->{tag_name} eq 'font' and
1958                      ($token->{attributes}->{color} or
1959                       $token->{attributes}->{face} or
1960                       $token->{attributes}->{size}))) {
1961            !!!cp ('t87.2');            !!!cp ('t87.2');
1962            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1963                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4508  sub _tree_construction_main ($) { Line 2033  sub _tree_construction_main ($) {
2033          }          }
2034        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2035          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
2036          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
2037          #            !!!cp ('t87.41');
2038              #
2039              ## XXXscript: Execute script here.
2040            } else {
2041              !!!cp ('t87.5');
2042              #
2043            }
2044        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2045          !!!cp ('t87.6');          !!!cp ('t87.6');
2046          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4520  sub _tree_construction_main ($) { Line 2051  sub _tree_construction_main ($) {
2051          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
2052              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
2053    
2054            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
2055    
2056          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
2057          ## Reprocess.          ## Reprocess.
2058          next B;          next B;
# Line 4532  sub _tree_construction_main ($) { Line 2065  sub _tree_construction_main ($) {
2065        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
2066          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
2067            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2068              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
2069              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
2070              #                $self->{open_elements}->[-1]->[0]->append_child
2071                    ($self->{document}->create_text_node ($1));
2072                  delete $self->{head_element_inserted};
2073                  ## NOTE: |</head> <link> |
2074                  #
2075                } else {
2076                  !!!cp ('t88.2');
2077                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2078                  ## NOTE: |</head> &#x20;|
2079                  #
2080                }
2081            } else {            } else {
2082              !!!cp ('t88.1');              !!!cp ('t88.1');
2083              ## Ignore the token.              ## Ignore the token.
# Line 4630  sub _tree_construction_main ($) { Line 2173  sub _tree_construction_main ($) {
2173            !!!cp ('t97');            !!!cp ('t97');
2174          }          }
2175    
2176              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2177                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2178                  !!!cp ('t98');              !!!cp ('t98');
2179                  ## As if </noscript>              ## As if </noscript>
2180                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2181                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2182                                  token => $token);                              token => $token);
2183                            
2184                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2185                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2186                } else {            } else {
2187                  !!!cp ('t99');              !!!cp ('t99');
2188                }            }
2189    
2190                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2191                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2192                  !!!cp ('t100');              !!!cp ('t100');
2193                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2194                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2195                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2196                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2197                } else {              $self->{head_element_inserted} = 1;
2198                  !!!cp ('t101');            } else {
2199                }              !!!cp ('t101');
2200                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2201                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2202                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2203                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2204                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2205                !!!next-token;            !!!nack ('t101.1');
2206                next B;            !!!next-token;
2207              } elsif ($token->{tag_name} eq 'link') {            next B;
2208                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'link') {
2209                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: There is a "as if in head" code clone.
2210                  !!!cp ('t102');            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2211                  !!!parse-error (type => 'after head',              !!!cp ('t102');
2212                                  text => $token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
2213                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
2214                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
2215                } else {                  [$self->{head_element}, $el_category->{head}];
2216                  !!!cp ('t103');              $self->{head_element_inserted} = 1;
2217                }            } else {
2218                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!cp ('t103');
2219                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            }
2220                pop @{$self->{open_elements}} # <head>            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2221                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}};
2222                !!!ack ('t103.1');            pop @{$self->{open_elements}} # <head>
2223                !!!next-token;                if $self->{insertion_mode} == AFTER_HEAD_IM;
2224                next B;            !!!ack ('t103.1');
2225              } elsif ($token->{tag_name} eq 'meta') {            !!!next-token;
2226                ## NOTE: There is a "as if in head" code clone.            next B;
2227                if ($self->{insertion_mode} == AFTER_HEAD_IM) {          } elsif ($token->{tag_name} eq 'command') {
2228                  !!!cp ('t104');            if ($self->{insertion_mode} == IN_HEAD_IM) {
2229                  !!!parse-error (type => 'after head',              ## NOTE: If the insertion mode at the time of the emission
2230                                  text => $token->{tag_name}, token => $token);              ## of the token was "before head", $self->{insertion_mode}
2231                  push @{$self->{open_elements}},              ## is already changed to |IN_HEAD_IM|.
2232                      [$self->{head_element}, $el_category->{head}];  
2233                } else {              ## NOTE: There is a "as if in head" code clone.
2234                  !!!cp ('t105');              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2235                }              pop @{$self->{open_elements}};
2236                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              pop @{$self->{open_elements}} # <head>
2237                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                  if $self->{insertion_mode} == AFTER_HEAD_IM;
2238                !!!ack ('t103.2');
2239                !!!next-token;
2240                next B;
2241              } else {
2242                ## NOTE: "in head noscript" or "after head" insertion mode
2243                ## - in these cases, these tags are treated as same as
2244                ## normal in-body tags.
2245                !!!cp ('t103.3');
2246                #
2247              }
2248            } elsif ($token->{tag_name} eq 'meta') {
2249              ## NOTE: There is a "as if in head" code clone.
2250              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2251                !!!cp ('t104');
2252                !!!parse-error (type => 'after head',
2253                                text => $token->{tag_name}, token => $token);
2254                push @{$self->{open_elements}},
2255                    [$self->{head_element}, $el_category->{head}];
2256                $self->{head_element_inserted} = 1;
2257              } else {
2258                !!!cp ('t105');
2259              }
2260              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2261              my $meta_el = pop @{$self->{open_elements}};
2262    
2263                unless ($self->{confident}) {                unless ($self->{confident}) {
2264                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4749  sub _tree_construction_main ($) { Line 2316  sub _tree_construction_main ($) {
2316                !!!ack ('t110.1');                !!!ack ('t110.1');
2317                !!!next-token;                !!!next-token;
2318                next B;                next B;
2319              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2320                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2321                  !!!cp ('t111');              !!!cp ('t111');
2322                  ## As if </noscript>              ## As if </noscript>
2323                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2324                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2325                                  token => $token);                              token => $token);
2326                            
2327                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2328                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2329                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2330                  !!!cp ('t112');              !!!cp ('t112');
2331                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2332                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2333                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2334                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2335                } else {              $self->{head_element_inserted} = 1;
2336                  !!!cp ('t113');            } else {
2337                }              !!!cp ('t113');
2338              }
2339    
2340                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2341                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2342                    : $self->{open_elements}->[-1]->[0];  
2343                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2344                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2345                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2346                next B;            ## |head| element is inserted only for the purpose of
2347              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2348                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2349                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2350                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2351                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2352                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2353                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2354                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2355                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2356                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2357                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2358                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2359                  !!!cp ('t115');              !!!cp ('t114');
2360                }              !!!parse-error (type => 'after head',
2361                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2362                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2363                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2364                next B;              $self->{head_element_inserted} = 1;
2365              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2366                !!!cp ('t115');
2367              }
2368              $parse_rcdata->(CDATA_CONTENT_MODEL);
2369              ## ISSUE: A spec bug [Bug 6038]
2370              splice @{$self->{open_elements}}, -2, 1, () # <head>
2371                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2372              next B;
2373            } elsif ($token->{tag_name} eq 'noscript') {
2374                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2375                  !!!cp ('t116');                  !!!cp ('t116');
2376                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4815  sub _tree_construction_main ($) { Line 2391  sub _tree_construction_main ($) {
2391                  !!!cp ('t118');                  !!!cp ('t118');
2392                  #                  #
2393                }                }
2394              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2395                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2396                  !!!cp ('t119');              !!!cp ('t119');
2397                  ## As if </noscript>              ## As if </noscript>
2398                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2399                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2400                                  token => $token);                              token => $token);
2401                            
2402                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2403                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2404                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2405                  !!!cp ('t120');              !!!cp ('t120');
2406                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2407                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2408                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2409                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2410                } else {              $self->{head_element_inserted} = 1;
2411                  !!!cp ('t121');            } else {
2412                }              !!!cp ('t121');
2413              }
2414    
2415                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2416                $script_start_tag->();            $script_start_tag->();
2417                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2418                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2419                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2420              } elsif ($token->{tag_name} eq 'body' or            next B;
2421                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2422                     $token->{tag_name} eq 'frameset') {
2423                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2424                  !!!cp ('t122');                  !!!cp ('t122');
2425                  ## As if </noscript>                  ## As if </noscript>
# Line 4976  sub _tree_construction_main ($) { Line 2554  sub _tree_construction_main ($) {
2554              } elsif ({              } elsif ({
2555                        body => 1, html => 1,                        body => 1, html => 1,
2556                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2557                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2558                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2559                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2560                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2561                  !!!cp ('t140');                  !!!cp ('t140');
# Line 5002  sub _tree_construction_main ($) { Line 2581  sub _tree_construction_main ($) {
2581                ## Ignore the token                ## Ignore the token
2582                !!!next-token;                !!!next-token;
2583                next B;                next B;
2584              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2585                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2586                  !!!cp ('t142.2');              !!!cp ('t142.2');
2587                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2588                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2589                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2590                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2591        
2592                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2593                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2594                  !!!cp ('t143.2');              !!!cp ('t143.2');
2595                  ## As if </head>              ## As if </head>
2596                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2597                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2598        
2599                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2600                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2601                  !!!cp ('t143.3');              !!!cp ('t143.3');
2602                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2603                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2604                                  text => 'br', token => $token);                              text => 'br', token => $token);
2605                  ## As if </noscript>              ## As if </noscript>
2606                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2607                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2608    
2609                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2610                  ## As if </head>              ## As if </head>
2611                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2612                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2613    
2614                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2615                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2616                  !!!cp ('t143.4');              !!!cp ('t143.4');
2617                  #              #
2618                } else {            } else {
2619                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2620                }            }
2621    
2622                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2623                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2624                !!!cp ('t145');                !!!cp ('t145');
2625                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2626                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5090  sub _tree_construction_main ($) { Line 2664  sub _tree_construction_main ($) {
2664              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2665              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2666              ## reprocess              ## reprocess
2667              next B;          next B;
2668        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2669          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2670            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5148  sub _tree_construction_main ($) { Line 2722  sub _tree_construction_main ($) {
2722        } else {        } else {
2723          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2724        }        }
   
           ## ISSUE: An issue in the spec.  
2725      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2726            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
2727              !!!cp ('t150');              !!!cp ('t150');
# Line 5165  sub _tree_construction_main ($) { Line 2737  sub _tree_construction_main ($) {
2737                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2738                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2739                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2740                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2741                  ## have an element in table scope                  ## have an element in table scope
2742                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2743                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2744                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2745                      !!!cp ('t151');                      !!!cp ('t151');
2746    
2747                      ## Close the cell                      ## Close the cell
# Line 5193  sub _tree_construction_main ($) { Line 2765  sub _tree_construction_main ($) {
2765                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2766                  !!!next-token;                  !!!next-token;
2767                  next B;                  next B;
2768                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2769                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2770                                  token => $token);                                  token => $token);
2771                                    
# Line 5203  sub _tree_construction_main ($) { Line 2775  sub _tree_construction_main ($) {
2775                  INSCOPE: {                  INSCOPE: {
2776                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2777                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2778                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2779                        !!!cp ('t155');                        !!!cp ('t155');
2780                        $i = $_;                        $i = $_;
2781                        last INSCOPE;                        last INSCOPE;
# Line 5229  sub _tree_construction_main ($) { Line 2801  sub _tree_construction_main ($) {
2801                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2802                  }                  }
2803    
2804                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2805                    !!!cp ('t159');                    !!!cp ('t159');
2806                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2807                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5258  sub _tree_construction_main ($) { Line 2830  sub _tree_construction_main ($) {
2830              }              }
2831            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2832              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2833                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2834                  ## have an element in table scope                  ## have an element in table scope
2835                  my $i;                  my $i;
2836                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5308  sub _tree_construction_main ($) { Line 2880  sub _tree_construction_main ($) {
2880                                    
2881                  !!!next-token;                  !!!next-token;
2882                  next B;                  next B;
2883                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2884                  !!!cp ('t169');                  !!!cp ('t169');
2885                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2886                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5320  sub _tree_construction_main ($) { Line 2892  sub _tree_construction_main ($) {
2892                  #                  #
2893                }                }
2894              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2895                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2896                  ## have a table element in table scope                  ## have a table element in table scope
2897                  my $i;                  my $i;
2898                  INSCOPE: {                  INSCOPE: {
2899                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2900                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2901                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2902                        !!!cp ('t171');                        !!!cp ('t171');
2903                        $i = $_;                        $i = $_;
2904                        last INSCOPE;                        last INSCOPE;
# Line 5351  sub _tree_construction_main ($) { Line 2923  sub _tree_construction_main ($) {
2923                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2924                  }                  }
2925                                    
2926                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2927                    !!!cp ('t175');                    !!!cp ('t175');
2928                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2929                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5369  sub _tree_construction_main ($) { Line 2941  sub _tree_construction_main ($) {
2941                                    
2942                  !!!next-token;                  !!!next-token;
2943                  next B;                  next B;
2944                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2945                  !!!cp ('t177');                  !!!cp ('t177');
2946                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2947                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5384  sub _tree_construction_main ($) { Line 2956  sub _tree_construction_main ($) {
2956                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2957                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2958                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2959                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2960                ## have an element in table scope                ## have an element in table scope
2961                my $i;                my $i;
2962                my $tn;                my $tn;
# Line 5401  sub _tree_construction_main ($) { Line 2973  sub _tree_construction_main ($) {
2973                                line => $token->{line},                                line => $token->{line},
2974                                column => $token->{column}};                                column => $token->{column}};
2975                      next B;                      next B;
2976                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2977                      !!!cp ('t180');                      !!!cp ('t180');
2978                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2979                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5421  sub _tree_construction_main ($) { Line 2993  sub _tree_construction_main ($) {
2993                  next B;                  next B;
2994                } # INSCOPE                } # INSCOPE
2995              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2996                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2997                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2998                                token => $token);                                token => $token);
2999    
# Line 5430  sub _tree_construction_main ($) { Line 3002  sub _tree_construction_main ($) {
3002                my $i;                my $i;
3003                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3004                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3005                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
3006                    !!!cp ('t184');                    !!!cp ('t184');
3007                    $i = $_;                    $i = $_;
3008                    last INSCOPE;                    last INSCOPE;
# Line 5441  sub _tree_construction_main ($) { Line 3013  sub _tree_construction_main ($) {
3013                } # INSCOPE                } # INSCOPE
3014                unless (defined $i) {                unless (defined $i) {
3015                  !!!cp ('t186');                  !!!cp ('t186');
3016            ## TODO: Wrong error type?
3017                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3018                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
3019                  ## Ignore the token                  ## Ignore the token
# Line 5454  sub _tree_construction_main ($) { Line 3027  sub _tree_construction_main ($) {
3027                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3028                }                }
3029    
3030                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
3031                  !!!cp ('t188');                  !!!cp ('t188');
3032                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
3033                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5486  sub _tree_construction_main ($) { Line 3059  sub _tree_construction_main ($) {
3059                  !!!cp ('t191');                  !!!cp ('t191');
3060                  #                  #
3061                }                }
3062              } elsif ({          } elsif ({
3063                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
3064                        thead => 1, tr => 1,                    thead => 1, tr => 1,
3065                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
3066                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
3067                !!!cp ('t192');            !!!cp ('t192');
3068                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
3069                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
3070                ## Ignore the token            ## Ignore the token
3071                !!!next-token;            !!!next-token;
3072                next B;            next B;
3073              } else {          } else {
3074                !!!cp ('t193');            !!!cp ('t193');
3075                #            #
3076              }          }
3077        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3078          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3079            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5519  sub _tree_construction_main ($) { Line 3092  sub _tree_construction_main ($) {
3092        $insert = $insert_to_current;        $insert = $insert_to_current;
3093        #        #
3094      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3095        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == START_TAG_TOKEN) {
         if (not $open_tables->[-1]->[1] and # tainted  
             $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {  
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
           unless (length $token->{data}) {  
             !!!cp ('t194');  
             !!!next-token;  
             next B;  
           } else {  
             !!!cp ('t195');  
           }  
         }  
   
         !!!parse-error (type => 'in table:#text', token => $token);  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     !!!cp ('t196');  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 !!!cp ('t199');  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
           $open_tables->[-1]->[1] = 1; # tainted  
         } else {  
           !!!cp ('t200');  
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
         }  
               
         !!!next-token;  
         next B;  
       } elsif ($token->{type} == START_TAG_TOKEN) {  
3096          if ({          if ({
3097               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3098               th => 1, td => 1,               th => 1, td => 1,
3099              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3100            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3101              ## Clear back to table context              ## Clear back to table context
3102              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3103                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5601  sub _tree_construction_main ($) { Line 3110  sub _tree_construction_main ($) {
3110              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3111            }            }
3112                        
3113            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3114              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3115                !!!cp ('t202');                !!!cp ('t202');
3116                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5615  sub _tree_construction_main ($) { Line 3124  sub _tree_construction_main ($) {
3124                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3125              }              }
3126                                    
3127                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3128                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3129                    !!!cp ('t204');                !!!cp ('t204');
3130                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3131                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3132                    !!!next-token;                !!!nack ('t204');
3133                    next B;                !!!next-token;
3134                  } else {                next B;
3135                    !!!cp ('t205');              } else {
3136                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3137                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3138                  }                ## reprocess in the "in row" insertion mode
3139                } else {              }
3140                  !!!cp ('t206');            } else {
3141                }              !!!cp ('t206');
3142              }
3143    
3144                ## Clear back to table row context                ## Clear back to table row context
3145                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5638  sub _tree_construction_main ($) { Line 3148  sub _tree_construction_main ($) {
3148                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3149                }                }
3150                                
3151                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3152                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3153              $self->{insertion_mode} = IN_CELL_IM;
3154    
3155                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3156                                
3157                !!!nack ('t207.1');            !!!nack ('t207.1');
3158              !!!next-token;
3159              next B;
3160            } elsif ({
3161                      caption => 1, col => 1, colgroup => 1,
3162                      tbody => 1, tfoot => 1, thead => 1,
3163                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3164                     }->{$token->{tag_name}}) {
3165              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3166                ## As if </tr>
3167                ## have an element in table scope
3168                my $i;
3169                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3170                  my $node = $self->{open_elements}->[$_];
3171                  if ($node->[1] == TABLE_ROW_EL) {
3172                    !!!cp ('t208');
3173                    $i = $_;
3174                    last INSCOPE;
3175                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3176                    !!!cp ('t209');
3177                    last INSCOPE;
3178                  }
3179                } # INSCOPE
3180                unless (defined $i) {
3181                  !!!cp ('t210');
3182                  ## TODO: This type is wrong.
3183                  !!!parse-error (type => 'unmacthed end tag',
3184                                  text => $token->{tag_name}, token => $token);
3185                  ## Ignore the token
3186                  !!!nack ('t210.1');
3187                !!!next-token;                !!!next-token;
3188                next B;                next B;
3189              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] & TABLE_ROW_EL) {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ($node->[1] & TABLE_SCOPING_EL) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                   !!!cp ('t210');  
 ## TODO: This type is wrong.  
                   !!!parse-error (type => 'unmacthed end tag',  
                                   text => $token->{tag_name}, token => $token);  
                   ## Ignore the token  
                   !!!nack ('t210.1');  
                   !!!next-token;  
                   next B;  
                 }  
3190                                    
3191                  ## Clear back to table row context                  ## Clear back to table row context
3192                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5698  sub _tree_construction_main ($) { Line 3209  sub _tree_construction_main ($) {
3209                  }                  }
3210                }                }
3211    
3212                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3213                  ## have an element in table scope                  ## have an element in table scope
3214                  my $i;                  my $i;
3215                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3216                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3217                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3218                      !!!cp ('t214');                      !!!cp ('t214');
3219                      $i = $_;                      $i = $_;
3220                      last INSCOPE;                      last INSCOPE;
# Line 5745  sub _tree_construction_main ($) { Line 3256  sub _tree_construction_main ($) {
3256                  !!!cp ('t218');                  !!!cp ('t218');
3257                }                }
3258    
3259                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3260                  ## Clear back to table context              ## Clear back to table context
3261                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3262                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3263                    !!!cp ('t219');                !!!cp ('t219');
3264                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3265                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3266                  }              }
3267                                
3268                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3269                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3270                  ## reprocess              ## reprocess
3271                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3272                  next B;              !!!ack-later;
3273                } elsif ({              next B;
3274                          caption => 1,            } elsif ({
3275                          colgroup => 1,                      caption => 1,
3276                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3277                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3278                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3279                ## Clear back to table context
3280                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3281                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3282                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5772  sub _tree_construction_main ($) { Line 3284  sub _tree_construction_main ($) {
3284                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3285                  }                  }
3286                                    
3287                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3288                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3289                                    
3290                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3291                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3292                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3293                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3294                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3295                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3296                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3297                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3298                  !!!next-token;                                        }->{$token->{tag_name}};
3299                  !!!nack ('t220.1');              !!!next-token;
3300                  next B;              !!!nack ('t220.1');
3301                } else {              next B;
3302                  die "$0: in table: <>: $token->{tag_name}";            } else {
3303                }              die "$0: in table: <>: $token->{tag_name}";
3304              }
3305              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3306                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3307                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5800  sub _tree_construction_main ($) { Line 3313  sub _tree_construction_main ($) {
3313                my $i;                my $i;
3314                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3315                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3316                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3317                    !!!cp ('t221');                    !!!cp ('t221');
3318                    $i = $_;                    $i = $_;
3319                    last INSCOPE;                    last INSCOPE;
# Line 5827  sub _tree_construction_main ($) { Line 3340  sub _tree_construction_main ($) {
3340                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3341                }                }
3342    
3343                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3344                  !!!cp ('t225');                  !!!cp ('t225');
3345                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3346                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5847  sub _tree_construction_main ($) { Line 3360  sub _tree_construction_main ($) {
3360            !!!ack-later;            !!!ack-later;
3361            next B;            next B;
3362          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
3363            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.8');
3364              !!!cp ('t227.8');            ## NOTE: This is a "as if in head" code clone.
3365              ## NOTE: This is a "as if in head" code clone.            $parse_rcdata->(CDATA_CONTENT_MODEL);
3366              $parse_rcdata->(CDATA_CONTENT_MODEL);            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3367              next B;            next B;
           } else {  
             !!!cp ('t227.7');  
             #  
           }  
3368          } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
3369            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.6');
3370              !!!cp ('t227.6');            ## NOTE: This is a "as if in head" code clone.
3371              ## NOTE: This is a "as if in head" code clone.            $script_start_tag->();
3372              $script_start_tag->();            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3373              next B;            next B;
           } else {  
             !!!cp ('t227.5');  
             #  
           }  
3374          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
3375            if (not $open_tables->[-1]->[1]) { # tainted            if ($token->{attributes}->{type}) {
3376              if ($token->{attributes}->{type}) { ## TODO: case              my $type = $token->{attributes}->{type}->{value};
3377                my $type = lc $token->{attributes}->{type}->{value};              $type =~ tr/A-Z/a-z/; ## ASCII case-insensitive.
3378                if ($type eq 'hidden') {              if ($type eq 'hidden') {
3379                  !!!cp ('t227.3');                !!!cp ('t227.3');
3380                  !!!parse-error (type => 'in table',                !!!parse-error (type => 'in table',
3381                                  text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
3382    
3383                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3384                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3385    
3386                  ## TODO: form element pointer                ## TODO: form element pointer
3387    
3388                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3389    
3390                  !!!next-token;                !!!next-token;
3391                  !!!ack ('t227.2.1');                !!!ack ('t227.2.1');
3392                  next B;                next B;
               } else {  
                 !!!cp ('t227.2');  
                 #  
               }  
3393              } else {              } else {
3394                !!!cp ('t227.1');                !!!cp ('t227.1');
3395                #                #
# Line 5907  sub _tree_construction_main ($) { Line 3409  sub _tree_construction_main ($) {
3409          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3410          #          #
3411        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3412              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3413                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3414                ## have an element in table scope            ## have an element in table scope
3415                my $i;                my $i;
3416                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3417                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3418                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3419                    !!!cp ('t228');                    !!!cp ('t228');
3420                    $i = $_;                    $i = $_;
3421                    last INSCOPE;                    last INSCOPE;
# Line 5948  sub _tree_construction_main ($) { Line 3450  sub _tree_construction_main ($) {
3450                !!!nack ('t231.1');                !!!nack ('t231.1');
3451                next B;                next B;
3452              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3453                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3454                  ## As if </tr>                  ## As if </tr>
3455                  ## have an element in table scope                  ## have an element in table scope
3456                  my $i;                  my $i;
3457                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3458                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3459                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3460                      !!!cp ('t233');                      !!!cp ('t233');
3461                      $i = $_;                      $i = $_;
3462                      last INSCOPE;                      last INSCOPE;
# Line 5987  sub _tree_construction_main ($) { Line 3489  sub _tree_construction_main ($) {
3489                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3490                }                }
3491    
3492                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3493                  ## have an element in table scope                  ## have an element in table scope
3494                  my $i;                  my $i;
3495                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3496                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3497                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3498                      !!!cp ('t237');                      !!!cp ('t237');
3499                      $i = $_;                      $i = $_;
3500                      last INSCOPE;                      last INSCOPE;
# Line 6039  sub _tree_construction_main ($) { Line 3541  sub _tree_construction_main ($) {
3541                my $i;                my $i;
3542                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3543                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3544                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3545                    !!!cp ('t241');                    !!!cp ('t241');
3546                    $i = $_;                    $i = $_;
3547                    last INSCOPE;                    last INSCOPE;
# Line 6069  sub _tree_construction_main ($) { Line 3571  sub _tree_construction_main ($) {
3571                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3572                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3573                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3574                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3575                  ## have an element in table scope                  ## have an element in table scope
3576                  my $i;                  my $i;
3577                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6098  sub _tree_construction_main ($) { Line 3600  sub _tree_construction_main ($) {
3600                  my $i;                  my $i;
3601                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3602                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3603                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3604                      !!!cp ('t250');                      !!!cp ('t250');
3605                      $i = $_;                      $i = $_;
3606                      last INSCOPE;                      last INSCOPE;
# Line 6188  sub _tree_construction_main ($) { Line 3690  sub _tree_construction_main ($) {
3690            #            #
3691          }          }
3692        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3693          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3694                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3695            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3696            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6203  sub _tree_construction_main ($) { Line 3705  sub _tree_construction_main ($) {
3705        } else {        } else {
3706          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3707        }        }
3708      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3709            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3710              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3711                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6230  sub _tree_construction_main ($) { Line 3732  sub _tree_construction_main ($) {
3732              }              }
3733            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3734              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3735                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3736                  !!!cp ('t264');                  !!!cp ('t264');
3737                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3738                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6256  sub _tree_construction_main ($) { Line 3758  sub _tree_construction_main ($) {
3758                #                #
3759              }              }
3760        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3761          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3762              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3763            !!!cp ('t270.2');            !!!cp ('t270.2');
3764            ## Stop parsing.            ## Stop parsing.
# Line 6274  sub _tree_construction_main ($) { Line 3776  sub _tree_construction_main ($) {
3776        }        }
3777    
3778            ## As if </colgroup>            ## As if </colgroup>
3779            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3780              !!!cp ('t269');              !!!cp ('t269');
3781  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3782              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6299  sub _tree_construction_main ($) { Line 3801  sub _tree_construction_main ($) {
3801          next B;          next B;
3802        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3803          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3804            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3805              !!!cp ('t272');              !!!cp ('t272');
3806              ## As if </option>              ## As if </option>
3807              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6312  sub _tree_construction_main ($) { Line 3814  sub _tree_construction_main ($) {
3814            !!!next-token;            !!!next-token;
3815            next B;            next B;
3816          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3817            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3818              !!!cp ('t274');              !!!cp ('t274');
3819              ## As if </option>              ## As if </option>
3820              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6320  sub _tree_construction_main ($) { Line 3822  sub _tree_construction_main ($) {
3822              !!!cp ('t275');              !!!cp ('t275');
3823            }            }
3824    
3825            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3826              !!!cp ('t276');              !!!cp ('t276');
3827              ## As if </optgroup>              ## As if </optgroup>
3828              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6333  sub _tree_construction_main ($) { Line 3835  sub _tree_construction_main ($) {
3835            !!!next-token;            !!!next-token;
3836            next B;            next B;
3837          } elsif ({          } elsif ({
3838                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3839                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3840                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3841                          == IN_SELECT_IN_TABLE_IM and
3842                    {                    {
3843                     caption => 1, table => 1,                     caption => 1, table => 1,
3844                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3845                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3846                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3847            ## TODO: The type below is not good - <select> is replaced by </select>  
3848            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3849                            token => $token);            if ($token->{tag_name} eq 'select') {
3850            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3851            ## as if there were </select> (otherwise).                                token => $token);
3852            ## have an element in table scope            } else {
3853                !!!parse-error (type => 'not closed', text => 'select',
3854                                token => $token);
3855              }
3856    
3857              ## 2./<select>-1. Unless "have an element in table scope" (select):
3858            my $i;            my $i;
3859            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3860              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3861              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3862                !!!cp ('t278');                !!!cp ('t278');
3863                $i = $_;                $i = $_;
3864                last INSCOPE;                last INSCOPE;
# Line 6361  sub _tree_construction_main ($) { Line 3869  sub _tree_construction_main ($) {
3869            } # INSCOPE            } # INSCOPE
3870            unless (defined $i) {            unless (defined $i) {
3871              !!!cp ('t280');              !!!cp ('t280');
3872              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3873                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3874              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3875                  ## case two errors, "select in select" and "unmatched
3876                  ## end tags" are reported to the user, the latter might
3877                  ## be confusing but this is what the spec requires.
3878                  !!!parse-error (type => 'unmatched end tag',
3879                                  text => 'select',
3880                                  token => $token);
3881                }
3882                ## Ignore the token.
3883              !!!nack ('t280.1');              !!!nack ('t280.1');
3884              !!!next-token;              !!!next-token;
3885              next B;              next B;
3886            }            }
3887    
3888              ## 3. Otherwise, as if there were <select>:
3889                                
3890            !!!cp ('t281');            !!!cp ('t281');
3891            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6384  sub _tree_construction_main ($) { Line 3902  sub _tree_construction_main ($) {
3902              ## Reprocess the token.              ## Reprocess the token.
3903              next B;              next B;
3904            }            }
3905            } elsif ($token->{tag_name} eq 'script') {
3906              !!!cp ('t281.3');
3907              ## NOTE: This is an "as if in head" code clone
3908              $script_start_tag->();
3909              next B;
3910          } else {          } else {
3911            !!!cp ('t282');            !!!cp ('t282');
3912            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6395  sub _tree_construction_main ($) { Line 3918  sub _tree_construction_main ($) {
3918          }          }
3919        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3920          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3921            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3922                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3923              !!!cp ('t283');              !!!cp ('t283');
3924              ## As if </option>              ## As if </option>
3925              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3926            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3927              !!!cp ('t284');              !!!cp ('t284');
3928              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3929            } else {            } else {
# Line 6413  sub _tree_construction_main ($) { Line 3936  sub _tree_construction_main ($) {
3936            !!!next-token;            !!!next-token;
3937            next B;            next B;
3938          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3939            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3940              !!!cp ('t286');              !!!cp ('t286');
3941              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3942            } else {            } else {
# Line 6430  sub _tree_construction_main ($) { Line 3953  sub _tree_construction_main ($) {
3953            my $i;            my $i;
3954            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3955              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3956              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3957                !!!cp ('t288');                !!!cp ('t288');
3958                $i = $_;                $i = $_;
3959                last INSCOPE;                last INSCOPE;
# Line 6457  sub _tree_construction_main ($) { Line 3980  sub _tree_construction_main ($) {
3980            !!!nack ('t291.1');            !!!nack ('t291.1');
3981            !!!next-token;            !!!next-token;
3982            next B;            next B;
3983          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3984                         == IN_SELECT_IN_TABLE_IM and
3985                   {                   {
3986                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3987                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6492  sub _tree_construction_main ($) { Line 4016  sub _tree_construction_main ($) {
4016            undef $i;            undef $i;
4017            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4018              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4019              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4020                !!!cp ('t295');                !!!cp ('t295');
4021                $i = $_;                $i = $_;
4022                last INSCOPE;                last INSCOPE;
# Line 6531  sub _tree_construction_main ($) { Line 4055  sub _tree_construction_main ($) {
4055            next B;            next B;
4056          }          }
4057        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4058          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4059                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4060            !!!cp ('t299.1');            !!!cp ('t299.1');
4061            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6718  sub _tree_construction_main ($) { Line 4242  sub _tree_construction_main ($) {
4242        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4243          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4244              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4245            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4246                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4247              !!!cp ('t325');              !!!cp ('t325');
4248              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6732  sub _tree_construction_main ($) { Line 4256  sub _tree_construction_main ($) {
4256            }            }
4257    
4258            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4259                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4260              !!!cp ('t327');              !!!cp ('t327');
4261              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4262            } else {            } else {
# Line 6764  sub _tree_construction_main ($) { Line 4288  sub _tree_construction_main ($) {
4288            next B;            next B;
4289          }          }
4290        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4291          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4292                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4293            !!!cp ('t331.1');            !!!cp ('t331.1');
4294            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6777  sub _tree_construction_main ($) { Line 4301  sub _tree_construction_main ($) {
4301        } else {        } else {
4302          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4303        }        }
   
       ## ISSUE: An issue in spec here  
4304      } else {      } else {
4305        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4306      }      }
# Line 6796  sub _tree_construction_main ($) { Line 4318  sub _tree_construction_main ($) {
4318          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4319          next B;          next B;
4320        } elsif ({        } elsif ({
4321                  base => 1, link => 1,                  base => 1, command => 1, link => 1,
4322                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4323          !!!cp ('t334');          !!!cp ('t334');
4324          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
4325          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4326          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
4327          !!!ack ('t334.1');          !!!ack ('t334.1');
4328          !!!next-token;          !!!next-token;
4329          next B;          next B;
4330        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
4331          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
4332          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4333          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
4334    
4335          unless ($self->{confident}) {          unless ($self->{confident}) {
4336            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
# Line 6869  sub _tree_construction_main ($) { Line 4391  sub _tree_construction_main ($) {
4391          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4392                                
4393          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4394              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4395            !!!cp ('t342');            !!!cp ('t342');
4396            ## Ignore the token            ## Ignore the token
4397          } else {          } else {
# Line 6887  sub _tree_construction_main ($) { Line 4409  sub _tree_construction_main ($) {
4409          !!!next-token;          !!!next-token;
4410          next B;          next B;
4411        } elsif ({        } elsif ({
4412                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
4413                  div => 1, dl => 1, fieldset => 1,  
4414                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
4415                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
4416                    center => 1, datagrid => 1, details => 1, dialog => 1,
4417                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
4418                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
4419                    h6 => 1, header => 1, hgroup => 1,
4420                    menu => 1, nav => 1, ol => 1, p => 1,
4421                    section => 1, ul => 1,
4422                    ## NOTE: As normal, but drops leading newline
4423                  pre => 1, listing => 1,                  pre => 1, listing => 1,
4424                    ## NOTE: As normal, but interacts with the form element pointer
4425                  form => 1,                  form => 1,
4426                    
4427                  table => 1,                  table => 1,
4428                  hr => 1,                  hr => 1,
4429                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4430    
4431            ## 1. When there is an opening |form| element:
4432          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4433            !!!cp ('t350');            !!!cp ('t350');
4434            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6905  sub _tree_construction_main ($) { Line 4438  sub _tree_construction_main ($) {
4438            next B;            next B;
4439          }          }
4440    
4441          ## has a p element in scope          ## 2. Close the |p| element, if any.
4442          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4443            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4444              !!!cp ('t344');            ## has a p element in scope
4445              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4446              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4447                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4448              next B;                !!!back-token; # <form>
4449            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4450              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4451              last INSCOPE;                next B;
4452                } elsif ($_->[1] & SCOPING_EL) {
4453                  !!!cp ('t345');
4454                  last INSCOPE;
4455                }
4456              } # INSCOPE
4457            }
4458    
4459            ## 3. Close the opening <hn> element, if any.
4460            if ({h1 => 1, h2 => 1, h3 => 1,
4461                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4462              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4463                !!!parse-error (type => 'not closed',
4464                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4465                                token => $token);
4466                pop @{$self->{open_elements}};
4467            }            }
4468          } # INSCOPE          }
4469              
4470            ## 4. Insertion.
4471          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4472          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4473            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6959  sub _tree_construction_main ($) { Line 4508  sub _tree_construction_main ($) {
4508            !!!next-token;            !!!next-token;
4509          }          }
4510          next B;          next B;
4511        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
4512          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
4513    
4514            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4515              ## Interpreted as <li><foo/></li><li/> (non-conforming):
4516              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4517              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
4518              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4519              ## object (Fx)
4520              ## Generate non-tree (non-conforming):
4521              ## basefont (IE7 (where basefont is non-void)), center (IE),
4522              ## form (IE), hn (IE)
4523            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4524              ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4525              ## div (Fx, S)
4526    
4527            my $non_optional;
4528            my $i = -1;
4529    
4530            ## 1.
4531            for my $node (reverse @{$self->{open_elements}}) {
4532              if ($node->[1] == LI_EL) {
4533                ## 2. (a) As if </li>
4534                {
4535                  ## If no </li> - not applied
4536                  #
4537    
4538                  ## Otherwise
4539    
4540                  ## 1. generate implied end tags, except for </li>
4541                  #
4542    
4543                  ## 2. If current node != "li", parse error
4544                  if ($non_optional) {
4545                    !!!parse-error (type => 'not closed',
4546                                    text => $non_optional->[0]->manakai_local_name,
4547                                    token => $token);
4548                    !!!cp ('t355');
4549                  } else {
4550                    !!!cp ('t356');
4551                  }
4552    
4553                  ## 3. Pop
4554                  splice @{$self->{open_elements}}, $i;
4555                }
4556    
4557                last; ## 2. (b) goto 5.
4558              } elsif (
4559                       ## NOTE: not "formatting" and not "phrasing"
4560                       ($node->[1] & SPECIAL_EL or
4561                        $node->[1] & SCOPING_EL) and
4562                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4563                       (not $node->[1] & ADDRESS_DIV_P_EL)
4564                      ) {
4565                ## 3.
4566                !!!cp ('t357');
4567                last; ## goto 5.
4568              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4569                !!!cp ('t358');
4570                #
4571              } else {
4572                !!!cp ('t359');
4573                $non_optional ||= $node;
4574                #
4575              }
4576              ## 4.
4577              ## goto 2.
4578              $i--;
4579            }
4580    
4581            ## 5. (a) has a |p| element in scope
4582          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4583            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4584              !!!cp ('t353');              !!!cp ('t353');
4585    
4586                ## NOTE: |<p><li>|, for example.
4587    
4588              !!!back-token; # <x>              !!!back-token; # <x>
4589              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
4590                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6974  sub _tree_construction_main ($) { Line 4595  sub _tree_construction_main ($) {
4595            }            }
4596          } # INSCOPE          } # INSCOPE
4597    
4598          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## 5. (b) insert
4599            ## Interpreted as <li><foo/></li><li/> (non-conforming)          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4600            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),          !!!nack ('t359.1');
4601            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),          !!!next-token;
4602            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),          next B;
4603            ## object (Fx)        } elsif ($token->{tag_name} eq 'dt' or
4604            ## Generate non-tree (non-conforming)                 $token->{tag_name} eq 'dd') {
4605            ## basefont (IE7 (where basefont is non-void)), center (IE),          ## NOTE: As normal, but imply </dt> or </dd> when ...
4606            ## form (IE), hn (IE)  
4607          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          my $non_optional;
           ## Interpreted as <li><foo><li/></foo></li> (non-conforming)  
           ## div (Fx, S)  
             
         ## Step 1  
4608          my $i = -1;          my $i = -1;
4609          my $node = $self->{open_elements}->[$i];  
4610          my $li_or_dtdd = {li => {li => 1},          ## 1.
4611                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
4612                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] == DTDD_EL) {
4613          LI: {              ## 2. (a) As if </li>
4614            ## Step 2              {
4615            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
4616              if ($i != -1) {                #
4617                !!!cp ('t355');  
4618                !!!parse-error (type => 'not closed',                ## Otherwise
4619                                text => $self->{open_elements}->[-1]->[0]  
4620                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
4621                                token => $token);                #
4622              } else {  
4623                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
4624                  if ($non_optional) {
4625                    !!!parse-error (type => 'not closed',
4626                                    text => $non_optional->[0]->manakai_local_name,
4627                                    token => $token);
4628                    !!!cp ('t355.1');
4629                  } else {
4630                    !!!cp ('t356.1');
4631                  }
4632    
4633                  ## 3. Pop
4634                  splice @{$self->{open_elements}}, $i;
4635              }              }
4636              splice @{$self->{open_elements}}, $i;  
4637              last LI;              last; ## 2. (b) goto 5.
4638              } elsif (
4639                       ## NOTE: not "formatting" and not "phrasing"
4640                       ($node->[1] & SPECIAL_EL or
4641                        $node->[1] & SCOPING_EL) and
4642                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4643    
4644                       (not $node->[1] & ADDRESS_DIV_P_EL)
4645                      ) {
4646                ## 3.
4647                !!!cp ('t357.1');
4648                last; ## goto 5.
4649              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4650                !!!cp ('t358.1');
4651                #
4652            } else {            } else {
4653              !!!cp ('t357');              !!!cp ('t359.1');
4654            }              $non_optional ||= $node;
4655                          #
           ## Step 3  
           if (not ($node->[1] & FORMATTING_EL) and  
               #not $phrasing_category->{$node->[1]} and  
               ($node->[1] & SPECIAL_EL or  
                $node->[1] & SCOPING_EL) and  
               not ($node->[1] & ADDRESS_EL) and  
               not ($node->[1] & DIV_EL)) {  
             !!!cp ('t358');  
             last LI;  
4656            }            }
4657                        ## 4.
4658            !!!cp ('t359');            ## goto 2.
           ## Step 4  
4659            $i--;            $i--;
4660            $node = $self->{open_elements}->[$i];          }
4661            redo LI;  
4662          } # LI          ## 5. (a) has a |p| element in scope
4663                      INSCOPE: for (reverse @{$self->{open_elements}}) {
4664              if ($_->[1] == P_EL) {
4665                !!!cp ('t353.1');
4666                !!!back-token; # <x>
4667                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4668                          line => $token->{line}, column => $token->{column}};
4669                next B;
4670              } elsif ($_->[1] & SCOPING_EL) {
4671                !!!cp ('t354.1');
4672                last INSCOPE;
4673              }
4674            } # INSCOPE
4675    
4676            ## 5. (b) insert
4677          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4678          !!!nack ('t359.1');          !!!nack ('t359.2');
4679          !!!next-token;          !!!next-token;
4680          next B;          next B;
4681        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
4682            ## NOTE: As normal, but effectively ends parsing
4683    
4684          ## has a p element in scope          ## has a p element in scope
4685          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4686            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4687              !!!cp ('t367');              !!!cp ('t367');
4688              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4689              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7058  sub _tree_construction_main ($) { Line 4705  sub _tree_construction_main ($) {
4705        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4706          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4707            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4708            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4709              !!!cp ('t371');              !!!cp ('t371');
4710              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4711                            
# Line 7102  sub _tree_construction_main ($) { Line 4749  sub _tree_construction_main ($) {
4749          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4750          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4751            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4752            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4753              !!!cp ('t376');              !!!cp ('t376');
4754              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4755              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7125  sub _tree_construction_main ($) { Line 4772  sub _tree_construction_main ($) {
4772          ## has a button element in scope          ## has a button element in scope
4773          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4774            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4775            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4776              !!!cp ('t378');              !!!cp ('t378');
4777              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4778              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7190  sub _tree_construction_main ($) { Line 4837  sub _tree_construction_main ($) {
4837                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4838                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4839                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
                         {type => START_TAG_TOKEN, tag_name => 'p',  
                          line => $token->{line}, column => $token->{column}},  
4840                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4841                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4842                         );                         );
# Line 7214  sub _tree_construction_main ($) { Line 4859  sub _tree_construction_main ($) {
4859                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4860                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4861                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
                         {type => END_TAG_TOKEN, tag_name => 'p',  
                          line => $token->{line}, column => $token->{column}},  
4862                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4863                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4864                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7225  sub _tree_construction_main ($) { Line 4868  sub _tree_construction_main ($) {
4868            next B;            next B;
4869          }          }
4870        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4871          my $tag_name = $token->{tag_name};          ## 1. Insert
4872          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4873                    
4874            ## Step 2 # XXX
4875          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4876    
4877            ## 2. Drop U+000A LINE FEED
4878            $self->{ignore_newline} = 1;
4879    
4880            ## 3. RCDATA
4881          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4882          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4883            
4884          $insert->($el);          ## 4., 6. Insertion mode
4885                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4886          my $text = '';  
4887            ## XXX: 5. frameset-ok flag
4888    
4889          !!!nack ('t392.1');          !!!nack ('t392.1');
4890          !!!next-token;          !!!next-token;
4891          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4892            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4893            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4894              !!!cp ('t392');          ## has an |option| element in scope
4895              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4896            } else {            my $node = $self->{open_elements}->[$_];
4897              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4898                !!!cp ('t397.1');
4899                ## NOTE: As if </option>
4900                !!!back-token; # <option> or <optgroup>
4901                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4902                          line => $token->{line}, column => $token->{column}};
4903                next B;
4904              } elsif ($node->[1] & SCOPING_EL) {
4905                !!!cp ('t397.2');
4906                last INSCOPE;
4907            }            }
4908          } else {          } # INSCOPE
4909            !!!cp ('t394');  
4910          }          $reconstruct_active_formatting_elements->($insert_to_current);
4911          while ($token->{type} == CHARACTER_TOKEN) {  
4912            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4913            $text .= $token->{data};  
4914            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
4915          !!!next-token;          !!!next-token;
4916          next B;          redo B;
4917        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4918                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4919          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4920          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4921            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4922            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4923              !!!cp ('t398.1');              !!!cp ('t398.1');
4924              ## generate implied end tags              ## generate implied end tags
4925              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4926                !!!cp ('t398.2');                !!!cp ('t398.2');
4927                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4928              }              }
4929              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4930                !!!cp ('t398.3');                !!!cp ('t398.3');
4931                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4932                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4933                                    ->manakai_local_name,                                    ->manakai_local_name,
4934                                token => $token);                                token => $token);
4935                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4936                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4937              }              }
4938              last INSCOPE;              last INSCOPE;
4939            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7298  sub _tree_construction_main ($) { Line 4941  sub _tree_construction_main ($) {
4941              last INSCOPE;              last INSCOPE;
4942            }            }
4943          } # INSCOPE          } # INSCOPE
4944              
4945            ## TODO: <non-ruby><rt> is not allowed.
4946    
4947          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4948    
# Line 7318  sub _tree_construction_main ($) { Line 4963  sub _tree_construction_main ($) {
4963                    
4964          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4965            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4966            !!!ack ('t398.1');            !!!ack ('t398.6');
4967          } else {          } else {
4968            !!!cp ('t398.2');            !!!cp ('t398.7');
4969            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
4970            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
4971            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7331  sub _tree_construction_main ($) { Line 4976  sub _tree_construction_main ($) {
4976          next B;          next B;
4977        } elsif ({        } elsif ({
4978                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4979                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
4980                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
4981                  thead => 1, tr => 1,                  thead => 1, tr => 1,
4982                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7342  sub _tree_construction_main ($) { Line 4987  sub _tree_construction_main ($) {
4987          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
4988          !!!next-token;          !!!next-token;
4989          next B;          next B;
4990                  } elsif ($token->{tag_name} eq 'param' or
4991          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
4992            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4993            pop @{$self->{open_elements}};
4994    
4995            !!!ack ('t398.5');
4996            !!!next-token;
4997            redo B;
4998        } else {        } else {
4999          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
5000            !!!cp ('t384');            !!!cp ('t384');
# Line 7379  sub _tree_construction_main ($) { Line 5030  sub _tree_construction_main ($) {
5030            !!!ack ('t388.2');            !!!ack ('t388.2');
5031          } elsif ({          } elsif ({
5032                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5033                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
5034                    #image => 1,                    keygen => 1,
5035                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5036            !!!cp ('t388.1');            !!!cp ('t388.1');
5037            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7390  sub _tree_construction_main ($) { Line 5041  sub _tree_construction_main ($) {
5041                    
5042            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5043                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5044                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5045              !!!cp ('t400.1');              !!!cp ('t400.1');
5046              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5047            } else {            } else {
# Line 7407  sub _tree_construction_main ($) { Line 5058  sub _tree_construction_main ($) {
5058        }        }
5059      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5060        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5061          ## has a |body| element in scope  
5062            ## 1. If not "have an element in scope":
5063            ## "has a |body| element in scope"
5064          my $i;          my $i;
5065          INSCOPE: {          INSCOPE: {
5066            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5067              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5068                !!!cp ('t405');                !!!cp ('t405');
5069                $i = $_;                $i = $_;
5070                last INSCOPE;                last INSCOPE;
# Line 7421  sub _tree_construction_main ($) { Line 5074  sub _tree_construction_main ($) {
5074              }              }
5075            }            }
5076    
5077            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
5078    
5079              !!!parse-error (type => 'unmatched end tag',
5080                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5081            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
5082            !!!next-token;            !!!next-token;
5083            next B;            next B;
5084          } # INSCOPE          } # INSCOPE
5085    
5086            ## 2. If unclosed elements:
5087          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5088            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5089                      $_->[1] == OPTGROUP_EL ||
5090                      $_->[1] == OPTION_EL ||
5091                      $_->[1] == RUBY_COMPONENT_EL) {
5092              !!!cp ('t403');              !!!cp ('t403');
5093              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5094                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7440  sub _tree_construction_main ($) { Line 5099  sub _tree_construction_main ($) {
5099            }            }
5100          }          }
5101    
5102            ## 3. Switch the insertion mode.
5103          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5104          !!!next-token;          !!!next-token;
5105          next B;          next B;
# Line 7447  sub _tree_construction_main ($) { Line 5107  sub _tree_construction_main ($) {
5107          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5108          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5109          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5110              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5111            ## ISSUE: There is an issue in the spec.            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
5112              !!!cp ('t406');              !!!cp ('t406');
5113              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5114                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7470  sub _tree_construction_main ($) { Line 5129  sub _tree_construction_main ($) {
5129            next B;            next B;
5130          }          }
5131        } elsif ({        } elsif ({
5132                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
5133                  div => 1, dl => 1, fieldset => 1, listing => 1,  
5134                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
5135                    address => 1, article => 1, aside => 1, blockquote => 1,
5136                    center => 1, datagrid => 1, details => 1, dialog => 1,
5137                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
5138                    footer => 1, header => 1, hgroup => 1,
5139                    listing => 1, menu => 1, nav => 1,
5140                    ol => 1, pre => 1, section => 1, ul => 1,
5141    
5142                    ## NOTE: As normal, but ... optional tags
5143                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
5144    
5145                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
5146                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5147            ## NOTE: Code for <li> start tags includes "as if </li>" code.
5148            ## Code for <dt> or <dd> start tags includes "as if </dt> or
5149            ## </dd>" code.
5150    
5151          ## has an element in scope          ## has an element in scope
5152          my $i;          my $i;
5153          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 7502  sub _tree_construction_main ($) { Line 5174  sub _tree_construction_main ($) {
5174                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
5175                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
5176                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
5177                      option => 1,
5178                      optgroup => 1,
5179                    p => 1,                    p => 1,
5180                    rt => 1,                    rt => 1,
5181                    rp => 1,                    rp => 1,
# Line 7534  sub _tree_construction_main ($) { Line 5208  sub _tree_construction_main ($) {
5208          !!!next-token;          !!!next-token;
5209          next B;          next B;
5210        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
5211            ## NOTE: As normal, but interacts with the form element pointer
5212    
5213          undef $self->{form_element};          undef $self->{form_element};
5214    
5215          ## has an element in scope          ## has an element in scope
5216          my $i;          my $i;
5217          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5218            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5219            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5220              !!!cp ('t418');              !!!cp ('t418');
5221              $i = $_;              $i = $_;
5222              last INSCOPE;              last INSCOPE;
# Line 7581  sub _tree_construction_main ($) { Line 5257  sub _tree_construction_main ($) {
5257          !!!next-token;          !!!next-token;
5258          next B;          next B;
5259        } elsif ({        } elsif ({
5260                    ## NOTE: As normal, except acts as a closer for any ...
5261                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5262                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5263          ## has an element in scope          ## has an element in scope
5264          my $i;          my $i;
5265          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5266            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5267            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5268              !!!cp ('t423');              !!!cp ('t423');
5269              $i = $_;              $i = $_;
5270              last INSCOPE;              last INSCOPE;
# Line 7626  sub _tree_construction_main ($) { Line 5303  sub _tree_construction_main ($) {
5303          !!!next-token;          !!!next-token;
5304          next B;          next B;
5305        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
5306            ## NOTE: As normal, except </p> implies <p> and ...
5307    
5308          ## has an element in scope          ## has an element in scope
5309            my $non_optional;
5310          my $i;          my $i;
5311          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5312            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5313            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5314              !!!cp ('t410.1');              !!!cp ('t410.1');
5315              $i = $_;              $i = $_;
5316              last INSCOPE;              last INSCOPE;
5317            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
5318              !!!cp ('t411.1');              !!!cp ('t411.1');
5319              last INSCOPE;              last INSCOPE;
5320              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
5321                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
5322                !!!cp ('t411.2');
5323                #
5324              } else {
5325                !!!cp ('t411.3');
5326                $non_optional ||= $node;
5327                #
5328            }            }
5329          } # INSCOPE          } # INSCOPE
5330    
5331          if (defined $i) {          if (defined $i) {
5332            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
5333                    ne $token->{tag_name}) {            #
5334    
5335              ## 2. If current node != "p", parse error
5336              if ($non_optional) {
5337              !!!cp ('t412.1');              !!!cp ('t412.1');
5338              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5339                              text => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
5340                              token => $token);                              token => $token);
5341            } else {            } else {
5342              !!!cp ('t414.1');              !!!cp ('t414.1');
5343            }            }
5344    
5345              ## 3. Pop
5346            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5347          } else {          } else {
5348            !!!cp ('t413.1');            !!!cp ('t413.1');
# Line 7692  sub _tree_construction_main ($) { Line 5383  sub _tree_construction_main ($) {
5383          ## Ignore the token.          ## Ignore the token.
5384          !!!next-token;          !!!next-token;
5385          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
5386        } else {        } else {
5387            if ($token->{tag_name} eq 'sarcasm') {
5388              sleep 0.001; # take a deep breath
5389            }
5390    
5391          ## Step 1          ## Step 1
5392          my $node_i = -1;          my $node_i = -1;
5393          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
5394    
5395          ## Step 2          ## Step 2
5396          S2: {          S2: {
5397            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5398              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5399              if ($node_tag_name eq $token->{tag_name}) {
5400              ## Step 1              ## Step 1
5401              ## generate implied end tags              ## generate implied end tags
5402              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7733  sub _tree_construction_main ($) { Line 5409  sub _tree_construction_main ($) {
5409              }              }
5410                    
5411              ## Step 2              ## Step 2
5412              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5413                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5414                $current_tag_name =~ tr/A-Z/a-z/;
5415                if ($current_tag_name ne $token->{tag_name}) {
5416                !!!cp ('t431');                !!!cp ('t431');
5417                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5418                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7809  sub _tree_construction_main ($) { Line 5487  sub _tree_construction_main ($) {
5487    ## TODO: script stuffs    ## TODO: script stuffs
5488  } # _tree_construct_main  } # _tree_construct_main
5489    
5490    ## XXX: How this method is organized is somewhat out of date, although
5491    ## it still does what the current spec documents.
5492  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5493    my $class = shift;    my $class = shift;
5494    my $node = shift;    my $node = shift; # /context/
5495    #my $s = \$_[0];    #my $s = \$_[0];
5496    my $onerror = $_[1];    my $onerror = $_[1];
5497    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
5498    
   ## ISSUE: Should {confident} be true?  
   
5499    my $nt = $node->node_type;    my $nt = $node->node_type;
5500    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5501      # MUST      # MUST
5502            
5503      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7834  sub set_inner_html ($$$$;$) { Line 5512  sub set_inner_html ($$$$;$) {
5512    
5513      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5514      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5515    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5516      ## TODO: If non-html element      ## TODO: If non-html element
5517    
5518      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5519    
5520  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5521    
5522      ## Step 1 # MUST      ## F1. Create an HTML document.
5523      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5524      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5525      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5526    
5527        ## F2. Propagate quirkness flag
5528        my $node_doc = $node->owner_document;
5529        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5530    
5531        ## F3. Create an HTML parser
5532      my $p = $class->new;      my $p = $class->new;
5533      $p->{document} = $doc;      $p->{document} = $doc;
5534    
# Line 7972  sub set_inner_html ($$$$;$) { Line 5656  sub set_inner_html ($$$$;$) {
5656      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5657      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5658    
5659      ## Step 2      ## F4. If /context/ is not undef...
5660    
5661        ## F4.1. content model flag
5662      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5663      $p->{content_model} = {      $p->{content_model} = {
5664        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 7988  sub set_inner_html ($$$$;$) { Line 5674  sub set_inner_html ($$$$;$) {
5674      }->{$node_ln};      }->{$node_ln};
5675      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5676          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5677    
5678      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5679        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5680    
5681      ## Step 3      ## F4.2. Root |html| element
5682      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5683        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5684    
5685      ## Step 4 # MUST      ## F4.3.
5686      $doc->append_child ($root);      $doc->append_child ($root);
5687    
5688      ## Step 5 # MUST      ## F4.4.
5689      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5690    
5691      undef $p->{head_element};      undef $p->{head_element};
5692        undef $p->{head_element_inserted};
5693    
5694      ## Step 6 # MUST      ## F4.5.
5695      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5696    
5697      ## Step 7 # MUST      ## F4.6.
5698      my $anode = $node;      my $anode = $node;
5699      AN: while (defined $anode) {      AN: while (defined $anode) {
5700        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8023  sub set_inner_html ($$$$;$) { Line 5709  sub set_inner_html ($$$$;$) {
5709        }        }
5710        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5711      } # AN      } # AN
5712        
5713      ## Step 9 # MUST      ## F.5. Set the input stream.
5714        $p->{confident} = 1; ## Confident: irrelevant.
5715    
5716        ## F.6. Start the parser.
5717      {      {
5718        my $self = $p;        my $self = $p;
5719        !!!next-token;        !!!next-token;
5720      }      }
5721      $p->_tree_construction_main;      $p->_tree_construction_main;
5722    
5723      ## Step 10 # MUST      ## F.7.
5724      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5725      for (@cn) {      for (@cn) {
5726        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.193  
changed lines
  Added in v.1.237

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24