/[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.197 by wakaba, Sat Oct 4 08:29:19 2008 UTC revision 1.241 by wakaba, Sun Sep 6 12:53:19 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
36  sub ADDRESS_EL () { 0b10 }  
37  sub BODY_EL () { 0b100 }  ## Bits 12-15
38  sub BUTTON_EL () { 0b1000 }  sub SPECIAL_EL () { 0b1_000000000000000 }
39  sub CAPTION_EL () { 0b10000 }  sub SCOPING_EL () { 0b1_00000000000000 }
40  sub DD_EL () { 0b100000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub DIV_EL () { 0b1000000 }  sub PHRASING_EL () { 0b1_000000000000 }
42  sub DT_EL () { 0b10000000 }  
43  sub FORM_EL () { 0b100000000 }  ## Bits 10-11
44  sub FORMATTING_EL () { 0b1000000000 }  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45  sub FRAMESET_EL () { 0b10000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46  sub HEADING_EL () { 0b100000000000 }  
47  sub HTML_EL () { 0b1000000000000 }  ## Bits 6-9
48  sub LI_EL () { 0b10000000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
49  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50  sub OPTION_EL () { 0b1000000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51  sub OPTGROUP_EL () { 0b10000000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
52  sub P_EL () { 0b100000000000000000 }  
53  sub SELECT_EL () { 0b1000000000000000000 }  ## Bit 5
54  sub TABLE_EL () { 0b10000000000000000000 }  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55  sub TABLE_CELL_EL () { 0b100000000000000000000 }  
56  sub TABLE_ROW_EL () { 0b1000000000000000000000 }  ## NOTE: Used in </body> and EOF algorithms.
57  sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  ## Bit 4
58  sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
 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  
 }  
59    
60  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
61  ## NOTE: There is a code where a modified version of  ## NOTE: There is a code where a modified version of
62  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63  ## implementation (search for the algorithm name).  ## implementation (search for the algorithm name).
64  sub END_TAG_OPTIONAL_EL () {  ## Bit 3
65    DD_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
   DT_EL |  
   LI_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
66    
67  ## NOTE: Used in </body> and EOF algorithms.  ## Bits 0-2
 sub ALL_END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
68    
69  sub SCOPING_EL () {  sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    BUTTON_EL |  sub FORM_EL () { SPECIAL_EL | 0b001 }
71    CAPTION_EL |  sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    HTML_EL |  sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    TABLE_EL |  sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    TABLE_CELL_EL |  sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    MISC_SCOPING_EL  
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 160  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 185  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 193  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 200  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 237  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 338  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 488  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 560  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 581  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 674  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 835  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 915  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 945  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 3450  sub _initialize_tree_constructor ($) { Line 869  sub _initialize_tree_constructor ($) {
869    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
870    $self->{document}->set_user_data (manakai_source_line => 1);    $self->{document}->set_user_data (manakai_source_line => 1);
871    $self->{document}->set_user_data (manakai_source_column => 1);    $self->{document}->set_user_data (manakai_source_column => 1);
872    
873      $self->{frameset_ok} = 1;
874  } # _initialize_tree_constructor  } # _initialize_tree_constructor
875    
876  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 3469  sub _construct_tree ($) { Line 890  sub _construct_tree ($) {
890    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
891    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
892    ## 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  
893        
894    !!!next-token;    !!!next-token;
895    
896    undef $self->{form_element};    undef $self->{form_element};
897    undef $self->{head_element};    undef $self->{head_element};
898      undef $self->{head_element_inserted};
899    $self->{open_elements} = [];    $self->{open_elements} = [];
900    undef $self->{inner_html_node};    undef $self->{inner_html_node};
901      undef $self->{ignore_newline};
902    
903    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
904    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3499  sub _tree_construction_initial ($) { Line 918  sub _tree_construction_initial ($) {
918    
919    INITIAL: {    INITIAL: {
920      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
921        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
922        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
923        ## language.        ## another language.  (We don't support such mode switchings; it
924          ## is nonsense to do anything different from what browsers do.)
925        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
926        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
927        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        my $doctype = $self->{document}->create_document_type_definition
928        if (not defined $token->{name} or # <!DOCTYPE>            ($doctype_name);
929            defined $token->{sysid}) {  
930          $doctype_name =~ tr/A-Z/a-z/; # ASCII case-insensitive
931          if ($doctype_name ne 'html') {
932          !!!cp ('t1');          !!!cp ('t1');
933          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
934        } elsif ($doctype_name ne 'HTML') {        } elsif (defined $token->{pubid}) {
935          !!!cp ('t2');          !!!cp ('t2');
936            ## XXX Obsolete permitted DOCTYPEs
937          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
938        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{sysid}) {
939          if ($token->{pubid} eq 'XSLT-compat') {          if ($token->{sysid} eq 'about:legacy-compat') {
940            !!!cp ('t1.2');            !!!cp ('t1.2'); ## <!DOCTYPE HTML SYSTEM "about:legacy-compat">
941            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
942                            level => $self->{level}->{should});                            level => $self->{level}->{should});
943          } else {          } else {
944            !!!parse-error (type => 'not HTML5', token => $token);            !!!parse-error (type => 'not HTML5', token => $token);
945          }          }
946        } else {        } else { ## <!DOCTYPE HTML>
947          !!!cp ('t3');          !!!cp ('t3');
948          #          #
949        }        }
950                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
951        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
952        ## 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.
953        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
954        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
955    
956        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
957        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
958          ## string, while |null| is an allowed value for the attribute
959          ## according to DOM3 Core.
960        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
961                
962        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'html') {
963          !!!cp ('t4');          !!!cp ('t4');
964          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
965        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{pubid}) {
# Line 3785  sub _tree_construction_root_element ($) Line 1209  sub _tree_construction_root_element ($)
1209      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
1210      !!!ack-later;      !!!ack-later;
1211      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
1212    } # B    } # B
1213    
1214    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3822  sub _reset_insertion_mode ($) { Line 1244  sub _reset_insertion_mode ($) {
1244          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1245          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1246          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1247        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1248          if ($last) {          if ($last) {
1249            !!!cp ('t28.2');            !!!cp ('t28.2');
1250            #            #
# Line 3851  sub _reset_insertion_mode ($) { Line 1273  sub _reset_insertion_mode ($) {
1273        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1274                
1275        ## Step 15        ## Step 15
1276        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1277          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1278            !!!cp ('t29');            !!!cp ('t29');
1279            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3983  sub _tree_construction_main ($) { Line 1405  sub _tree_construction_main ($) {
1405    
1406      ## Step 1      ## Step 1
1407      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1408      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1409    
1410      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1411      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1412      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1413    
1414      ## Step 4      ## Step 3, 4
1415      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;  
1416    
1417      ## 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";  
       }  
     }  
1418      !!!next-token;      !!!next-token;
1419    }; # $parse_rcdata    }; # $parse_rcdata
1420    
1421    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1422        ## Step 1
1423      my $script_el;      my $script_el;
1424      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1425    
1426        ## Step 2
1427      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1428    
1429        ## Step 3
1430        ## TODO: Mark as "already executed", if ...
1431    
1432        ## Step 4 (HTML5 revision 2702)
1433        $insert->($script_el);
1434        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1435    
1436        ## Step 5
1437      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1438      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;  
1439    
1440      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1441          $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  
1442    
1443        $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...  
     }  
       
1444      !!!next-token;      !!!next-token;
1445    }; # $script_start_tag    }; # $script_start_tag
1446    
1447    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1448    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag (OBSOLETE; unused).
1449      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1450    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1451    
1452    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4171  sub _tree_construction_main ($) { Line 1531  sub _tree_construction_main ($) {
1531            !!!cp ('t59');            !!!cp ('t59');
1532            $furthest_block = $node;            $furthest_block = $node;
1533            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1534              ## NOTE: The topmost (eldest) node.
1535          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1536            !!!cp ('t60');            !!!cp ('t60');
1537            last OE;            last OE;
# Line 4254  sub _tree_construction_main ($) { Line 1615  sub _tree_construction_main ($) {
1615                
1616        ## Step 8        ## Step 8
1617        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
1618            ## Foster parenting.
1619          my $foster_parent_element;          my $foster_parent_element;
1620          my $next_sibling;          my $next_sibling;
1621          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1622            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1623                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;              !!!cp ('t65.2');
1624                               if (defined $parent and $parent->node_type == 1) {              $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1625                                 !!!cp ('t65.1');              $next_sibling = $self->{open_elements}->[$_]->[0];
1626                                 $foster_parent_element = $parent;              undef $next_sibling
1627                                 $next_sibling = $self->{open_elements}->[$_]->[0];                  unless $next_sibling->parent_node eq $foster_parent_element;
1628                               } else {              last OE;
1629                                 !!!cp ('t65.2');            }
1630                                 $foster_parent_element          } # OE
1631                                   = $self->{open_elements}->[$_ - 1]->[0];          $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1632                               }  
                              last OE;  
                            }  
                          } # OE  
                          $foster_parent_element = $self->{open_elements}->[0]->[0]  
                            unless defined $foster_parent_element;  
1633          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
1634          $open_tables->[-1]->[1] = 1; # tainted          $open_tables->[-1]->[1] = 1; # tainted
1635        } else {        } else {
# Line 4317  sub _tree_construction_main ($) { Line 1674  sub _tree_construction_main ($) {
1674            $i = $_;            $i = $_;
1675          }          }
1676        } # OE        } # OE
1677        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1678                
1679        ## Step 14        ## Step 14
1680        redo FET;        redo FET;
# Line 4328  sub _tree_construction_main ($) { Line 1685  sub _tree_construction_main ($) {
1685      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
1686    }; # $insert_to_current    }; # $insert_to_current
1687    
1688      ## Foster parenting.  Note that there are three "foster parenting"
1689      ## code in the parser: for elements (this one), for texts, and for
1690      ## elements in the AAA code.
1691    my $insert_to_foster = sub {    my $insert_to_foster = sub {
1692      my $child = shift;      my $child = shift;
1693      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
# Line 4335  sub _tree_construction_main ($) { Line 1695  sub _tree_construction_main ($) {
1695        my $foster_parent_element;        my $foster_parent_element;
1696        my $next_sibling;        my $next_sibling;
1697        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1698          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1699                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;            !!!cp ('t71');
1700                               if (defined $parent and $parent->node_type == 1) {            $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1701                                 !!!cp ('t70');            $next_sibling = $self->{open_elements}->[$_]->[0];
1702                                 $foster_parent_element = $parent;            undef $next_sibling
1703                                 $next_sibling = $self->{open_elements}->[$_]->[0];                unless $next_sibling->parent_node eq $foster_parent_element;
1704                               } else {            last OE;
1705                                 !!!cp ('t71');          }
1706                                 $foster_parent_element        } # OE
1707                                   = $self->{open_elements}->[$_ - 1]->[0];        $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1708                               }  
1709                               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);  
1710        $open_tables->[-1]->[1] = 1; # tainted        $open_tables->[-1]->[1] = 1; # tainted
1711      } else {      } else {
1712        !!!cp ('t72');        !!!cp ('t72');
# Line 4360  sub _tree_construction_main ($) { Line 1714  sub _tree_construction_main ($) {
1714      }      }
1715    }; # $insert_to_foster    }; # $insert_to_foster
1716    
1717      ## NOTE: Insert a character (MUST): When a character is inserted, if
1718      ## the last node that was inserted by the parser is a Text node and
1719      ## the character has to be inserted after that node, then the
1720      ## character is appended to the Text node.  However, if any other
1721      ## node is inserted by the parser, then a new Text node is created
1722      ## and the character is appended as that Text node.  If I'm not
1723      ## wrong, for a parser with scripting disabled, there are only two
1724      ## cases where this occurs.  One is the case where an element node
1725      ## is inserted to the |head| element.  This is covered by using the
1726      ## |$self->{head_element_inserted}| flag.  Another is the case where
1727      ## an element or comment is inserted into the |table| subtree while
1728      ## foster parenting happens.  This is covered by using the [2] flag
1729      ## of the |$open_tables| structure.  All other cases are handled
1730      ## simply by calling |manakai_append_text| method.
1731    
1732      ## TODO: |<body><script>document.write("a<br>");
1733      ## document.body.removeChild (document.body.lastChild);
1734      ## document.write ("b")</script>|
1735    
1736    B: while (1) {    B: while (1) {
1737    
1738        ## The "in table text" insertion mode.
1739        if ($self->{insertion_mode} & TABLE_IMS and
1740            not $self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
1741            not $self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1742          C: {
1743            my $s;
1744            if ($token->{type} == CHARACTER_TOKEN) {
1745              !!!cp ('t194');
1746              $self->{pending_chars} ||= [];
1747              push @{$self->{pending_chars}}, $token;
1748              !!!next-token;
1749              next B;
1750            } else {
1751              if ($self->{pending_chars}) {
1752                $s = join '', map { $_->{data} } @{$self->{pending_chars}};
1753                delete $self->{pending_chars};
1754                if ($s =~ /[^\x09\x0A\x0C\x0D\x20]/) {
1755                  !!!cp ('t195');
1756                  #
1757                } else {
1758                  !!!cp ('t195.1');
1759                  #$self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1760                  $self->{open_elements}->[-1]->[0]->append_child
1761                      ($self->{document}->create_text_node ($s));
1762                  last C;
1763                }
1764              } else {
1765                !!!cp ('t195.2');
1766                last C;
1767              }
1768            }
1769    
1770            ## Foster parenting.
1771            !!!parse-error (type => 'in table:#text', token => $token);
1772    
1773            ## NOTE: As if in body, but insert into the foster parent element.
1774            $reconstruct_active_formatting_elements->($insert_to_foster);
1775                
1776            if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
1777              # MUST
1778              my $foster_parent_element;
1779              my $next_sibling;
1780              OE: for (reverse 0..$#{$self->{open_elements}}) {
1781                if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1782                  !!!cp ('t197');
1783                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1784                  $next_sibling = $self->{open_elements}->[$_]->[0];
1785                  undef $next_sibling
1786                    unless $next_sibling->parent_node eq $foster_parent_element;
1787                  last OE;
1788                }
1789              } # OE
1790              $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1791    
1792              !!!cp ('t199');
1793              $foster_parent_element->insert_before
1794                  ($self->{document}->create_text_node ($s), $next_sibling);
1795    
1796              $open_tables->[-1]->[1] = 1; # tainted
1797              $open_tables->[-1]->[2] = 1; # ~node inserted
1798            } else {
1799              ## NOTE: Fragment case or in a foster parent'ed element
1800              ## (e.g. |<table><span>a|).  In fragment case, whether the
1801              ## character is appended to existing node or a new node is
1802              ## created is irrelevant, since the foster parent'ed nodes
1803              ## are discarded and fragment parsing does not invoke any
1804              ## script.
1805              !!!cp ('t200');
1806              $self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1807            }
1808          } # C
1809        } # TABLE_IMS
1810    
1811      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1812        !!!cp ('t73');        !!!cp ('t73');
1813        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
# Line 4407  sub _tree_construction_main ($) { Line 1854  sub _tree_construction_main ($) {
1854        } else {        } else {
1855          !!!cp ('t87');          !!!cp ('t87');
1856          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1857            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1858        }        }
1859        !!!next-token;        !!!next-token;
1860        next B;        next B;
1861        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1862          if ($token->{type} == CHARACTER_TOKEN) {
1863            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1864            delete $self->{ignore_newline};
1865    
1866            if (length $token->{data}) {
1867              !!!cp ('t43');
1868              $self->{open_elements}->[-1]->[0]->manakai_append_text
1869                  ($token->{data});
1870            } else {
1871              !!!cp ('t43.1');
1872            }
1873            !!!next-token;
1874            next B;
1875          } elsif ($token->{type} == END_TAG_TOKEN) {
1876            delete $self->{ignore_newline};
1877    
1878            if ($token->{tag_name} eq 'script') {
1879              !!!cp ('t50');
1880              
1881              ## Para 1-2
1882              my $script = pop @{$self->{open_elements}};
1883              
1884              ## Para 3
1885              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1886    
1887              ## Para 4
1888              ## TODO: $old_insertion_point = $current_insertion_point;
1889              ## TODO: $current_insertion_point = just before $self->{nc};
1890    
1891              ## Para 5
1892              ## TODO: Run the $script->[0].
1893    
1894              ## Para 6
1895              ## TODO: $current_insertion_point = $old_insertion_point;
1896    
1897              ## Para 7
1898              ## TODO: if ($pending_external_script) {
1899                ## TODO: ...
1900              ## TODO: }
1901    
1902              !!!next-token;
1903              next B;
1904            } else {
1905              !!!cp ('t42');
1906    
1907              pop @{$self->{open_elements}};
1908    
1909              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1910              !!!next-token;
1911              next B;
1912            }
1913          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1914            delete $self->{ignore_newline};
1915    
1916            !!!cp ('t44');
1917            !!!parse-error (type => 'not closed',
1918                            text => $self->{open_elements}->[-1]->[0]
1919                                ->manakai_local_name,
1920                            token => $token);
1921    
1922            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1923            #  ## TODO: Mark as "already executed"
1924            #}
1925    
1926            pop @{$self->{open_elements}};
1927    
1928            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1929            ## Reprocess.
1930            next B;
1931          } else {
1932            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1933          }
1934      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1935        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1936          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4421  sub _tree_construction_main ($) { Line 1942  sub _tree_construction_main ($) {
1942               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1943              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1944              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1945               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1946            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1947            !!!cp ('t87.2');            !!!cp ('t87.2');
1948            #            #
1949          } elsif ({          } elsif ({
1950                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1951                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1952                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1953                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1954                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1955                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1956                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1957                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1958                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1959                     ($token->{tag_name} eq 'font' and
1960                      ($token->{attributes}->{color} or
1961                       $token->{attributes}->{face} or
1962                       $token->{attributes}->{size}))) {
1963            !!!cp ('t87.2');            !!!cp ('t87.2');
1964            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1965                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4510  sub _tree_construction_main ($) { Line 2035  sub _tree_construction_main ($) {
2035          }          }
2036        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2037          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
2038          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
2039          #            !!!cp ('t87.41');
2040              #
2041              ## XXXscript: Execute script here.
2042            } else {
2043              !!!cp ('t87.5');
2044              #
2045            }
2046        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2047          !!!cp ('t87.6');          !!!cp ('t87.6');
2048          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4522  sub _tree_construction_main ($) { Line 2053  sub _tree_construction_main ($) {
2053          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
2054              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
2055    
2056            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
2057    
2058          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
2059          ## Reprocess.          ## Reprocess.
2060          next B;          next B;
# Line 4534  sub _tree_construction_main ($) { Line 2067  sub _tree_construction_main ($) {
2067        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
2068          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
2069            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2070              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
2071              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
2072              #                $self->{open_elements}->[-1]->[0]->append_child
2073                    ($self->{document}->create_text_node ($1));
2074                  delete $self->{head_element_inserted};
2075                  ## NOTE: |</head> <link> |
2076                  #
2077                } else {
2078                  !!!cp ('t88.2');
2079                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2080                  ## NOTE: |</head> &#x20;|
2081                  #
2082                }
2083            } else {            } else {
2084              !!!cp ('t88.1');              !!!cp ('t88.1');
2085              ## Ignore the token.              ## Ignore the token.
# Line 4632  sub _tree_construction_main ($) { Line 2175  sub _tree_construction_main ($) {
2175            !!!cp ('t97');            !!!cp ('t97');
2176          }          }
2177    
2178              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2179                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2180                  !!!cp ('t98');              !!!cp ('t98');
2181                  ## As if </noscript>              ## As if </noscript>
2182                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2183                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2184                                  token => $token);                              token => $token);
2185                            
2186                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2187                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2188                } else {            } else {
2189                  !!!cp ('t99');              !!!cp ('t99');
2190                }            }
2191    
2192                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2193                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2194                  !!!cp ('t100');              !!!cp ('t100');
2195                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2196                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2197                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2198                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2199                } else {              $self->{head_element_inserted} = 1;
2200                  !!!cp ('t101');            } else {
2201                }              !!!cp ('t101');
2202                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2203                pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2204                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2205                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2206                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2207                !!!next-token;            !!!nack ('t101.1');
2208                next B;            !!!next-token;
2209              next B;
2210          } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
2211            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2212            if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
# Line 4671  sub _tree_construction_main ($) { Line 2215  sub _tree_construction_main ($) {
2215                              text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2216              push @{$self->{open_elements}},              push @{$self->{open_elements}},
2217                  [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2218                $self->{head_element_inserted} = 1;
2219            } else {            } else {
2220              !!!cp ('t103');              !!!cp ('t103');
2221            }            }
# Line 4681  sub _tree_construction_main ($) { Line 2226  sub _tree_construction_main ($) {
2226            !!!ack ('t103.1');            !!!ack ('t103.1');
2227            !!!next-token;            !!!next-token;
2228            next B;            next B;
2229          } elsif ($token->{tag_name} eq 'command' or          } elsif ($token->{tag_name} eq 'command') {
                  $token->{tag_name} eq 'eventsource') {  
2230            if ($self->{insertion_mode} == IN_HEAD_IM) {            if ($self->{insertion_mode} == IN_HEAD_IM) {
2231              ## NOTE: If the insertion mode at the time of the emission              ## NOTE: If the insertion mode at the time of the emission
2232              ## of the token was "before head", $self->{insertion_mode}              ## of the token was "before head", $self->{insertion_mode}
# Line 4703  sub _tree_construction_main ($) { Line 2247  sub _tree_construction_main ($) {
2247              !!!cp ('t103.3');              !!!cp ('t103.3');
2248              #              #
2249            }            }
2250              } elsif ($token->{tag_name} eq 'meta') {          } elsif ($token->{tag_name} eq 'meta') {
2251                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2252                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2253                  !!!cp ('t104');              !!!cp ('t104');
2254                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2255                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2256                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2257                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2258                } else {              $self->{head_element_inserted} = 1;
2259                  !!!cp ('t105');            } else {
2260                }              !!!cp ('t105');
2261                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2262                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2263              my $meta_el = pop @{$self->{open_elements}};
2264    
2265                unless ($self->{confident}) {                unless ($self->{confident}) {
2266                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4773  sub _tree_construction_main ($) { Line 2318  sub _tree_construction_main ($) {
2318                !!!ack ('t110.1');                !!!ack ('t110.1');
2319                !!!next-token;                !!!next-token;
2320                next B;                next B;
2321              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2322                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2323                  !!!cp ('t111');              !!!cp ('t111');
2324                  ## As if </noscript>              ## As if </noscript>
2325                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2326                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2327                                  token => $token);                              token => $token);
2328                            
2329                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2330                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2331                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2332                  !!!cp ('t112');              !!!cp ('t112');
2333                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2334                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2335                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2336                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2337                } else {              $self->{head_element_inserted} = 1;
2338                  !!!cp ('t113');            } else {
2339                }              !!!cp ('t113');
2340              }
2341    
2342                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2343                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2344                    : $self->{open_elements}->[-1]->[0];  
2345                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2346                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2347                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2348                next B;            ## |head| element is inserted only for the purpose of
2349              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2350                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2351                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2352                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2353                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2354                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2355                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2356                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2357                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2358                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2359                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2360                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2361                  !!!cp ('t115');              !!!cp ('t114');
2362                }              !!!parse-error (type => 'after head',
2363                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2364                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2365                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2366                next B;              $self->{head_element_inserted} = 1;
2367              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2368                !!!cp ('t115');
2369              }
2370              $parse_rcdata->(CDATA_CONTENT_MODEL);
2371              ## ISSUE: A spec bug [Bug 6038]
2372              splice @{$self->{open_elements}}, -2, 1, () # <head>
2373                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2374              next B;
2375            } elsif ($token->{tag_name} eq 'noscript') {
2376                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2377                  !!!cp ('t116');                  !!!cp ('t116');
2378                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4839  sub _tree_construction_main ($) { Line 2393  sub _tree_construction_main ($) {
2393                  !!!cp ('t118');                  !!!cp ('t118');
2394                  #                  #
2395                }                }
2396              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2397                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2398                  !!!cp ('t119');              !!!cp ('t119');
2399                  ## As if </noscript>              ## As if </noscript>
2400                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2401                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2402                                  token => $token);                              token => $token);
2403                            
2404                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2405                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2406                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2407                  !!!cp ('t120');              !!!cp ('t120');
2408                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2409                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2410                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2411                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2412                } else {              $self->{head_element_inserted} = 1;
2413                  !!!cp ('t121');            } else {
2414                }              !!!cp ('t121');
2415              }
2416    
2417                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2418                $script_start_tag->();            $script_start_tag->();
2419                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2420                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2421                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2422              } elsif ($token->{tag_name} eq 'body' or            next B;
2423                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2424                     $token->{tag_name} eq 'frameset') {
2425                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2426                  !!!cp ('t122');                  !!!cp ('t122');
2427                  ## As if </noscript>                  ## As if </noscript>
# Line 4935  sub _tree_construction_main ($) { Line 2491  sub _tree_construction_main ($) {
2491              ## reprocess              ## reprocess
2492              !!!ack-later;              !!!ack-later;
2493              next B;              next B;
2494            } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2495              if ($token->{tag_name} eq 'head') {          ## "Before head", "in head", and "after head" insertion modes
2496                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          ## ignore most of end tags.  Exceptions are "body", "html",
2497                  !!!cp ('t132');          ## and "br" end tags.  "Before head" and "in head" insertion
2498                  ## As if <head>          ## modes also recognize "head" end tag.  "In head noscript"
2499                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);          ## insertion modes ignore end tags except for "noscript" and
2500                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});          ## "br".
                 push @{$self->{open_elements}},  
                     [$self->{head_element}, $el_category->{head}];  
2501    
2502                  ## Reprocess in the "in head" insertion mode...          if ($token->{tag_name} eq 'head') {
2503                  pop @{$self->{open_elements}};            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2504                  $self->{insertion_mode} = AFTER_HEAD_IM;              !!!cp ('t132');
2505                  !!!next-token;              ## As if <head>
2506                  next B;              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2507                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2508                  !!!cp ('t133');              push @{$self->{open_elements}},
2509                  ## As if </noscript>                  [$self->{head_element}, $el_category->{head}];
2510                  pop @{$self->{open_elements}};  
2511                  !!!parse-error (type => 'in noscript:/',              ## Reprocess in the "in head" insertion mode...
2512                                  text => 'head', token => $token);              pop @{$self->{open_elements}};
2513                                $self->{insertion_mode} = AFTER_HEAD_IM;
2514                  ## Reprocess in the "in head" insertion mode...              !!!next-token;
2515                  pop @{$self->{open_elements}};              next B;
2516                  $self->{insertion_mode} = AFTER_HEAD_IM;            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2517                  !!!next-token;              !!!cp ('t133');
2518                  next B;              #
2519                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2520                  !!!cp ('t134');              !!!cp ('t134');
2521                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2522                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2523                  !!!next-token;              !!!next-token;
2524                  next B;              next B;
2525                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2526                  !!!cp ('t134.1');              !!!cp ('t134.1');
2527                  !!!parse-error (type => 'unmatched end tag', text => 'head',              #
2528                                  token => $token);            } else {
2529                  ## Ignore the token              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2530                  !!!next-token;            }
2531                  next B;          } elsif ($token->{tag_name} eq 'noscript') {
2532                } else {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2533                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              !!!cp ('t136');
2534                }              pop @{$self->{open_elements}};
2535              } elsif ($token->{tag_name} eq 'noscript') {              $self->{insertion_mode} = IN_HEAD_IM;
2536                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              !!!next-token;
2537                  !!!cp ('t136');              next B;
2538                  pop @{$self->{open_elements}};            } else {
2539                  $self->{insertion_mode} = IN_HEAD_IM;              !!!cp ('t138');
2540                  !!!next-token;              #
2541                  next B;            }
2542                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or          } elsif ({
2543                         $self->{insertion_mode} == AFTER_HEAD_IM) {              body => ($self->{insertion_mode} != IN_HEAD_NOSCRIPT_IM),
2544                  !!!cp ('t137');              html => ($self->{insertion_mode} != IN_HEAD_NOSCRIPT_IM),
2545                  !!!parse-error (type => 'unmatched end tag',              br => 1,
2546                                  text => 'noscript', token => $token);          }->{$token->{tag_name}}) {
2547                  ## Ignore the token ## ISSUE: An issue in the spec.            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2548                  !!!next-token;              !!!cp ('t142.2');
2549                  next B;              ## (before head) as if <head>, (in head) as if </head>
2550                } else {              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2551                  !!!cp ('t138');              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2552                  #              $self->{insertion_mode} = AFTER_HEAD_IM;
               }  
             } elsif ({  
                       body => 1, html => 1,  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == BEFORE_HEAD_IM or  
                   $self->{insertion_mode} == IN_HEAD_IM or  
                   $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {  
                 !!!cp ('t140');  
                 !!!parse-error (type => 'unmatched end tag',  
                                 text => $token->{tag_name}, token => $token);  
                 ## Ignore the token  
                 !!!next-token;  
                 next B;  
               } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {  
                 !!!cp ('t140.1');  
                 !!!parse-error (type => 'unmatched end tag',  
                                 text => $token->{tag_name}, token => $token);  
                 ## Ignore the token  
                 !!!next-token;  
                 next B;  
               } else {  
                 die "$0: $self->{insertion_mode}: Unknown insertion mode";  
               }  
             } elsif ($token->{tag_name} eq 'p') {  
               !!!cp ('t142');  
               !!!parse-error (type => 'unmatched end tag',  
                               text => $token->{tag_name}, token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } elsif ($token->{tag_name} eq 'br') {  
               if ($self->{insertion_mode} == BEFORE_HEAD_IM) {  
                 !!!cp ('t142.2');  
                 ## (before head) as if <head>, (in head) as if </head>  
                 !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);  
                 $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
                 $self->{insertion_mode} = AFTER_HEAD_IM;  
2553        
2554                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2555                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2556                  !!!cp ('t143.2');              !!!cp ('t143.2');
2557                  ## As if </head>              ## As if </head>
2558                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2559                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2560        
2561                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2562                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2563                  !!!cp ('t143.3');              !!!cp ('t143.3');
2564                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2565                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2566                                  text => 'br', token => $token);                              text => $token->{tag_name}, token => $token);
2567                  ## As if </noscript>              ## As if </noscript>
2568                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2569                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
   
                 ## Reprocess in the "in head" insertion mode...  
                 ## As if </head>  
                 pop @{$self->{open_elements}};  
                 $self->{insertion_mode} = AFTER_HEAD_IM;  
   
                 ## Reprocess in the "after head" insertion mode...  
               } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {  
                 !!!cp ('t143.4');  
                 #  
               } else {  
                 die "$0: $self->{insertion_mode}: Unknown insertion mode";  
               }  
   
               ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.  
               !!!parse-error (type => 'unmatched end tag',  
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
               !!!cp ('t145');  
               !!!parse-error (type => 'unmatched end tag',  
                               text => $token->{tag_name}, token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             }  
2570    
2571              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              ## Reprocess in the "in head" insertion mode...
2572                !!!cp ('t146');              ## As if </head>
2573                ## As if </noscript>              pop @{$self->{open_elements}};
2574                pop @{$self->{open_elements}};              $self->{insertion_mode} = AFTER_HEAD_IM;
               !!!parse-error (type => 'in noscript:/',  
                               text => $token->{tag_name}, token => $token);  
                 
               ## Reprocess in the "in head" insertion mode...  
               ## As if </head>  
               pop @{$self->{open_elements}};  
2575    
2576                ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2577              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2578                !!!cp ('t147');              !!!cp ('t143.4');
2579                ## As if </head>              #
2580                pop @{$self->{open_elements}};            } else {
2581                die "$0: $self->{insertion_mode}: Unknown insertion mode";
2582              }
2583    
2584                ## Reprocess in the "after head" insertion mode...            ## "after head" insertion mode
2585              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {            ## As if <body>
2586  ## ISSUE: This case cannot be reached?            !!!insert-element ('body',, $token);
2587                !!!cp ('t148');            $self->{insertion_mode} = IN_BODY_IM;
2588                !!!parse-error (type => 'unmatched end tag',            ## Reprocess.
2589                                text => $token->{tag_name}, token => $token);            next B;
2590                ## Ignore the token ## ISSUE: An issue in the spec.          }
               !!!next-token;  
               next B;  
             } else {  
               !!!cp ('t149');  
             }  
2591    
2592              ## "after head" insertion mode          ## End tags are ignored by default.
2593              ## As if <body>          !!!cp ('t145');
2594              !!!insert-element ('body',, $token);          !!!parse-error (type => 'unmatched end tag',
2595              $self->{insertion_mode} = IN_BODY_IM;                          text => $token->{tag_name}, token => $token);
2596              ## reprocess          ## Ignore the token.
2597              next B;          !!!next-token;
2598            next B;
2599        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2600          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2601            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5172  sub _tree_construction_main ($) { Line 2653  sub _tree_construction_main ($) {
2653        } else {        } else {
2654          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2655        }        }
   
           ## ISSUE: An issue in the spec.  
2656      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2657            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
2658              !!!cp ('t150');              !!!cp ('t150');
# Line 5189  sub _tree_construction_main ($) { Line 2668  sub _tree_construction_main ($) {
2668                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2669                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2670                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2671                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2672                  ## have an element in table scope                  ## have an element in table scope
2673                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2674                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2675                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2676                      !!!cp ('t151');                      !!!cp ('t151');
2677    
2678                      ## Close the cell                      ## Close the cell
# Line 5217  sub _tree_construction_main ($) { Line 2696  sub _tree_construction_main ($) {
2696                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2697                  !!!next-token;                  !!!next-token;
2698                  next B;                  next B;
2699                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2700                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2701                                  token => $token);                                  token => $token);
2702                                    
# Line 5227  sub _tree_construction_main ($) { Line 2706  sub _tree_construction_main ($) {
2706                  INSCOPE: {                  INSCOPE: {
2707                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2708                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2709                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2710                        !!!cp ('t155');                        !!!cp ('t155');
2711                        $i = $_;                        $i = $_;
2712                        last INSCOPE;                        last INSCOPE;
# Line 5253  sub _tree_construction_main ($) { Line 2732  sub _tree_construction_main ($) {
2732                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2733                  }                  }
2734    
2735                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2736                    !!!cp ('t159');                    !!!cp ('t159');
2737                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2738                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5282  sub _tree_construction_main ($) { Line 2761  sub _tree_construction_main ($) {
2761              }              }
2762            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2763              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2764                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2765                  ## have an element in table scope                  ## have an element in table scope
2766                  my $i;                  my $i;
2767                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5332  sub _tree_construction_main ($) { Line 2811  sub _tree_construction_main ($) {
2811                                    
2812                  !!!next-token;                  !!!next-token;
2813                  next B;                  next B;
2814                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2815                  !!!cp ('t169');                  !!!cp ('t169');
2816                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2817                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5344  sub _tree_construction_main ($) { Line 2823  sub _tree_construction_main ($) {
2823                  #                  #
2824                }                }
2825              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2826                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2827                  ## have a table element in table scope                  ## have a table element in table scope
2828                  my $i;                  my $i;
2829                  INSCOPE: {                  INSCOPE: {
2830                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2831                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2832                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2833                        !!!cp ('t171');                        !!!cp ('t171');
2834                        $i = $_;                        $i = $_;
2835                        last INSCOPE;                        last INSCOPE;
# Line 5375  sub _tree_construction_main ($) { Line 2854  sub _tree_construction_main ($) {
2854                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2855                  }                  }
2856                                    
2857                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2858                    !!!cp ('t175');                    !!!cp ('t175');
2859                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2860                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5393  sub _tree_construction_main ($) { Line 2872  sub _tree_construction_main ($) {
2872                                    
2873                  !!!next-token;                  !!!next-token;
2874                  next B;                  next B;
2875                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2876                  !!!cp ('t177');                  !!!cp ('t177');
2877                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2878                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5408  sub _tree_construction_main ($) { Line 2887  sub _tree_construction_main ($) {
2887                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2888                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2889                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2890                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2891                ## have an element in table scope                ## have an element in table scope
2892                my $i;                my $i;
2893                my $tn;                my $tn;
# Line 5425  sub _tree_construction_main ($) { Line 2904  sub _tree_construction_main ($) {
2904                                line => $token->{line},                                line => $token->{line},
2905                                column => $token->{column}};                                column => $token->{column}};
2906                      next B;                      next B;
2907                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2908                      !!!cp ('t180');                      !!!cp ('t180');
2909                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2910                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5445  sub _tree_construction_main ($) { Line 2924  sub _tree_construction_main ($) {
2924                  next B;                  next B;
2925                } # INSCOPE                } # INSCOPE
2926              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2927                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2928                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2929                                token => $token);                                token => $token);
2930    
# Line 5454  sub _tree_construction_main ($) { Line 2933  sub _tree_construction_main ($) {
2933                my $i;                my $i;
2934                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2935                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
2936                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
2937                    !!!cp ('t184');                    !!!cp ('t184');
2938                    $i = $_;                    $i = $_;
2939                    last INSCOPE;                    last INSCOPE;
# Line 5465  sub _tree_construction_main ($) { Line 2944  sub _tree_construction_main ($) {
2944                } # INSCOPE                } # INSCOPE
2945                unless (defined $i) {                unless (defined $i) {
2946                  !!!cp ('t186');                  !!!cp ('t186');
2947            ## TODO: Wrong error type?
2948                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2949                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
2950                  ## Ignore the token                  ## Ignore the token
# Line 5478  sub _tree_construction_main ($) { Line 2958  sub _tree_construction_main ($) {
2958                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2959                }                }
2960    
2961                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2962                  !!!cp ('t188');                  !!!cp ('t188');
2963                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
2964                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5510  sub _tree_construction_main ($) { Line 2990  sub _tree_construction_main ($) {
2990                  !!!cp ('t191');                  !!!cp ('t191');
2991                  #                  #
2992                }                }
2993              } elsif ({          } elsif ({
2994                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
2995                        thead => 1, tr => 1,                    thead => 1, tr => 1,
2996                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
2997                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2998                !!!cp ('t192');            !!!cp ('t192');
2999                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
3000                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
3001                ## Ignore the token            ## Ignore the token
3002                !!!next-token;            !!!next-token;
3003                next B;            next B;
3004              } else {          } else {
3005                !!!cp ('t193');            !!!cp ('t193');
3006                #            #
3007              }          }
3008        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3009          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3010            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5543  sub _tree_construction_main ($) { Line 3023  sub _tree_construction_main ($) {
3023        $insert = $insert_to_current;        $insert = $insert_to_current;
3024        #        #
3025      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3026        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) {  
3027          if ({          if ({
3028               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3029               th => 1, td => 1,               th => 1, td => 1,
3030              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3031            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3032              ## Clear back to table context              ## Clear back to table context
3033              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3034                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5625  sub _tree_construction_main ($) { Line 3041  sub _tree_construction_main ($) {
3041              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3042            }            }
3043                        
3044            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3045              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3046                !!!cp ('t202');                !!!cp ('t202');
3047                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5639  sub _tree_construction_main ($) { Line 3055  sub _tree_construction_main ($) {
3055                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3056              }              }
3057                                    
3058                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3059                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3060                    !!!cp ('t204');                !!!cp ('t204');
3061                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3062                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3063                    !!!next-token;                !!!nack ('t204');
3064                    next B;                !!!next-token;
3065                  } else {                next B;
3066                    !!!cp ('t205');              } else {
3067                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3068                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3069                  }                ## reprocess in the "in row" insertion mode
3070                } else {              }
3071                  !!!cp ('t206');            } else {
3072                }              !!!cp ('t206');
3073              }
3074    
3075                ## Clear back to table row context                ## Clear back to table row context
3076                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5662  sub _tree_construction_main ($) { Line 3079  sub _tree_construction_main ($) {
3079                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3080                }                }
3081                                
3082                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3083                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3084              $self->{insertion_mode} = IN_CELL_IM;
3085    
3086                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3087                                
3088                !!!nack ('t207.1');            !!!nack ('t207.1');
3089              !!!next-token;
3090              next B;
3091            } elsif ({
3092                      caption => 1, col => 1, colgroup => 1,
3093                      tbody => 1, tfoot => 1, thead => 1,
3094                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3095                     }->{$token->{tag_name}}) {
3096              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3097                ## As if </tr>
3098                ## have an element in table scope
3099                my $i;
3100                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3101                  my $node = $self->{open_elements}->[$_];
3102                  if ($node->[1] == TABLE_ROW_EL) {
3103                    !!!cp ('t208');
3104                    $i = $_;
3105                    last INSCOPE;
3106                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3107                    !!!cp ('t209');
3108                    last INSCOPE;
3109                  }
3110                } # INSCOPE
3111                unless (defined $i) {
3112                  !!!cp ('t210');
3113                  ## TODO: This type is wrong.
3114                  !!!parse-error (type => 'unmacthed end tag',
3115                                  text => $token->{tag_name}, token => $token);
3116                  ## Ignore the token
3117                  !!!nack ('t210.1');
3118                !!!next-token;                !!!next-token;
3119                next B;                next B;
3120              } 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;  
                 }  
3121                                    
3122                  ## Clear back to table row context                  ## Clear back to table row context
3123                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5722  sub _tree_construction_main ($) { Line 3140  sub _tree_construction_main ($) {
3140                  }                  }
3141                }                }
3142    
3143                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3144                  ## have an element in table scope                  ## have an element in table scope
3145                  my $i;                  my $i;
3146                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3147                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3148                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3149                      !!!cp ('t214');                      !!!cp ('t214');
3150                      $i = $_;                      $i = $_;
3151                      last INSCOPE;                      last INSCOPE;
# Line 5769  sub _tree_construction_main ($) { Line 3187  sub _tree_construction_main ($) {
3187                  !!!cp ('t218');                  !!!cp ('t218');
3188                }                }
3189    
3190                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3191                  ## Clear back to table context              ## Clear back to table context
3192                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3193                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3194                    !!!cp ('t219');                !!!cp ('t219');
3195                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3196                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3197                  }              }
3198                                
3199                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3200                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3201                  ## reprocess              ## reprocess
3202                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3203                  next B;              !!!ack-later;
3204                } elsif ({              next B;
3205                          caption => 1,            } elsif ({
3206                          colgroup => 1,                      caption => 1,
3207                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3208                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3209                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3210                ## Clear back to table context
3211                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3212                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3213                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5796  sub _tree_construction_main ($) { Line 3215  sub _tree_construction_main ($) {
3215                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3216                  }                  }
3217                                    
3218                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3219                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3220                                    
3221                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3222                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3223                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3224                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3225                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3226                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3227                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3228                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3229                  !!!next-token;                                        }->{$token->{tag_name}};
3230                  !!!nack ('t220.1');              !!!next-token;
3231                  next B;              !!!nack ('t220.1');
3232                } else {              next B;
3233                  die "$0: in table: <>: $token->{tag_name}";            } else {
3234                }              die "$0: in table: <>: $token->{tag_name}";
3235              }
3236              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3237                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3238                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5824  sub _tree_construction_main ($) { Line 3244  sub _tree_construction_main ($) {
3244                my $i;                my $i;
3245                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3246                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3247                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3248                    !!!cp ('t221');                    !!!cp ('t221');
3249                    $i = $_;                    $i = $_;
3250                    last INSCOPE;                    last INSCOPE;
# Line 5851  sub _tree_construction_main ($) { Line 3271  sub _tree_construction_main ($) {
3271                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3272                }                }
3273    
3274                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3275                  !!!cp ('t225');                  !!!cp ('t225');
3276                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3277                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5871  sub _tree_construction_main ($) { Line 3291  sub _tree_construction_main ($) {
3291            !!!ack-later;            !!!ack-later;
3292            next B;            next B;
3293          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
3294            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.8');
3295              !!!cp ('t227.8');            ## NOTE: This is a "as if in head" code clone.
3296              ## NOTE: This is a "as if in head" code clone.            $parse_rcdata->(CDATA_CONTENT_MODEL);
3297              $parse_rcdata->(CDATA_CONTENT_MODEL);            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3298              next B;            next B;
           } else {  
             !!!cp ('t227.7');  
             #  
           }  
3299          } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
3300            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.6');
3301              !!!cp ('t227.6');            ## NOTE: This is a "as if in head" code clone.
3302              ## NOTE: This is a "as if in head" code clone.            $script_start_tag->();
3303              $script_start_tag->();            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3304              next B;            next B;
           } else {  
             !!!cp ('t227.5');  
             #  
           }  
3305          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
3306            if (not $open_tables->[-1]->[1]) { # tainted            if ($token->{attributes}->{type}) {
3307              if ($token->{attributes}->{type}) { ## TODO: case              my $type = $token->{attributes}->{type}->{value};
3308                my $type = lc $token->{attributes}->{type}->{value};              $type =~ tr/A-Z/a-z/; ## ASCII case-insensitive.
3309                if ($type eq 'hidden') {              if ($type eq 'hidden') {
3310                  !!!cp ('t227.3');                !!!cp ('t227.3');
3311                  !!!parse-error (type => 'in table',                !!!parse-error (type => 'in table',
3312                                  text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
3313    
3314                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3315                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3316    
3317                  ## TODO: form element pointer                ## TODO: form element pointer
3318    
3319                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3320    
3321                  !!!next-token;                !!!next-token;
3322                  !!!ack ('t227.2.1');                !!!ack ('t227.2.1');
3323                  next B;                next B;
               } else {  
                 !!!cp ('t227.2');  
                 #  
               }  
3324              } else {              } else {
3325                !!!cp ('t227.1');                !!!cp ('t227.1');
3326                #                #
# Line 5931  sub _tree_construction_main ($) { Line 3340  sub _tree_construction_main ($) {
3340          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3341          #          #
3342        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3343              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3344                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3345                ## have an element in table scope            ## have an element in table scope
3346                my $i;                my $i;
3347                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3348                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3349                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3350                    !!!cp ('t228');                    !!!cp ('t228');
3351                    $i = $_;                    $i = $_;
3352                    last INSCOPE;                    last INSCOPE;
# Line 5972  sub _tree_construction_main ($) { Line 3381  sub _tree_construction_main ($) {
3381                !!!nack ('t231.1');                !!!nack ('t231.1');
3382                next B;                next B;
3383              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3384                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3385                  ## As if </tr>                  ## As if </tr>
3386                  ## have an element in table scope                  ## have an element in table scope
3387                  my $i;                  my $i;
3388                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3389                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3390                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3391                      !!!cp ('t233');                      !!!cp ('t233');
3392                      $i = $_;                      $i = $_;
3393                      last INSCOPE;                      last INSCOPE;
# Line 6011  sub _tree_construction_main ($) { Line 3420  sub _tree_construction_main ($) {
3420                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3421                }                }
3422    
3423                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3424                  ## have an element in table scope                  ## have an element in table scope
3425                  my $i;                  my $i;
3426                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3427                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3428                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3429                      !!!cp ('t237');                      !!!cp ('t237');
3430                      $i = $_;                      $i = $_;
3431                      last INSCOPE;                      last INSCOPE;
# Line 6063  sub _tree_construction_main ($) { Line 3472  sub _tree_construction_main ($) {
3472                my $i;                my $i;
3473                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3474                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3475                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3476                    !!!cp ('t241');                    !!!cp ('t241');
3477                    $i = $_;                    $i = $_;
3478                    last INSCOPE;                    last INSCOPE;
# Line 6093  sub _tree_construction_main ($) { Line 3502  sub _tree_construction_main ($) {
3502                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3503                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3504                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3505                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3506                  ## have an element in table scope                  ## have an element in table scope
3507                  my $i;                  my $i;
3508                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6122  sub _tree_construction_main ($) { Line 3531  sub _tree_construction_main ($) {
3531                  my $i;                  my $i;
3532                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3533                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3534                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3535                      !!!cp ('t250');                      !!!cp ('t250');
3536                      $i = $_;                      $i = $_;
3537                      last INSCOPE;                      last INSCOPE;
# Line 6212  sub _tree_construction_main ($) { Line 3621  sub _tree_construction_main ($) {
3621            #            #
3622          }          }
3623        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3624          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3625                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3626            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3627            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6227  sub _tree_construction_main ($) { Line 3636  sub _tree_construction_main ($) {
3636        } else {        } else {
3637          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3638        }        }
3639      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3640            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3641              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3642                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6254  sub _tree_construction_main ($) { Line 3663  sub _tree_construction_main ($) {
3663              }              }
3664            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3665              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3666                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3667                  !!!cp ('t264');                  !!!cp ('t264');
3668                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3669                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6280  sub _tree_construction_main ($) { Line 3689  sub _tree_construction_main ($) {
3689                #                #
3690              }              }
3691        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3692          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3693              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3694            !!!cp ('t270.2');            !!!cp ('t270.2');
3695            ## Stop parsing.            ## Stop parsing.
# Line 6298  sub _tree_construction_main ($) { Line 3707  sub _tree_construction_main ($) {
3707        }        }
3708    
3709            ## As if </colgroup>            ## As if </colgroup>
3710            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3711              !!!cp ('t269');              !!!cp ('t269');
3712  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3713              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6323  sub _tree_construction_main ($) { Line 3732  sub _tree_construction_main ($) {
3732          next B;          next B;
3733        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3734          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3735            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3736              !!!cp ('t272');              !!!cp ('t272');
3737              ## As if </option>              ## As if </option>
3738              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6336  sub _tree_construction_main ($) { Line 3745  sub _tree_construction_main ($) {
3745            !!!next-token;            !!!next-token;
3746            next B;            next B;
3747          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3748            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3749              !!!cp ('t274');              !!!cp ('t274');
3750              ## As if </option>              ## As if </option>
3751              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6344  sub _tree_construction_main ($) { Line 3753  sub _tree_construction_main ($) {
3753              !!!cp ('t275');              !!!cp ('t275');
3754            }            }
3755    
3756            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3757              !!!cp ('t276');              !!!cp ('t276');
3758              ## As if </optgroup>              ## As if </optgroup>
3759              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6357  sub _tree_construction_main ($) { Line 3766  sub _tree_construction_main ($) {
3766            !!!next-token;            !!!next-token;
3767            next B;            next B;
3768          } elsif ({          } elsif ({
3769                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3770                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3771                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3772                          == IN_SELECT_IN_TABLE_IM and
3773                    {                    {
3774                     caption => 1, table => 1,                     caption => 1, table => 1,
3775                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3776                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3777                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3778            ## TODO: The type below is not good - <select> is replaced by </select>  
3779            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3780                            token => $token);            if ($token->{tag_name} eq 'select') {
3781            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3782            ## as if there were </select> (otherwise).                                token => $token);
3783            ## have an element in table scope            } else {
3784                !!!parse-error (type => 'not closed', text => 'select',
3785                                token => $token);
3786              }
3787    
3788              ## 2./<select>-1. Unless "have an element in table scope" (select):
3789            my $i;            my $i;
3790            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3791              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3792              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3793                !!!cp ('t278');                !!!cp ('t278');
3794                $i = $_;                $i = $_;
3795                last INSCOPE;                last INSCOPE;
# Line 6385  sub _tree_construction_main ($) { Line 3800  sub _tree_construction_main ($) {
3800            } # INSCOPE            } # INSCOPE
3801            unless (defined $i) {            unless (defined $i) {
3802              !!!cp ('t280');              !!!cp ('t280');
3803              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3804                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3805              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3806                  ## case two errors, "select in select" and "unmatched
3807                  ## end tags" are reported to the user, the latter might
3808                  ## be confusing but this is what the spec requires.
3809                  !!!parse-error (type => 'unmatched end tag',
3810                                  text => 'select',
3811                                  token => $token);
3812                }
3813                ## Ignore the token.
3814              !!!nack ('t280.1');              !!!nack ('t280.1');
3815              !!!next-token;              !!!next-token;
3816              next B;              next B;
3817            }            }
3818    
3819              ## 3. Otherwise, as if there were <select>:
3820                                
3821            !!!cp ('t281');            !!!cp ('t281');
3822            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6408  sub _tree_construction_main ($) { Line 3833  sub _tree_construction_main ($) {
3833              ## Reprocess the token.              ## Reprocess the token.
3834              next B;              next B;
3835            }            }
3836            } elsif ($token->{tag_name} eq 'script') {
3837              !!!cp ('t281.3');
3838              ## NOTE: This is an "as if in head" code clone
3839              $script_start_tag->();
3840              next B;
3841          } else {          } else {
3842            !!!cp ('t282');            !!!cp ('t282');
3843            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6419  sub _tree_construction_main ($) { Line 3849  sub _tree_construction_main ($) {
3849          }          }
3850        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3851          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3852            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3853                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3854              !!!cp ('t283');              !!!cp ('t283');
3855              ## As if </option>              ## As if </option>
3856              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3857            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3858              !!!cp ('t284');              !!!cp ('t284');
3859              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3860            } else {            } else {
# Line 6437  sub _tree_construction_main ($) { Line 3867  sub _tree_construction_main ($) {
3867            !!!next-token;            !!!next-token;
3868            next B;            next B;
3869          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3870            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3871              !!!cp ('t286');              !!!cp ('t286');
3872              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3873            } else {            } else {
# Line 6454  sub _tree_construction_main ($) { Line 3884  sub _tree_construction_main ($) {
3884            my $i;            my $i;
3885            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3886              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3887              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3888                !!!cp ('t288');                !!!cp ('t288');
3889                $i = $_;                $i = $_;
3890                last INSCOPE;                last INSCOPE;
# Line 6481  sub _tree_construction_main ($) { Line 3911  sub _tree_construction_main ($) {
3911            !!!nack ('t291.1');            !!!nack ('t291.1');
3912            !!!next-token;            !!!next-token;
3913            next B;            next B;
3914          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3915                         == IN_SELECT_IN_TABLE_IM and
3916                   {                   {
3917                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3918                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6516  sub _tree_construction_main ($) { Line 3947  sub _tree_construction_main ($) {
3947            undef $i;            undef $i;
3948            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3949              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3950              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3951                !!!cp ('t295');                !!!cp ('t295');
3952                $i = $_;                $i = $_;
3953                last INSCOPE;                last INSCOPE;
# Line 6555  sub _tree_construction_main ($) { Line 3986  sub _tree_construction_main ($) {
3986            next B;            next B;
3987          }          }
3988        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3989          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3990                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3991            !!!cp ('t299.1');            !!!cp ('t299.1');
3992            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6742  sub _tree_construction_main ($) { Line 4173  sub _tree_construction_main ($) {
4173        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4174          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4175              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4176            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4177                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4178              !!!cp ('t325');              !!!cp ('t325');
4179              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6756  sub _tree_construction_main ($) { Line 4187  sub _tree_construction_main ($) {
4187            }            }
4188    
4189            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4190                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4191              !!!cp ('t327');              !!!cp ('t327');
4192              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4193            } else {            } else {
# Line 6788  sub _tree_construction_main ($) { Line 4219  sub _tree_construction_main ($) {
4219            next B;            next B;
4220          }          }
4221        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4222          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4223                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4224            !!!cp ('t331.1');            !!!cp ('t331.1');
4225            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6801  sub _tree_construction_main ($) { Line 4232  sub _tree_construction_main ($) {
4232        } else {        } else {
4233          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4234        }        }
   
       ## ISSUE: An issue in spec here  
4235      } else {      } else {
4236        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4237      }      }
# Line 6820  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4250          next B;          next B;
4251        } elsif ({        } elsif ({
4252                  base => 1, command => 1, eventsource => 1, link => 1,                  base => 1, command => 1, link => 1,
4253                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4254          !!!cp ('t334');          !!!cp ('t334');
4255          ## 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
# Line 6893  sub _tree_construction_main ($) { Line 4322  sub _tree_construction_main ($) {
4322          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4323                                
4324          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4325              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4326            !!!cp ('t342');            !!!cp ('t342');
4327            ## Ignore the token            ## Ignore the token
4328          } else {          } else {
# Line 6910  sub _tree_construction_main ($) { Line 4339  sub _tree_construction_main ($) {
4339          !!!nack ('t343.1');          !!!nack ('t343.1');
4340          !!!next-token;          !!!next-token;
4341          next B;          next B;
4342          } elsif ($token->{tag_nane} eq 'frameset') {
4343            !!!parse-error (type => 'in body', text => $token->{tag_name},
4344                            token => $token);
4345    
4346            if (@{$self->{open_elements}} == 1 or
4347                not ($self->{open_elements}->[1]->[1] != BODY_EL)) {
4348              !!!cp ('t343.2');
4349              ## Ignore the token.
4350            } elsif (not $self->{frameset_ok}) {
4351              !!!cp ('t343.3');
4352              ## Ignore the token.
4353            } else {
4354              !!!cp ('t343.4');
4355              
4356              ## 1. Remove the second element.
4357              my $body = $self->{open_elements}->[1]->[0];
4358              my $body_parent = $body->parent_node;
4359              $body_parent->remove_child ($body) if $body_parent;
4360    
4361              ## 2. Pop nodes.
4362              splice @{$self->{open_elements}}, 1;
4363    
4364              ## 3. Insert.
4365              !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4366    
4367              ## 4. Switch.
4368              $self->{insertion_mode} = IN_FRAMESET_IM;
4369            }
4370    
4371            !!!nack ('t343.5');
4372            !!!next-token;
4373            next B;
4374        } elsif ({        } elsif ({
4375                  ## NOTE: Start tags for non-phrasing flow content elements                  ## NOTE: Start tags for non-phrasing flow content elements
4376    
# Line 6918  sub _tree_construction_main ($) { Line 4379  sub _tree_construction_main ($) {
4379                  center => 1, datagrid => 1, details => 1, dialog => 1,                  center => 1, datagrid => 1, details => 1, dialog => 1,
4380                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
4381                  footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,                  footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
4382                  h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,                  h6 => 1, header => 1, hgroup => 1,
4383                    menu => 1, nav => 1, ol => 1, p => 1,
4384                  section => 1, ul => 1,                  section => 1, ul => 1,
4385                  ## NOTE: As normal, but drops leading newline                  ## NOTE: As normal, but drops leading newline
4386                  pre => 1, listing => 1,                  pre => 1, listing => 1,
# Line 6928  sub _tree_construction_main ($) { Line 4390  sub _tree_construction_main ($) {
4390                  table => 1,                  table => 1,
4391                  hr => 1,                  hr => 1,
4392                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4393    
4394            ## 1. When there is an opening |form| element:
4395          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4396            !!!cp ('t350');            !!!cp ('t350');
4397            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6937  sub _tree_construction_main ($) { Line 4401  sub _tree_construction_main ($) {
4401            next B;            next B;
4402          }          }
4403    
4404          ## has a p element in scope          ## 2. Close the |p| element, if any.
4405          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4406            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4407              !!!cp ('t344');            ## has a p element in scope
4408              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4409              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4410                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4411              next B;                !!!back-token; # <form>
4412            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4413              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4414              last INSCOPE;                next B;
4415                } elsif ($_->[1] & SCOPING_EL) {
4416                  !!!cp ('t345');
4417                  last INSCOPE;
4418                }
4419              } # INSCOPE
4420            }
4421    
4422            ## 3. Close the opening <hn> element, if any.
4423            if ({h1 => 1, h2 => 1, h3 => 1,
4424                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4425              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4426                !!!parse-error (type => 'not closed',
4427                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4428                                token => $token);
4429                pop @{$self->{open_elements}};
4430            }            }
4431          } # INSCOPE          }
4432              
4433            ## 4. Insertion.
4434          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4435          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4436            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6984  sub _tree_construction_main ($) { Line 4464  sub _tree_construction_main ($) {
4464            !!!cp ('t386');            !!!cp ('t386');
4465            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4466                    
4467            !!!nack ('t386.1');            !!!ack ('t386.1');
4468            !!!next-token;            !!!next-token;
4469          } else {          } else {
4470            !!!nack ('t347.1');            !!!nack ('t347.1');
# Line 6994  sub _tree_construction_main ($) { Line 4474  sub _tree_construction_main ($) {
4474        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4475          ## NOTE: As normal, but imply </li> when there's another <li> ...          ## NOTE: As normal, but imply </li> when there's another <li> ...
4476    
4477          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4478            ## Interpreted as <li><foo/></li><li/> (non-conforming)            ## Interpreted as <li><foo/></li><li/> (non-conforming):
4479            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4480            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
4481            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4482            ## object (Fx)            ## object (Fx)
4483            ## Generate non-tree (non-conforming)            ## Generate non-tree (non-conforming):
4484            ## basefont (IE7 (where basefont is non-void)), center (IE),            ## basefont (IE7 (where basefont is non-void)), center (IE),
4485            ## form (IE), hn (IE)            ## form (IE), hn (IE)
4486          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4487            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)            ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4488            ## div (Fx, S)            ## div (Fx, S)
4489    
4490          my $non_optional;          my $non_optional;
# Line 7012  sub _tree_construction_main ($) { Line 4492  sub _tree_construction_main ($) {
4492    
4493          ## 1.          ## 1.
4494          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4495            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4496              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4497              {              {
4498                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7043  sub _tree_construction_main ($) { Line 4523  sub _tree_construction_main ($) {
4523                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4524                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4525                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4526                       (not $node->[1] & ADDRESS_DIV_P_EL)
4527                     (not $node->[1] & ADDRESS_EL) &                    ) {
                    (not $node->[1] & DIV_EL) &  
                    (not $node->[1] & P_EL)) {  
4528              ## 3.              ## 3.
4529              !!!cp ('t357');              !!!cp ('t357');
4530              last; ## goto 5.              last; ## goto 5.
# Line 7065  sub _tree_construction_main ($) { Line 4543  sub _tree_construction_main ($) {
4543    
4544          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4545          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4546            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4547              !!!cp ('t353');              !!!cp ('t353');
4548    
4549                ## NOTE: |<p><li>|, for example.
4550    
4551              !!!back-token; # <x>              !!!back-token; # <x>
4552              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
4553                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 7091  sub _tree_construction_main ($) { Line 4572  sub _tree_construction_main ($) {
4572    
4573          ## 1.          ## 1.
4574          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4575            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4576              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4577              {              {
4578                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7123  sub _tree_construction_main ($) { Line 4604  sub _tree_construction_main ($) {
4604                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4605                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4606    
4607                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4608                     (not $node->[1] & DIV_EL) &                    ) {
                    (not $node->[1] & P_EL)) {  
4609              ## 3.              ## 3.
4610              !!!cp ('t357.1');              !!!cp ('t357.1');
4611              last; ## goto 5.              last; ## goto 5.
# Line 7144  sub _tree_construction_main ($) { Line 4624  sub _tree_construction_main ($) {
4624    
4625          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4626          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4627            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4628              !!!cp ('t353.1');              !!!cp ('t353.1');
4629              !!!back-token; # <x>              !!!back-token; # <x>
4630              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7166  sub _tree_construction_main ($) { Line 4646  sub _tree_construction_main ($) {
4646    
4647          ## has a p element in scope          ## has a p element in scope
4648          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4649            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4650              !!!cp ('t367');              !!!cp ('t367');
4651              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4652              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7188  sub _tree_construction_main ($) { Line 4668  sub _tree_construction_main ($) {
4668        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4669          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4670            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4671            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4672              !!!cp ('t371');              !!!cp ('t371');
4673              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4674                            
# Line 7232  sub _tree_construction_main ($) { Line 4712  sub _tree_construction_main ($) {
4712          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4713          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4714            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4715            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4716              !!!cp ('t376');              !!!cp ('t376');
4717              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4718              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7255  sub _tree_construction_main ($) { Line 4735  sub _tree_construction_main ($) {
4735          ## has a button element in scope          ## has a button element in scope
4736          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4737            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4738            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4739              !!!cp ('t378');              !!!cp ('t378');
4740              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4741              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7320  sub _tree_construction_main ($) { Line 4800  sub _tree_construction_main ($) {
4800                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4801                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4802                           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}},  
4803                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4804                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4805                         );                         );
# Line 7344  sub _tree_construction_main ($) { Line 4822  sub _tree_construction_main ($) {
4822                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4823                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4824                           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}},  
4825                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4826                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4827                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7355  sub _tree_construction_main ($) { Line 4831  sub _tree_construction_main ($) {
4831            next B;            next B;
4832          }          }
4833        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4834          my $tag_name = $token->{tag_name};          ## 1. Insert
4835          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4836                    
4837            ## Step 2 # XXX
4838          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4839    
4840            ## 2. Drop U+000A LINE FEED
4841            $self->{ignore_newline} = 1;
4842    
4843            ## 3. RCDATA
4844          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4845          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4846            
4847          $insert->($el);          ## 4., 6. Insertion mode
4848                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4849          my $text = '';  
4850            ## XXX: 5. frameset-ok flag
4851    
4852          !!!nack ('t392.1');          !!!nack ('t392.1');
4853          !!!next-token;          !!!next-token;
4854          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4855            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4856            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4857              !!!cp ('t392');          ## has an |option| element in scope
4858              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4859            } else {            my $node = $self->{open_elements}->[$_];
4860              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4861                !!!cp ('t397.1');
4862                ## NOTE: As if </option>
4863                !!!back-token; # <option> or <optgroup>
4864                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4865                          line => $token->{line}, column => $token->{column}};
4866                next B;
4867              } elsif ($node->[1] & SCOPING_EL) {
4868                !!!cp ('t397.2');
4869                last INSCOPE;
4870            }            }
4871          } else {          } # INSCOPE
4872            !!!cp ('t394');  
4873          }          $reconstruct_active_formatting_elements->($insert_to_current);
4874          while ($token->{type} == CHARACTER_TOKEN) {  
4875            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4876            $text .= $token->{data};  
4877            !!!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);  
         }  
4878          !!!next-token;          !!!next-token;
4879          next B;          redo B;
4880        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4881                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4882          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4883          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4884            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4885            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4886              !!!cp ('t398.1');              !!!cp ('t398.1');
4887              ## generate implied end tags              ## generate implied end tags
4888              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4889                !!!cp ('t398.2');                !!!cp ('t398.2');
4890                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4891              }              }
4892              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4893                !!!cp ('t398.3');                !!!cp ('t398.3');
4894                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4895                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4896                                    ->manakai_local_name,                                    ->manakai_local_name,
4897                                token => $token);                                token => $token);
4898                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4899                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4900              }              }
4901              last INSCOPE;              last INSCOPE;
4902            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7428  sub _tree_construction_main ($) { Line 4904  sub _tree_construction_main ($) {
4904              last INSCOPE;              last INSCOPE;
4905            }            }
4906          } # INSCOPE          } # INSCOPE
4907              
4908            ## TODO: <non-ruby><rt> is not allowed.
4909    
4910          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4911    
# Line 7448  sub _tree_construction_main ($) { Line 4926  sub _tree_construction_main ($) {
4926                    
4927          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4928            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4929            !!!ack ('t398.1');            !!!ack ('t398.6');
4930          } else {          } else {
4931            !!!cp ('t398.2');            !!!cp ('t398.7');
4932            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
4933            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
4934            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7461  sub _tree_construction_main ($) { Line 4939  sub _tree_construction_main ($) {
4939          next B;          next B;
4940        } elsif ({        } elsif ({
4941                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4942                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
4943                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
4944                  thead => 1, tr => 1,                  thead => 1, tr => 1,
4945                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7472  sub _tree_construction_main ($) { Line 4950  sub _tree_construction_main ($) {
4950          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
4951          !!!next-token;          !!!next-token;
4952          next B;          next B;
4953                  } elsif ($token->{tag_name} eq 'param' or
4954          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
4955            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4956            pop @{$self->{open_elements}};
4957    
4958            !!!ack ('t398.5');
4959            !!!next-token;
4960            redo B;
4961        } else {        } else {
4962          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
4963            !!!cp ('t384');            !!!cp ('t384');
# Line 7509  sub _tree_construction_main ($) { Line 4993  sub _tree_construction_main ($) {
4993            !!!ack ('t388.2');            !!!ack ('t388.2');
4994          } elsif ({          } elsif ({
4995                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
4996                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
4997                    #image => 1,                    keygen => 1,
4998                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
4999            !!!cp ('t388.1');            !!!cp ('t388.1');
5000            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7520  sub _tree_construction_main ($) { Line 5004  sub _tree_construction_main ($) {
5004                    
5005            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5006                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5007                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5008              !!!cp ('t400.1');              !!!cp ('t400.1');
5009              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5010            } else {            } else {
# Line 7536  sub _tree_construction_main ($) { Line 5020  sub _tree_construction_main ($) {
5020          next B;          next B;
5021        }        }
5022      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5023        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body' or $token->{tag_name} eq 'html') {
5024          ## has a |body| element in scope  
5025            ## 1. If not "have an element in scope":
5026            ## "has a |body| element in scope"
5027          my $i;          my $i;
5028          INSCOPE: {          INSCOPE: {
5029            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5030              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5031                !!!cp ('t405');                !!!cp ('t405');
5032                $i = $_;                $i = $_;
5033                last INSCOPE;                last INSCOPE;
# Line 7551  sub _tree_construction_main ($) { Line 5037  sub _tree_construction_main ($) {
5037              }              }
5038            }            }
5039    
5040            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|,
5041              ## and fragment cases.
5042    
5043              !!!parse-error (type => 'unmatched end tag',
5044                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5045            ## NOTE: Ignore the token.            ## Ignore the token.  (</body> or </html>)
5046            !!!next-token;            !!!next-token;
5047            next B;            next B;
5048          } # INSCOPE          } # INSCOPE
5049    
5050            ## 2. If unclosed elements:
5051          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5052            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5053                      $_->[1] == OPTGROUP_EL ||
5054                      $_->[1] == OPTION_EL ||
5055                      $_->[1] == RUBY_COMPONENT_EL) {
5056              !!!cp ('t403');              !!!cp ('t403');
5057              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5058                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7570  sub _tree_construction_main ($) { Line 5063  sub _tree_construction_main ($) {
5063            }            }
5064          }          }
5065    
5066            ## 3. Switch the insertion mode.
5067          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5068          !!!next-token;          if ($token->{tag_name} eq 'body') {
         next B;  
       } elsif ($token->{tag_name} eq 'html') {  
         ## TODO: Update this code.  It seems that the code below is not  
         ## up-to-date, though it has same effect as speced.  
         if (@{$self->{open_elements}} > 1 and  
             $self->{open_elements}->[1]->[1] & BODY_EL) {  
           ## ISSUE: There is an issue in the spec.  
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
             !!!cp ('t406');  
             !!!parse-error (type => 'not closed',  
                             text => $self->{open_elements}->[1]->[0]  
                                 ->manakai_local_name,  
                             token => $token);  
           } else {  
             !!!cp ('t407');  
           }  
           $self->{insertion_mode} = AFTER_BODY_IM;  
           ## reprocess  
           next B;  
         } else {  
           !!!cp ('t408');  
           !!!parse-error (type => 'unmatched end tag',  
                           text => $token->{tag_name}, token => $token);  
           ## Ignore the token  
5069            !!!next-token;            !!!next-token;
5070            next B;          } else { # html
5071              ## Reprocess.
5072          }          }
5073            next B;
5074        } elsif ({        } elsif ({
5075                  ## NOTE: End tags for non-phrasing flow content elements                  ## NOTE: End tags for non-phrasing flow content elements
5076    
# Line 7606  sub _tree_construction_main ($) { Line 5078  sub _tree_construction_main ($) {
5078                  address => 1, article => 1, aside => 1, blockquote => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
5079                  center => 1, datagrid => 1, details => 1, dialog => 1,                  center => 1, datagrid => 1, details => 1, dialog => 1,
5080                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
5081                  footer => 1, header => 1, listing => 1, menu => 1, nav => 1,                  footer => 1, header => 1, hgroup => 1,
5082                    listing => 1, menu => 1, nav => 1,
5083                  ol => 1, pre => 1, section => 1, ul => 1,                  ol => 1, pre => 1, section => 1, ul => 1,
5084    
5085                  ## NOTE: As normal, but ... optional tags                  ## NOTE: As normal, but ... optional tags
# Line 7686  sub _tree_construction_main ($) { Line 5159  sub _tree_construction_main ($) {
5159          my $i;          my $i;
5160          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5161            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5162            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5163              !!!cp ('t418');              !!!cp ('t418');
5164              $i = $_;              $i = $_;
5165              last INSCOPE;              last INSCOPE;
# Line 7734  sub _tree_construction_main ($) { Line 5207  sub _tree_construction_main ($) {
5207          my $i;          my $i;
5208          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5209            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5210            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5211              !!!cp ('t423');              !!!cp ('t423');
5212              $i = $_;              $i = $_;
5213              last INSCOPE;              last INSCOPE;
# Line 7780  sub _tree_construction_main ($) { Line 5253  sub _tree_construction_main ($) {
5253          my $i;          my $i;
5254          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5255            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5256            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5257              !!!cp ('t410.1');              !!!cp ('t410.1');
5258              $i = $_;              $i = $_;
5259              last INSCOPE;              last INSCOPE;
# Line 7853  sub _tree_construction_main ($) { Line 5326  sub _tree_construction_main ($) {
5326          ## Ignore the token.          ## Ignore the token.
5327          !!!next-token;          !!!next-token;
5328          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;  
5329        } else {        } else {
5330          if ($token->{tag_name} eq 'sarcasm') {          if ($token->{tag_name} eq 'sarcasm') {
5331            sleep 0.001; # take a deep breath            sleep 0.001; # take a deep breath
# Line 7882  sub _tree_construction_main ($) { Line 5337  sub _tree_construction_main ($) {
5337    
5338          ## Step 2          ## Step 2
5339          S2: {          S2: {
5340            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5341              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5342              if ($node_tag_name eq $token->{tag_name}) {
5343              ## Step 1              ## Step 1
5344              ## generate implied end tags              ## generate implied end tags
5345              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7895  sub _tree_construction_main ($) { Line 5352  sub _tree_construction_main ($) {
5352              }              }
5353                    
5354              ## Step 2              ## Step 2
5355              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5356                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5357                $current_tag_name =~ tr/A-Z/a-z/;
5358                if ($current_tag_name ne $token->{tag_name}) {
5359                !!!cp ('t431');                !!!cp ('t431');
5360                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5361                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7971  sub _tree_construction_main ($) { Line 5430  sub _tree_construction_main ($) {
5430    ## TODO: script stuffs    ## TODO: script stuffs
5431  } # _tree_construct_main  } # _tree_construct_main
5432    
5433    ## XXX: How this method is organized is somewhat out of date, although
5434    ## it still does what the current spec documents.
5435  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5436    my $class = shift;    my $class = shift;
5437    my $node = shift;    my $node = shift; # /context/
5438    #my $s = \$_[0];    #my $s = \$_[0];
5439    my $onerror = $_[1];    my $onerror = $_[1];
5440    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
5441    
   ## ISSUE: Should {confident} be true?  
   
5442    my $nt = $node->node_type;    my $nt = $node->node_type;
5443    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5444      # MUST      # MUST
5445            
5446      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7996  sub set_inner_html ($$$$;$) { Line 5455  sub set_inner_html ($$$$;$) {
5455    
5456      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5457      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5458    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5459      ## TODO: If non-html element      ## TODO: If non-html element
5460    
5461      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5462    
5463  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5464    
5465      ## Step 1 # MUST      ## F1. Create an HTML document.
5466      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5467      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5468      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5469    
5470        ## F2. Propagate quirkness flag
5471        my $node_doc = $node->owner_document;
5472        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5473    
5474        ## F3. Create an HTML parser
5475      my $p = $class->new;      my $p = $class->new;
5476      $p->{document} = $doc;      $p->{document} = $doc;
5477    
# Line 8134  sub set_inner_html ($$$$;$) { Line 5599  sub set_inner_html ($$$$;$) {
5599      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5600      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5601    
5602      ## Step 2      ## F4. If /context/ is not undef...
5603    
5604        ## F4.1. content model flag
5605      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5606      $p->{content_model} = {      $p->{content_model} = {
5607        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8150  sub set_inner_html ($$$$;$) { Line 5617  sub set_inner_html ($$$$;$) {
5617      }->{$node_ln};      }->{$node_ln};
5618      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5619          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5620    
5621      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5622        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5623    
5624      ## Step 3      ## F4.2. Root |html| element
5625      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5626        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5627    
5628      ## Step 4 # MUST      ## F4.3.
5629      $doc->append_child ($root);      $doc->append_child ($root);
5630    
5631      ## Step 5 # MUST      ## F4.4.
5632      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5633    
5634      undef $p->{head_element};      undef $p->{head_element};
5635        undef $p->{head_element_inserted};
5636    
5637      ## Step 6 # MUST      ## F4.5.
5638      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5639    
5640      ## Step 7 # MUST      ## F4.6.
5641      my $anode = $node;      my $anode = $node;
5642      AN: while (defined $anode) {      AN: while (defined $anode) {
5643        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8185  sub set_inner_html ($$$$;$) { Line 5652  sub set_inner_html ($$$$;$) {
5652        }        }
5653        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5654      } # AN      } # AN
5655        
5656      ## Step 9 # MUST      ## F.5. Set the input stream.
5657        $p->{confident} = 1; ## Confident: irrelevant.
5658    
5659        ## F.6. Start the parser.
5660      {      {
5661        my $self = $p;        my $self = $p;
5662        !!!next-token;        !!!next-token;
5663      }      }
5664      $p->_tree_construction_main;      $p->_tree_construction_main;
5665    
5666      ## Step 10 # MUST      ## F.7.
5667      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5668      for (@cn) {      for (@cn) {
5669        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.197  
changed lines
  Added in v.1.241

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24