/[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.204 by wakaba, Sun Oct 5 05:59:35 2008 UTC revision 1.243 by wakaba, Sun Sep 6 13:52:06 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 |  
   
   ## ISSUE: option, optgroup, rt, rp?  
   
   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    SELECT_EL |    TABLE_ROWS_EL |
128    TABLE_ROW_EL |    TABLE_SCOPING_EL |
129    TABLE_ROW_GROUP_EL |    0b001
   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 | MISC_SCOPING_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 559  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 580  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 673  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 834  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 914  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 944  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};  
         $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);  
         $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 3447  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 3474  sub _construct_tree ($) { Line 898  sub _construct_tree ($) {
898    undef $self->{head_element_inserted};    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 3493  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 3814  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 3843  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 3975  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;  
     }  
1416    
1417      ## Step 5      !!!nack ('t40.1');
     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;  
   
     ## Step 7  
     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.    ## 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    
# Line 4248  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 4322  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 4329  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 4374  sub _tree_construction_main ($) { Line 1734  sub _tree_construction_main ($) {
1734    ## document.write ("b")</script>|    ## 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 4424  sub _tree_construction_main ($) { Line 1858  sub _tree_construction_main ($) {
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');
1937    
1938          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
1939    
1940            if ($token->{data} =~ /[^\x09\x0A\x0C\x0D\x20]/) {
1941              delete $self->{frameset_ok};
1942            }
1943    
1944          !!!next-token;          !!!next-token;
1945          next B;          next B;
1946        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
# Line 4435  sub _tree_construction_main ($) { Line 1948  sub _tree_construction_main ($) {
1948               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1949              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1950              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1951               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1952            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1953            !!!cp ('t87.2');            !!!cp ('t87.2');
1954            #            #
1955          } elsif ({          } elsif ({
1956                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1957                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1958                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1959                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1960                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1961                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1962                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1963                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1964                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1965                     ($token->{tag_name} eq 'font' and
1966                      ($token->{attributes}->{color} or
1967                       $token->{attributes}->{face} or
1968                       $token->{attributes}->{size}))) {
1969            !!!cp ('t87.2');            !!!cp ('t87.2');
1970            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1971                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4524  sub _tree_construction_main ($) { Line 2041  sub _tree_construction_main ($) {
2041          }          }
2042        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2043          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
2044          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
2045          #            !!!cp ('t87.41');
2046              #
2047              ## XXXscript: Execute script here.
2048            } else {
2049              !!!cp ('t87.5');
2050              #
2051            }
2052        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2053          !!!cp ('t87.6');          !!!cp ('t87.6');
2054          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4612  sub _tree_construction_main ($) { Line 2135  sub _tree_construction_main ($) {
2135          ## As if <body>          ## As if <body>
2136          !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
2137          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
2138          ## reprocess          ## The "frameset-ok" flag is left unchanged in this case.
2139            ## Reporcess the token.
2140          next B;          next B;
2141        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
2142          if ($token->{tag_name} eq 'head') {          if ($token->{tag_name} eq 'head') {
# Line 4709  sub _tree_construction_main ($) { Line 2233  sub _tree_construction_main ($) {
2233            !!!ack ('t103.1');            !!!ack ('t103.1');
2234            !!!next-token;            !!!next-token;
2235            next B;            next B;
2236          } elsif ($token->{tag_name} eq 'command' or          } elsif ($token->{tag_name} eq 'command') {
                  $token->{tag_name} eq 'eventsource') {  
2237            if ($self->{insertion_mode} == IN_HEAD_IM) {            if ($self->{insertion_mode} == IN_HEAD_IM) {
2238              ## NOTE: If the insertion mode at the time of the emission              ## NOTE: If the insertion mode at the time of the emission
2239              ## of the token was "before head", $self->{insertion_mode}              ## of the token was "before head", $self->{insertion_mode}
# Line 4825  sub _tree_construction_main ($) { Line 2348  sub _tree_construction_main ($) {
2348    
2349            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2350            $parse_rcdata->(RCDATA_CONTENT_MODEL);            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2351            pop @{$self->{open_elements}} # <head>  
2352                if $self->{insertion_mode} == AFTER_HEAD_IM;            ## NOTE: At this point the stack of open elements contain
2353              ## the |head| element (index == -2) and the |script| element
2354              ## (index == -1).  In the "after head" insertion mode the
2355              ## |head| element is inserted only for the purpose of
2356              ## providing the context for the |script| element, and
2357              ## therefore we can now and have to remove the element from
2358              ## the stack.
2359              splice @{$self->{open_elements}}, -2, 1, () # <head>
2360                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2361            next B;            next B;
2362          } elsif ($token->{tag_name} eq 'style' or          } elsif ($token->{tag_name} eq 'style' or
2363                   $token->{tag_name} eq 'noframes') {                   $token->{tag_name} eq 'noframes') {
# Line 4844  sub _tree_construction_main ($) { Line 2375  sub _tree_construction_main ($) {
2375              !!!cp ('t115');              !!!cp ('t115');
2376            }            }
2377            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
2378            pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug [Bug 6038]
2379                if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1, () # <head>
2380                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2381            next B;            next B;
2382              } elsif ($token->{tag_name} eq 'noscript') {          } elsif ($token->{tag_name} eq 'noscript') {
2383                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2384                  !!!cp ('t116');                  !!!cp ('t116');
2385                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4891  sub _tree_construction_main ($) { Line 2423  sub _tree_construction_main ($) {
2423    
2424            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2425            $script_start_tag->();            $script_start_tag->();
2426            pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2427                if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2428                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2429            next B;            next B;
2430          } elsif ($token->{tag_name} eq 'body' or          } elsif ($token->{tag_name} eq 'body' or
2431                   $token->{tag_name} eq 'frameset') {                   $token->{tag_name} eq 'frameset') {
2432                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2433                  !!!cp ('t122');              !!!cp ('t122');
2434                  ## As if </noscript>              ## As if </noscript>
2435                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2436                  !!!parse-error (type => 'in noscript',              !!!parse-error (type => 'in noscript',
2437                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2438                                
2439                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2440                  ## As if </head>              ## As if </head>
2441                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2442                                
2443                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2444                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2445                  !!!cp ('t124');              !!!cp ('t124');
2446                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2447                                
2448                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2449                } else {            } else {
2450                  !!!cp ('t125');              !!!cp ('t125');
2451                }            }
2452    
2453                ## "after head" insertion mode            ## "after head" insertion mode
2454                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2455                if ($token->{tag_name} eq 'body') {            if ($token->{tag_name} eq 'body') {
2456                  !!!cp ('t126');              !!!cp ('t126');
2457                  $self->{insertion_mode} = IN_BODY_IM;              delete $self->{frameset_ok};
2458                } elsif ($token->{tag_name} eq 'frameset') {              $self->{insertion_mode} = IN_BODY_IM;
2459                  !!!cp ('t127');            } elsif ($token->{tag_name} eq 'frameset') {
2460                  $self->{insertion_mode} = IN_FRAMESET_IM;              !!!cp ('t127');
2461                } else {              $self->{insertion_mode} = IN_FRAMESET_IM;
2462                  die "$0: tag name: $self->{tag_name}";            } else {
2463                }              die "$0: tag name: $self->{tag_name}";
2464                !!!nack ('t127.1');            }
2465                !!!next-token;            !!!nack ('t127.1');
2466                next B;            !!!next-token;
2467              } else {            next B;
2468                !!!cp ('t128');          } else {
2469                #            !!!cp ('t128');
2470              }            #
2471            }
2472    
2473              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2474                !!!cp ('t129');                !!!cp ('t129');
# Line 4958  sub _tree_construction_main ($) { Line 2492  sub _tree_construction_main ($) {
2492                !!!cp ('t131');                !!!cp ('t131');
2493              }              }
2494    
2495              ## "after head" insertion mode          ## "after head" insertion mode
2496              ## As if <body>          ## As if <body>
2497              !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
2498              $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
2499              ## reprocess          ## The "frameset-ok" flag is not changed in this case.
2500              !!!ack-later;          ## Reprocess the token.
2501              next B;          !!!ack-later;
2502            } elsif ($token->{type} == END_TAG_TOKEN) {          next B;
2503              if ($token->{tag_name} eq 'head') {        } elsif ($token->{type} == END_TAG_TOKEN) {
2504                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          ## "Before head", "in head", and "after head" insertion modes
2505                  !!!cp ('t132');          ## ignore most of end tags.  Exceptions are "body", "html",
2506                  ## As if <head>          ## and "br" end tags.  "Before head" and "in head" insertion
2507                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);          ## modes also recognize "head" end tag.  "In head noscript"
2508                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});          ## insertion modes ignore end tags except for "noscript" and
2509                  push @{$self->{open_elements}},          ## "br".
                     [$self->{head_element}, $el_category->{head}];  
2510    
2511                  ## Reprocess in the "in head" insertion mode...          if ($token->{tag_name} eq 'head') {
2512                  pop @{$self->{open_elements}};            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2513                  $self->{insertion_mode} = AFTER_HEAD_IM;              !!!cp ('t132');
2514                  !!!next-token;              ## As if <head>
2515                  next B;              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2516                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2517                  !!!cp ('t133');              push @{$self->{open_elements}},
2518                  ## As if </noscript>                  [$self->{head_element}, $el_category->{head}];
2519                  pop @{$self->{open_elements}};  
2520                  !!!parse-error (type => 'in noscript:/',              ## Reprocess in the "in head" insertion mode...
2521                                  text => 'head', token => $token);              pop @{$self->{open_elements}};
2522                                $self->{insertion_mode} = AFTER_HEAD_IM;
2523                  ## Reprocess in the "in head" insertion mode...              !!!next-token;
2524                  pop @{$self->{open_elements}};              next B;
2525                  $self->{insertion_mode} = AFTER_HEAD_IM;            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2526                  !!!next-token;              !!!cp ('t133');
2527                  next B;              #
2528                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2529                  !!!cp ('t134');              !!!cp ('t134');
2530                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2531                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2532                  !!!next-token;              !!!next-token;
2533                  next B;              next B;
2534                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2535                  !!!cp ('t134.1');              !!!cp ('t134.1');
2536                  !!!parse-error (type => 'unmatched end tag', text => 'head',              #
2537                                  token => $token);            } else {
2538                  ## Ignore the token              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2539                  !!!next-token;            }
2540                  next B;          } elsif ($token->{tag_name} eq 'noscript') {
2541                } else {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2542                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              !!!cp ('t136');
2543                }              pop @{$self->{open_elements}};
2544              } elsif ($token->{tag_name} eq 'noscript') {              $self->{insertion_mode} = IN_HEAD_IM;
2545                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              !!!next-token;
2546                  !!!cp ('t136');              next B;
2547                  pop @{$self->{open_elements}};            } else {
2548                  $self->{insertion_mode} = IN_HEAD_IM;              !!!cp ('t138');
2549                  !!!next-token;              #
2550                  next B;            }
2551                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or          } elsif ({
2552                         $self->{insertion_mode} == AFTER_HEAD_IM) {              body => ($self->{insertion_mode} != IN_HEAD_NOSCRIPT_IM),
2553                  !!!cp ('t137');              html => ($self->{insertion_mode} != IN_HEAD_NOSCRIPT_IM),
2554                  !!!parse-error (type => 'unmatched end tag',              br => 1,
2555                                  text => 'noscript', token => $token);          }->{$token->{tag_name}}) {
2556                  ## Ignore the token ## ISSUE: An issue in the spec.            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2557                  !!!next-token;              !!!cp ('t142.2');
2558                  next B;              ## (before head) as if <head>, (in head) as if </head>
2559                } else {              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2560                  !!!cp ('t138');              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2561                  #              $self->{insertion_mode} = AFTER_HEAD_IM;
               }  
             } elsif ({  
                       body => 1, html => 1,  
                      }->{$token->{tag_name}}) {  
               ## TODO: This branch is entirely redundant.  
               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;  
2562        
2563                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2564                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2565                  !!!cp ('t143.2');              !!!cp ('t143.2');
2566                  ## As if </head>              ## As if </head>
2567                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2568                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2569        
2570                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2571                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2572                  !!!cp ('t143.3');              !!!cp ('t143.3');
2573                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2574                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2575                                  text => 'br', token => $token);                              text => $token->{tag_name}, token => $token);
2576                  ## As if </noscript>              ## As if </noscript>
2577                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2578                  $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;  
             }  
2579    
2580              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {              ## Reprocess in the "in head" insertion mode...
2581                !!!cp ('t146');              ## As if </head>
2582                ## As if </noscript>              pop @{$self->{open_elements}};
2583                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}};  
2584    
2585                ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2586              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2587                !!!cp ('t147');              !!!cp ('t143.4');
2588                ## As if </head>              #
2589                pop @{$self->{open_elements}};            } else {
2590                die "$0: $self->{insertion_mode}: Unknown insertion mode";
2591              }
2592    
2593                ## Reprocess in the "after head" insertion mode...            ## "after head" insertion mode
2594              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {            ## As if <body>
2595  ## ISSUE: This case cannot be reached?            !!!insert-element ('body',, $token);
2596                !!!cp ('t148');            $self->{insertion_mode} = IN_BODY_IM;
2597                !!!parse-error (type => 'unmatched end tag',            ## The "frameset-ok" flag is left unchanged in this case.
2598                                text => $token->{tag_name}, token => $token);            ## Reprocess the token.
2599                ## Ignore the token ## ISSUE: An issue in the spec.            next B;
2600                !!!next-token;          }
               next B;  
             } else {  
               !!!cp ('t149');  
             }  
2601    
2602              ## "after head" insertion mode          ## End tags are ignored by default.
2603              ## As if <body>          !!!cp ('t145');
2604              !!!insert-element ('body',, $token);          !!!parse-error (type => 'unmatched end tag',
2605              $self->{insertion_mode} = IN_BODY_IM;                          text => $token->{tag_name}, token => $token);
2606              ## reprocess          ## Ignore the token.
2607              next B;          !!!next-token;
2608            next B;
2609        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2610          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2611            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5198  sub _tree_construction_main ($) { Line 2658  sub _tree_construction_main ($) {
2658          ## NOTE: As if <body>          ## NOTE: As if <body>
2659          !!!insert-element ('body',, $token);          !!!insert-element ('body',, $token);
2660          $self->{insertion_mode} = IN_BODY_IM;          $self->{insertion_mode} = IN_BODY_IM;
2661          ## NOTE: Reprocess.          ## The "frameset-ok" flag is left unchanged in this case.
2662            ## Reprocess the token.
2663          next B;          next B;
2664        } else {        } else {
2665          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2666        }        }
2667      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2668            if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
2669              !!!cp ('t150');          !!!cp ('t150');
2670              ## NOTE: There is a code clone of "character in body".          $reconstruct_active_formatting_elements->($insert_to_current);
2671              $reconstruct_active_formatting_elements->($insert_to_current);          
2672                        $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
2673    
2674              !!!next-token;          if ($token->{data} =~ /[^\x09\x0A\x0C\x0D\x20]/) {
2675              next B;            delete $self->{frameset_ok};
2676            } elsif ($token->{type} == START_TAG_TOKEN) {          }
2677    
2678            !!!next-token;
2679            next B;
2680          } elsif ($token->{type} == START_TAG_TOKEN) {
2681              if ({              if ({
2682                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2683                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2684                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2685                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2686                  ## have an element in table scope                  ## have an element in table scope
2687                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2688                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2689                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2690                      !!!cp ('t151');                      !!!cp ('t151');
2691    
2692                      ## Close the cell                      ## Close the cell
# Line 5246  sub _tree_construction_main ($) { Line 2710  sub _tree_construction_main ($) {
2710                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2711                  !!!next-token;                  !!!next-token;
2712                  next B;                  next B;
2713                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2714                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2715                                  token => $token);                                  token => $token);
2716                                    
# Line 5256  sub _tree_construction_main ($) { Line 2720  sub _tree_construction_main ($) {
2720                  INSCOPE: {                  INSCOPE: {
2721                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2722                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2723                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2724                        !!!cp ('t155');                        !!!cp ('t155');
2725                        $i = $_;                        $i = $_;
2726                        last INSCOPE;                        last INSCOPE;
# Line 5282  sub _tree_construction_main ($) { Line 2746  sub _tree_construction_main ($) {
2746                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2747                  }                  }
2748    
2749                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2750                    !!!cp ('t159');                    !!!cp ('t159');
2751                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2752                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5311  sub _tree_construction_main ($) { Line 2775  sub _tree_construction_main ($) {
2775              }              }
2776            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2777              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2778                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2779                  ## have an element in table scope                  ## have an element in table scope
2780                  my $i;                  my $i;
2781                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5361  sub _tree_construction_main ($) { Line 2825  sub _tree_construction_main ($) {
2825                                    
2826                  !!!next-token;                  !!!next-token;
2827                  next B;                  next B;
2828                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2829                  !!!cp ('t169');                  !!!cp ('t169');
2830                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2831                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5373  sub _tree_construction_main ($) { Line 2837  sub _tree_construction_main ($) {
2837                  #                  #
2838                }                }
2839              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2840                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2841                  ## have a table element in table scope                  ## have a table element in table scope
2842                  my $i;                  my $i;
2843                  INSCOPE: {                  INSCOPE: {
2844                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2845                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2846                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2847                        !!!cp ('t171');                        !!!cp ('t171');
2848                        $i = $_;                        $i = $_;
2849                        last INSCOPE;                        last INSCOPE;
# Line 5404  sub _tree_construction_main ($) { Line 2868  sub _tree_construction_main ($) {
2868                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2869                  }                  }
2870                                    
2871                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2872                    !!!cp ('t175');                    !!!cp ('t175');
2873                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2874                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5422  sub _tree_construction_main ($) { Line 2886  sub _tree_construction_main ($) {
2886                                    
2887                  !!!next-token;                  !!!next-token;
2888                  next B;                  next B;
2889                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2890                  !!!cp ('t177');                  !!!cp ('t177');
2891                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2892                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5437  sub _tree_construction_main ($) { Line 2901  sub _tree_construction_main ($) {
2901                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2902                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2903                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2904                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2905                ## have an element in table scope                ## have an element in table scope
2906                my $i;                my $i;
2907                my $tn;                my $tn;
# Line 5454  sub _tree_construction_main ($) { Line 2918  sub _tree_construction_main ($) {
2918                                line => $token->{line},                                line => $token->{line},
2919                                column => $token->{column}};                                column => $token->{column}};
2920                      next B;                      next B;
2921                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2922                      !!!cp ('t180');                      !!!cp ('t180');
2923                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2924                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5474  sub _tree_construction_main ($) { Line 2938  sub _tree_construction_main ($) {
2938                  next B;                  next B;
2939                } # INSCOPE                } # INSCOPE
2940              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2941                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2942                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2943                                token => $token);                                token => $token);
2944    
# Line 5483  sub _tree_construction_main ($) { Line 2947  sub _tree_construction_main ($) {
2947                my $i;                my $i;
2948                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2949                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
2950                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
2951                    !!!cp ('t184');                    !!!cp ('t184');
2952                    $i = $_;                    $i = $_;
2953                    last INSCOPE;                    last INSCOPE;
# Line 5494  sub _tree_construction_main ($) { Line 2958  sub _tree_construction_main ($) {
2958                } # INSCOPE                } # INSCOPE
2959                unless (defined $i) {                unless (defined $i) {
2960                  !!!cp ('t186');                  !!!cp ('t186');
2961            ## TODO: Wrong error type?
2962                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2963                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
2964                  ## Ignore the token                  ## Ignore the token
# Line 5507  sub _tree_construction_main ($) { Line 2972  sub _tree_construction_main ($) {
2972                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2973                }                }
2974    
2975                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2976                  !!!cp ('t188');                  !!!cp ('t188');
2977                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
2978                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5539  sub _tree_construction_main ($) { Line 3004  sub _tree_construction_main ($) {
3004                  !!!cp ('t191');                  !!!cp ('t191');
3005                  #                  #
3006                }                }
3007              } elsif ({          } elsif ({
3008                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
3009                        thead => 1, tr => 1,                    thead => 1, tr => 1,
3010                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
3011                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
3012                !!!cp ('t192');            !!!cp ('t192');
3013                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
3014                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
3015                ## Ignore the token            ## Ignore the token
3016                !!!next-token;            !!!next-token;
3017                next B;            next B;
3018              } else {          } else {
3019                !!!cp ('t193');            !!!cp ('t193');
3020                #            #
3021              }          }
3022        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3023          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3024            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5572  sub _tree_construction_main ($) { Line 3037  sub _tree_construction_main ($) {
3037        $insert = $insert_to_current;        $insert = $insert_to_current;
3038        #        #
3039      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3040        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);  
   
         ## NOTE: As if in body, but insert into the foster parent element.  
         $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) {  
                 $foster_parent_element = $parent;  
                 !!!cp ('t196');  
                 $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;  
           undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted  
           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  
           $open_tables->[-1]->[2] = 1; # ~node inserted  
         } else {  
           ## NOTE: Fragment case or in a foster parent'ed element  
           ## (e.g. |<table><span>a|).  In fragment case, whether the  
           ## character is appended to existing node or a new node is  
           ## created is irrelevant, since the foster parent'ed nodes  
           ## are discarded and fragment parsing does not invoke any  
           ## script.  
           !!!cp ('t200');  
           $self->{open_elements}->[-1]->[0]->manakai_append_text  
               ($token->{data});  
         }  
               
         !!!next-token;  
         next B;  
       } elsif ($token->{type} == START_TAG_TOKEN) {  
3041          if ({          if ({
3042               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3043               th => 1, td => 1,               th => 1, td => 1,
3044              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3045            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3046              ## Clear back to table context              ## Clear back to table context
3047              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3048                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5662  sub _tree_construction_main ($) { Line 3055  sub _tree_construction_main ($) {
3055              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3056            }            }
3057                        
3058            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3059              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3060                !!!cp ('t202');                !!!cp ('t202');
3061                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5714  sub _tree_construction_main ($) { Line 3107  sub _tree_construction_main ($) {
3107                    tbody => 1, tfoot => 1, thead => 1,                    tbody => 1, tfoot => 1, thead => 1,
3108                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3109                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
3110            if ($self->{insertion_mode} == IN_ROW_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3111              ## As if </tr>              ## As if </tr>
3112              ## have an element in table scope              ## have an element in table scope
3113              my $i;              my $i;
3114              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3115                my $node = $self->{open_elements}->[$_];                my $node = $self->{open_elements}->[$_];
3116                if ($node->[1] & TABLE_ROW_EL) {                if ($node->[1] == TABLE_ROW_EL) {
3117                  !!!cp ('t208');                  !!!cp ('t208');
3118                  $i = $_;                  $i = $_;
3119                  last INSCOPE;                  last INSCOPE;
# Line 5761  sub _tree_construction_main ($) { Line 3154  sub _tree_construction_main ($) {
3154                  }                  }
3155                }                }
3156    
3157                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3158                  ## have an element in table scope                  ## have an element in table scope
3159                  my $i;                  my $i;
3160                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3161                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3162                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3163                      !!!cp ('t214');                      !!!cp ('t214');
3164                      $i = $_;                      $i = $_;
3165                      last INSCOPE;                      last INSCOPE;
# Line 5865  sub _tree_construction_main ($) { Line 3258  sub _tree_construction_main ($) {
3258                my $i;                my $i;
3259                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3260                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3261                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3262                    !!!cp ('t221');                    !!!cp ('t221');
3263                    $i = $_;                    $i = $_;
3264                    last INSCOPE;                    last INSCOPE;
# Line 5892  sub _tree_construction_main ($) { Line 3285  sub _tree_construction_main ($) {
3285                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3286                }                }
3287    
3288                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3289                  !!!cp ('t225');                  !!!cp ('t225');
3290                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3291                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5912  sub _tree_construction_main ($) { Line 3305  sub _tree_construction_main ($) {
3305            !!!ack-later;            !!!ack-later;
3306            next B;            next B;
3307          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
3308            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.8');
3309              !!!cp ('t227.8');            ## NOTE: This is a "as if in head" code clone.
3310              ## NOTE: This is a "as if in head" code clone.            $parse_rcdata->(CDATA_CONTENT_MODEL);
3311              $parse_rcdata->(CDATA_CONTENT_MODEL);            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3312              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted            next B;
             next B;  
           } else {  
             !!!cp ('t227.7');  
             #  
           }  
3313          } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
3314            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.6');
3315              !!!cp ('t227.6');            ## NOTE: This is a "as if in head" code clone.
3316              ## NOTE: This is a "as if in head" code clone.            $script_start_tag->();
3317              $script_start_tag->();            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3318              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted            next B;
             next B;  
           } else {  
             !!!cp ('t227.5');  
             #  
           }  
3319          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
3320            if (not $open_tables->[-1]->[1]) { # tainted            if ($token->{attributes}->{type}) {
3321              if ($token->{attributes}->{type}) { ## TODO: case              my $type = $token->{attributes}->{type}->{value};
3322                my $type = lc $token->{attributes}->{type}->{value};              $type =~ tr/A-Z/a-z/; ## ASCII case-insensitive.
3323                if ($type eq 'hidden') {              if ($type eq 'hidden') {
3324                  !!!cp ('t227.3');                !!!cp ('t227.3');
3325                  !!!parse-error (type => 'in table',                !!!parse-error (type => 'in table',
3326                                  text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
3327    
3328                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3329                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3330    
3331                  ## TODO: form element pointer                ## TODO: form element pointer
3332    
3333                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3334    
3335                  !!!next-token;                !!!next-token;
3336                  !!!ack ('t227.2.1');                !!!ack ('t227.2.1');
3337                  next B;                next B;
               } else {  
                 !!!cp ('t227.2');  
                 #  
               }  
3338              } else {              } else {
3339                !!!cp ('t227.1');                !!!cp ('t227.1');
3340                #                #
# Line 5975  sub _tree_construction_main ($) { Line 3354  sub _tree_construction_main ($) {
3354          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3355          #          #
3356        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3357              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3358                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3359                ## have an element in table scope            ## have an element in table scope
3360                my $i;                my $i;
3361                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3362                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3363                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3364                    !!!cp ('t228');                    !!!cp ('t228');
3365                    $i = $_;                    $i = $_;
3366                    last INSCOPE;                    last INSCOPE;
# Line 6016  sub _tree_construction_main ($) { Line 3395  sub _tree_construction_main ($) {
3395                !!!nack ('t231.1');                !!!nack ('t231.1');
3396                next B;                next B;
3397              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3398                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3399                  ## As if </tr>                  ## As if </tr>
3400                  ## have an element in table scope                  ## have an element in table scope
3401                  my $i;                  my $i;
3402                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3403                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3404                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3405                      !!!cp ('t233');                      !!!cp ('t233');
3406                      $i = $_;                      $i = $_;
3407                      last INSCOPE;                      last INSCOPE;
# Line 6055  sub _tree_construction_main ($) { Line 3434  sub _tree_construction_main ($) {
3434                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3435                }                }
3436    
3437                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3438                  ## have an element in table scope                  ## have an element in table scope
3439                  my $i;                  my $i;
3440                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3441                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3442                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3443                      !!!cp ('t237');                      !!!cp ('t237');
3444                      $i = $_;                      $i = $_;
3445                      last INSCOPE;                      last INSCOPE;
# Line 6107  sub _tree_construction_main ($) { Line 3486  sub _tree_construction_main ($) {
3486                my $i;                my $i;
3487                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3488                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3489                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3490                    !!!cp ('t241');                    !!!cp ('t241');
3491                    $i = $_;                    $i = $_;
3492                    last INSCOPE;                    last INSCOPE;
# Line 6137  sub _tree_construction_main ($) { Line 3516  sub _tree_construction_main ($) {
3516                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3517                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3518                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3519                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3520                  ## have an element in table scope                  ## have an element in table scope
3521                  my $i;                  my $i;
3522                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6166  sub _tree_construction_main ($) { Line 3545  sub _tree_construction_main ($) {
3545                  my $i;                  my $i;
3546                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3547                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3548                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3549                      !!!cp ('t250');                      !!!cp ('t250');
3550                      $i = $_;                      $i = $_;
3551                      last INSCOPE;                      last INSCOPE;
# Line 6256  sub _tree_construction_main ($) { Line 3635  sub _tree_construction_main ($) {
3635            #            #
3636          }          }
3637        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3638          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3639                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3640            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3641            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6271  sub _tree_construction_main ($) { Line 3650  sub _tree_construction_main ($) {
3650        } else {        } else {
3651          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3652        }        }
3653      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3654            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3655              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3656                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6298  sub _tree_construction_main ($) { Line 3677  sub _tree_construction_main ($) {
3677              }              }
3678            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3679              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3680                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3681                  !!!cp ('t264');                  !!!cp ('t264');
3682                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3683                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6324  sub _tree_construction_main ($) { Line 3703  sub _tree_construction_main ($) {
3703                #                #
3704              }              }
3705        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3706          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3707              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3708            !!!cp ('t270.2');            !!!cp ('t270.2');
3709            ## Stop parsing.            ## Stop parsing.
# Line 6342  sub _tree_construction_main ($) { Line 3721  sub _tree_construction_main ($) {
3721        }        }
3722    
3723            ## As if </colgroup>            ## As if </colgroup>
3724            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3725              !!!cp ('t269');              !!!cp ('t269');
3726  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3727              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6367  sub _tree_construction_main ($) { Line 3746  sub _tree_construction_main ($) {
3746          next B;          next B;
3747        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3748          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3749            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3750              !!!cp ('t272');              !!!cp ('t272');
3751              ## As if </option>              ## As if </option>
3752              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6380  sub _tree_construction_main ($) { Line 3759  sub _tree_construction_main ($) {
3759            !!!next-token;            !!!next-token;
3760            next B;            next B;
3761          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3762            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3763              !!!cp ('t274');              !!!cp ('t274');
3764              ## As if </option>              ## As if </option>
3765              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6388  sub _tree_construction_main ($) { Line 3767  sub _tree_construction_main ($) {
3767              !!!cp ('t275');              !!!cp ('t275');
3768            }            }
3769    
3770            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3771              !!!cp ('t276');              !!!cp ('t276');
3772              ## As if </optgroup>              ## As if </optgroup>
3773              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6401  sub _tree_construction_main ($) { Line 3780  sub _tree_construction_main ($) {
3780            !!!next-token;            !!!next-token;
3781            next B;            next B;
3782          } elsif ({          } elsif ({
3783                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3784                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3785                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3786                          == IN_SELECT_IN_TABLE_IM and
3787                    {                    {
3788                     caption => 1, table => 1,                     caption => 1, table => 1,
3789                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3790                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3791                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3792            ## TODO: The type below is not good - <select> is replaced by </select>  
3793            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3794                            token => $token);            if ($token->{tag_name} eq 'select') {
3795            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3796            ## as if there were </select> (otherwise).                                token => $token);
3797            ## have an element in table scope            } else {
3798                !!!parse-error (type => 'not closed', text => 'select',
3799                                token => $token);
3800              }
3801    
3802              ## 2./<select>-1. Unless "have an element in table scope" (select):
3803            my $i;            my $i;
3804            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3805              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3806              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3807                !!!cp ('t278');                !!!cp ('t278');
3808                $i = $_;                $i = $_;
3809                last INSCOPE;                last INSCOPE;
# Line 6429  sub _tree_construction_main ($) { Line 3814  sub _tree_construction_main ($) {
3814            } # INSCOPE            } # INSCOPE
3815            unless (defined $i) {            unless (defined $i) {
3816              !!!cp ('t280');              !!!cp ('t280');
3817              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3818                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3819              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3820                  ## case two errors, "select in select" and "unmatched
3821                  ## end tags" are reported to the user, the latter might
3822                  ## be confusing but this is what the spec requires.
3823                  !!!parse-error (type => 'unmatched end tag',
3824                                  text => 'select',
3825                                  token => $token);
3826                }
3827                ## Ignore the token.
3828              !!!nack ('t280.1');              !!!nack ('t280.1');
3829              !!!next-token;              !!!next-token;
3830              next B;              next B;
3831            }            }
3832    
3833              ## 3. Otherwise, as if there were <select>:
3834                                
3835            !!!cp ('t281');            !!!cp ('t281');
3836            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6452  sub _tree_construction_main ($) { Line 3847  sub _tree_construction_main ($) {
3847              ## Reprocess the token.              ## Reprocess the token.
3848              next B;              next B;
3849            }            }
3850            } elsif ($token->{tag_name} eq 'script') {
3851              !!!cp ('t281.3');
3852              ## NOTE: This is an "as if in head" code clone
3853              $script_start_tag->();
3854              next B;
3855          } else {          } else {
3856            !!!cp ('t282');            !!!cp ('t282');
3857            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6463  sub _tree_construction_main ($) { Line 3863  sub _tree_construction_main ($) {
3863          }          }
3864        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3865          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3866            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3867                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3868              !!!cp ('t283');              !!!cp ('t283');
3869              ## As if </option>              ## As if </option>
3870              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3871            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3872              !!!cp ('t284');              !!!cp ('t284');
3873              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3874            } else {            } else {
# Line 6481  sub _tree_construction_main ($) { Line 3881  sub _tree_construction_main ($) {
3881            !!!next-token;            !!!next-token;
3882            next B;            next B;
3883          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3884            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3885              !!!cp ('t286');              !!!cp ('t286');
3886              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3887            } else {            } else {
# Line 6498  sub _tree_construction_main ($) { Line 3898  sub _tree_construction_main ($) {
3898            my $i;            my $i;
3899            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3900              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3901              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3902                !!!cp ('t288');                !!!cp ('t288');
3903                $i = $_;                $i = $_;
3904                last INSCOPE;                last INSCOPE;
# Line 6525  sub _tree_construction_main ($) { Line 3925  sub _tree_construction_main ($) {
3925            !!!nack ('t291.1');            !!!nack ('t291.1');
3926            !!!next-token;            !!!next-token;
3927            next B;            next B;
3928          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3929                         == IN_SELECT_IN_TABLE_IM and
3930                   {                   {
3931                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3932                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6560  sub _tree_construction_main ($) { Line 3961  sub _tree_construction_main ($) {
3961            undef $i;            undef $i;
3962            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3963              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3964              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3965                !!!cp ('t295');                !!!cp ('t295');
3966                $i = $_;                $i = $_;
3967                last INSCOPE;                last INSCOPE;
# Line 6599  sub _tree_construction_main ($) { Line 4000  sub _tree_construction_main ($) {
4000            next B;            next B;
4001          }          }
4002        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4003          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4004                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4005            !!!cp ('t299.1');            !!!cp ('t299.1');
4006            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6786  sub _tree_construction_main ($) { Line 4187  sub _tree_construction_main ($) {
4187        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4188          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4189              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4190            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4191                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4192              !!!cp ('t325');              !!!cp ('t325');
4193              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6800  sub _tree_construction_main ($) { Line 4201  sub _tree_construction_main ($) {
4201            }            }
4202    
4203            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4204                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4205              !!!cp ('t327');              !!!cp ('t327');
4206              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4207            } else {            } else {
# Line 6832  sub _tree_construction_main ($) { Line 4233  sub _tree_construction_main ($) {
4233            next B;            next B;
4234          }          }
4235        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4236          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4237                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4238            !!!cp ('t331.1');            !!!cp ('t331.1');
4239            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6862  sub _tree_construction_main ($) { Line 4263  sub _tree_construction_main ($) {
4263          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4264          next B;          next B;
4265        } elsif ({        } elsif ({
4266                  base => 1, command => 1, eventsource => 1, link => 1,                  base => 1, command => 1, link => 1,
4267                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4268          !!!cp ('t334');          !!!cp ('t334');
4269          ## 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 6935  sub _tree_construction_main ($) { Line 4336  sub _tree_construction_main ($) {
4336          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4337                                
4338          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4339              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4340            !!!cp ('t342');            !!!cp ('t342');
4341            ## Ignore the token            ## Ignore the token
4342          } else {          } else {
# Line 6952  sub _tree_construction_main ($) { Line 4353  sub _tree_construction_main ($) {
4353          !!!nack ('t343.1');          !!!nack ('t343.1');
4354          !!!next-token;          !!!next-token;
4355          next B;          next B;
4356          } elsif ($token->{tag_name} eq 'frameset') {
4357            !!!parse-error (type => 'in body', text => $token->{tag_name},
4358                            token => $token);
4359    
4360            if (@{$self->{open_elements}} == 1 or
4361                not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4362              !!!cp ('t343.2');
4363              ## Ignore the token.
4364            } elsif (not $self->{frameset_ok}) {
4365              !!!cp ('t343.3');
4366              ## Ignore the token.
4367            } else {
4368              !!!cp ('t343.4');
4369              
4370              ## 1. Remove the second element.
4371              my $body = $self->{open_elements}->[1]->[0];
4372              my $body_parent = $body->parent_node;
4373              $body_parent->remove_child ($body) if $body_parent;
4374    
4375              ## 2. Pop nodes.
4376              splice @{$self->{open_elements}}, 1;
4377    
4378              ## 3. Insert.
4379              !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4380    
4381              ## 4. Switch.
4382              $self->{insertion_mode} = IN_FRAMESET_IM;
4383            }
4384    
4385            !!!nack ('t343.5');
4386            !!!next-token;
4387            next B;
4388        } elsif ({        } elsif ({
4389                  ## NOTE: Start tags for non-phrasing flow content elements                  ## NOTE: Start tags for non-phrasing flow content elements
4390    
# Line 6960  sub _tree_construction_main ($) { Line 4393  sub _tree_construction_main ($) {
4393                  center => 1, datagrid => 1, details => 1, dialog => 1,                  center => 1, datagrid => 1, details => 1, dialog => 1,
4394                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
4395                  footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,                  footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
4396                  h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,                  h6 => 1, header => 1, hgroup => 1,
4397                    menu => 1, nav => 1, ol => 1, p => 1,
4398                  section => 1, ul => 1,                  section => 1, ul => 1,
4399                  ## NOTE: As normal, but drops leading newline                  ## NOTE: As normal, but drops leading newline
4400                  pre => 1, listing => 1,                  pre => 1, listing => 1,
# Line 6970  sub _tree_construction_main ($) { Line 4404  sub _tree_construction_main ($) {
4404                  table => 1,                  table => 1,
4405                  hr => 1,                  hr => 1,
4406                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4407    
4408            ## 1. When there is an opening |form| element:
4409          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4410            !!!cp ('t350');            !!!cp ('t350');
4411            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6979  sub _tree_construction_main ($) { Line 4415  sub _tree_construction_main ($) {
4415            next B;            next B;
4416          }          }
4417    
4418          ## has a p element in scope          ## 2. Close the |p| element, if any.
4419          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4420            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4421              !!!cp ('t344');            ## has a p element in scope
4422              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4423              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4424                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4425              next B;                !!!back-token; # <form>
4426            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4427              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4428              last INSCOPE;                next B;
4429                } elsif ($_->[1] & SCOPING_EL) {
4430                  !!!cp ('t345');
4431                  last INSCOPE;
4432                }
4433              } # INSCOPE
4434            }
4435    
4436            ## 3. Close the opening <hn> element, if any.
4437            if ({h1 => 1, h2 => 1, h3 => 1,
4438                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4439              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4440                !!!parse-error (type => 'not closed',
4441                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4442                                token => $token);
4443                pop @{$self->{open_elements}};
4444            }            }
4445          } # INSCOPE          }
4446              
4447            ## 4. Insertion.
4448          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4449          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4450            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 7008  sub _tree_construction_main ($) { Line 4460  sub _tree_construction_main ($) {
4460            } else {            } else {
4461              !!!cp ('t348');              !!!cp ('t348');
4462            }            }
4463    
4464              delete $self->{frameset_ok};
4465          } elsif ($token->{tag_name} eq 'form') {          } elsif ($token->{tag_name} eq 'form') {
4466            !!!cp ('t347.1');            !!!cp ('t347.1');
4467            $self->{form_element} = $self->{open_elements}->[-1]->[0];            $self->{form_element} = $self->{open_elements}->[-1]->[0];
# Line 7017  sub _tree_construction_main ($) { Line 4471  sub _tree_construction_main ($) {
4471          } elsif ($token->{tag_name} eq 'table') {          } elsif ($token->{tag_name} eq 'table') {
4472            !!!cp ('t382');            !!!cp ('t382');
4473            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
4474    
4475              delete $self->{frameset_ok};
4476                        
4477            $self->{insertion_mode} = IN_TABLE_IM;            $self->{insertion_mode} = IN_TABLE_IM;
4478    
# Line 7025  sub _tree_construction_main ($) { Line 4481  sub _tree_construction_main ($) {
4481          } elsif ($token->{tag_name} eq 'hr') {          } elsif ($token->{tag_name} eq 'hr') {
4482            !!!cp ('t386');            !!!cp ('t386');
4483            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4484                      
4485            !!!nack ('t386.1');            !!!ack ('t386.1');
4486    
4487              delete $self->{frameset_ok};
4488    
4489            !!!next-token;            !!!next-token;
4490          } else {          } else {
4491            !!!nack ('t347.1');            !!!nack ('t347.1');
# Line 7036  sub _tree_construction_main ($) { Line 4495  sub _tree_construction_main ($) {
4495        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4496          ## NOTE: As normal, but imply </li> when there's another <li> ...          ## NOTE: As normal, but imply </li> when there's another <li> ...
4497    
4498          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4499            ## Interpreted as <li><foo/></li><li/> (non-conforming)            ## Interpreted as <li><foo/></li><li/> (non-conforming):
4500            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4501            ## 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),
4502            ## 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),
4503            ## object (Fx)            ## object (Fx)
4504            ## Generate non-tree (non-conforming)            ## Generate non-tree (non-conforming):
4505            ## basefont (IE7 (where basefont is non-void)), center (IE),            ## basefont (IE7 (where basefont is non-void)), center (IE),
4506            ## form (IE), hn (IE)            ## form (IE), hn (IE)
4507          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4508            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)            ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4509            ## div (Fx, S)            ## div (Fx, S)
4510    
4511            ## 1. Frameset-ng
4512            delete $self->{frameset_ok};
4513    
4514          my $non_optional;          my $non_optional;
4515          my $i = -1;          my $i = -1;
4516    
4517          ## 1.          ## 2.
4518          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4519            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4520              ## 2. (a) As if </li>              ## 3. (a) As if </li>
4521              {              {
4522                ## If no </li> - not applied                ## If no </li> - not applied
4523                #                #
# Line 7079  sub _tree_construction_main ($) { Line 4541  sub _tree_construction_main ($) {
4541                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4542              }              }
4543    
4544              last; ## 2. (b) goto 5.              last; ## 3. (b) goto 5.
4545            } elsif (            } elsif (
4546                     ## NOTE: not "formatting" and not "phrasing"                     ## NOTE: not "formatting" and not "phrasing"
4547                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4548                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4549                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4550                       (not $node->[1] & ADDRESS_DIV_P_EL)
4551                     (not $node->[1] & ADDRESS_EL) &                    ) {
4552                     (not $node->[1] & DIV_EL) &              ## 4.
                    (not $node->[1] & P_EL)) {  
             ## 3.  
4553              !!!cp ('t357');              !!!cp ('t357');
4554              last; ## goto 5.              last; ## goto 6.
4555            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4556              !!!cp ('t358');              !!!cp ('t358');
4557              #              #
# Line 7100  sub _tree_construction_main ($) { Line 4560  sub _tree_construction_main ($) {
4560              $non_optional ||= $node;              $non_optional ||= $node;
4561              #              #
4562            }            }
4563            ## 4.            ## 5.
4564            ## goto 2.            ## goto 3.
4565            $i--;            $i--;
4566          }          }
4567    
4568          ## 5. (a) has a |p| element in scope          ## 6. (a) has a |p| element in scope
4569          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4570            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4571              !!!cp ('t353');              !!!cp ('t353');
4572    
4573              ## NOTE: |<p><li>|, for example.              ## NOTE: |<p><li>|, for example.
# Line 7122  sub _tree_construction_main ($) { Line 4582  sub _tree_construction_main ($) {
4582            }            }
4583          } # INSCOPE          } # INSCOPE
4584    
4585          ## 5. (b) insert          ## 6. (b) insert
4586          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4587          !!!nack ('t359.1');          !!!nack ('t359.1');
4588          !!!next-token;          !!!next-token;
# Line 7131  sub _tree_construction_main ($) { Line 4591  sub _tree_construction_main ($) {
4591                 $token->{tag_name} eq 'dd') {                 $token->{tag_name} eq 'dd') {
4592          ## NOTE: As normal, but imply </dt> or </dd> when ...          ## NOTE: As normal, but imply </dt> or </dd> when ...
4593    
4594            ## 1. Frameset-ng
4595            delete $self->{frameset_ok};
4596    
4597          my $non_optional;          my $non_optional;
4598          my $i = -1;          my $i = -1;
4599    
4600          ## 1.          ## 2.
4601          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4602            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4603              ## 2. (a) As if </li>              ## 3. (a) As if </li>
4604              {              {
4605                ## If no </li> - not applied                ## If no </li> - not applied
4606                #                #
# Line 7161  sub _tree_construction_main ($) { Line 4624  sub _tree_construction_main ($) {
4624                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
4625              }              }
4626    
4627              last; ## 2. (b) goto 5.              last; ## 3. (b) goto 5.
4628            } elsif (            } elsif (
4629                     ## NOTE: not "formatting" and not "phrasing"                     ## NOTE: not "formatting" and not "phrasing"
4630                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4631                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4632                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4633    
4634                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4635                     (not $node->[1] & DIV_EL) &                    ) {
4636                     (not $node->[1] & P_EL)) {              ## 4.
             ## 3.  
4637              !!!cp ('t357.1');              !!!cp ('t357.1');
4638              last; ## goto 5.              last; ## goto 5.
4639            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
# Line 7182  sub _tree_construction_main ($) { Line 4644  sub _tree_construction_main ($) {
4644              $non_optional ||= $node;              $non_optional ||= $node;
4645              #              #
4646            }            }
4647            ## 4.            ## 5.
4648            ## goto 2.            ## goto 3.
4649            $i--;            $i--;
4650          }          }
4651    
4652          ## 5. (a) has a |p| element in scope          ## 6. (a) has a |p| element in scope
4653          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4654            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4655              !!!cp ('t353.1');              !!!cp ('t353.1');
4656              !!!back-token; # <x>              !!!back-token; # <x>
4657              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7201  sub _tree_construction_main ($) { Line 4663  sub _tree_construction_main ($) {
4663            }            }
4664          } # INSCOPE          } # INSCOPE
4665    
4666          ## 5. (b) insert          ## 6. (b) insert
4667          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4668          !!!nack ('t359.2');          !!!nack ('t359.2');
4669          !!!next-token;          !!!next-token;
# Line 7211  sub _tree_construction_main ($) { Line 4673  sub _tree_construction_main ($) {
4673    
4674          ## has a p element in scope          ## has a p element in scope
4675          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4676            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4677              !!!cp ('t367');              !!!cp ('t367');
4678              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4679              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7233  sub _tree_construction_main ($) { Line 4695  sub _tree_construction_main ($) {
4695        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4696          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4697            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4698            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4699              !!!cp ('t371');              !!!cp ('t371');
4700              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4701                            
# Line 7277  sub _tree_construction_main ($) { Line 4739  sub _tree_construction_main ($) {
4739          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4740          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4741            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4742            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4743              !!!cp ('t376');              !!!cp ('t376');
4744              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4745              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7300  sub _tree_construction_main ($) { Line 4762  sub _tree_construction_main ($) {
4762          ## has a button element in scope          ## has a button element in scope
4763          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4764            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4765            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4766              !!!cp ('t378');              !!!cp ('t378');
4767              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4768              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7321  sub _tree_construction_main ($) { Line 4783  sub _tree_construction_main ($) {
4783    
4784          push @$active_formatting_elements, ['#marker', ''];          push @$active_formatting_elements, ['#marker', ''];
4785    
4786            delete $self->{frameset_ok};
4787    
4788          !!!nack ('t379.1');          !!!nack ('t379.1');
4789          !!!next-token;          !!!next-token;
4790          next B;          next B;
# Line 7334  sub _tree_construction_main ($) { Line 4798  sub _tree_construction_main ($) {
4798          if ($token->{tag_name} eq 'xmp') {          if ($token->{tag_name} eq 'xmp') {
4799            !!!cp ('t381');            !!!cp ('t381');
4800            $reconstruct_active_formatting_elements->($insert_to_current);            $reconstruct_active_formatting_elements->($insert_to_current);
4801    
4802              delete $self->{frameset_ok};
4803            } elsif ($token->{tag_name} eq 'iframe') {
4804              !!!cp ('t381.1');
4805              delete $self->{frameset_ok};
4806          } else {          } else {
4807            !!!cp ('t399');            !!!cp ('t399');
4808          }          }
# Line 7365  sub _tree_construction_main ($) { Line 4834  sub _tree_construction_main ($) {
4834                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4835                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4836                           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}},  
4837                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4838                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4839                         );                         );
# Line 7389  sub _tree_construction_main ($) { Line 4856  sub _tree_construction_main ($) {
4856                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4857                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4858                           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}},  
4859                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4860                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4861                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7400  sub _tree_construction_main ($) { Line 4865  sub _tree_construction_main ($) {
4865            next B;            next B;
4866          }          }
4867        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4868          my $tag_name = $token->{tag_name};          ## 1. Insert
4869          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4870                    
4871            ## Step 2 # XXX
4872          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4873    
4874            ## 2. Drop U+000A LINE FEED
4875            $self->{ignore_newline} = 1;
4876    
4877            ## 3. RCDATA
4878          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4879          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4880            
4881          $insert->($el);          ## 4., 6. Insertion mode
4882                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4883          my $text = '';  
4884            ## 5. Frameset-ng.
4885            delete $self->{frameset_ok};
4886    
4887          !!!nack ('t392.1');          !!!nack ('t392.1');
4888          !!!next-token;          !!!next-token;
         if ($token->{type} == CHARACTER_TOKEN) {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!cp ('t392');  
             !!!next-token;  
           } else {  
             !!!cp ('t393');  
           }  
         } else {  
           !!!cp ('t394');  
         }  
         while ($token->{type} == CHARACTER_TOKEN) {  
           !!!cp ('t395');  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         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);  
         }  
         !!!next-token;  
4889          next B;          next B;
4890        } elsif ($token->{tag_name} eq 'optgroup' or        } elsif ($token->{tag_name} eq 'optgroup' or
4891                 $token->{tag_name} eq 'option') {                 $token->{tag_name} eq 'option') {
4892          ## has an |option| element in scope          ## has an |option| element in scope
4893          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4894            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4895            if ($node->[1] & OPTION_EL) {            if ($node->[1] == OPTION_EL) {
4896              !!!cp ('t397.1');              !!!cp ('t397.1');
4897              ## NOTE: As if </option>              ## NOTE: As if </option>
4898              !!!back-token; # <option> or <optgroup>              !!!back-token; # <option> or <optgroup>
# Line 7476  sub _tree_construction_main ($) { Line 4917  sub _tree_construction_main ($) {
4917          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4918          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4919            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4920            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4921              !!!cp ('t398.1');              !!!cp ('t398.1');
4922              ## generate implied end tags              ## generate implied end tags
4923              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4924                !!!cp ('t398.2');                !!!cp ('t398.2');
4925                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4926              }              }
4927              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4928                !!!cp ('t398.3');                !!!cp ('t398.3');
4929                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4930                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4931                                    ->manakai_local_name,                                    ->manakai_local_name,
4932                                token => $token);                                token => $token);
4933                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4934                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4935              }              }
4936              last INSCOPE;              last INSCOPE;
4937            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7498  sub _tree_construction_main ($) { Line 4939  sub _tree_construction_main ($) {
4939              last INSCOPE;              last INSCOPE;
4940            }            }
4941          } # INSCOPE          } # INSCOPE
4942              
4943            ## TODO: <non-ruby><rt> is not allowed.
4944    
4945          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4946    
# Line 7531  sub _tree_construction_main ($) { Line 4974  sub _tree_construction_main ($) {
4974          next B;          next B;
4975        } elsif ({        } elsif ({
4976                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4977                  frameset => 1, head => 1,                  head => 1,
4978                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
4979                  thead => 1, tr => 1,                  thead => 1, tr => 1,
4980                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7568  sub _tree_construction_main ($) { Line 5011  sub _tree_construction_main ($) {
5011               applet => 1, marquee => 1, object => 1,               applet => 1, marquee => 1, object => 1,
5012              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
5013            !!!cp ('t380');            !!!cp ('t380');
5014    
5015            push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
5016    
5017              delete $self->{frameset_ok};
5018    
5019            !!!nack ('t380.1');            !!!nack ('t380.1');
5020          } elsif ({          } elsif ({
5021                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
# Line 7586  sub _tree_construction_main ($) { Line 5033  sub _tree_construction_main ($) {
5033          } elsif ({          } elsif ({
5034                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5035                    embed => 1, img => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
5036                      keygen => 1,
5037                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5038            !!!cp ('t388.1');            !!!cp ('t388.1');
5039    
5040            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
5041    
5042              delete $self->{frameset_ok};
5043    
5044            !!!ack ('t388.3');            !!!ack ('t388.3');
5045          } elsif ($token->{tag_name} eq 'select') {          } elsif ($token->{tag_name} eq 'select') {
5046            ## TODO: associate with $self->{form_element} if defined            ## TODO: associate with $self->{form_element} if defined
5047            
5048              delete $self->{frameset_ok};
5049              
5050            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5051                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5052                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5053              !!!cp ('t400.1');              !!!cp ('t400.1');
5054              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5055            } else {            } else {
# Line 7611  sub _tree_construction_main ($) { Line 5065  sub _tree_construction_main ($) {
5065          next B;          next B;
5066        }        }
5067      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5068        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body' or $token->{tag_name} eq 'html') {
5069          ## has a |body| element in scope  
5070            ## 1. If not "have an element in scope":
5071            ## "has a |body| element in scope"
5072          my $i;          my $i;
5073          INSCOPE: {          INSCOPE: {
5074            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5075              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5076                !!!cp ('t405');                !!!cp ('t405');
5077                $i = $_;                $i = $_;
5078                last INSCOPE;                last INSCOPE;
# Line 7626  sub _tree_construction_main ($) { Line 5082  sub _tree_construction_main ($) {
5082              }              }
5083            }            }
5084    
5085            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|,
5086              ## and fragment cases.
5087    
5088            !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
5089                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5090            ## NOTE: Ignore the token.            ## Ignore the token.  (</body> or </html>)
5091            !!!next-token;            !!!next-token;
5092            next B;            next B;
5093          } # INSCOPE          } # INSCOPE
5094    
5095            ## 2. If unclosed elements:
5096          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5097            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5098                      $_->[1] == OPTGROUP_EL ||
5099                      $_->[1] == OPTION_EL ||
5100                      $_->[1] == RUBY_COMPONENT_EL) {
5101              !!!cp ('t403');              !!!cp ('t403');
5102              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5103                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7647  sub _tree_construction_main ($) { Line 5108  sub _tree_construction_main ($) {
5108            }            }
5109          }          }
5110    
5111            ## 3. Switch the insertion mode.
5112          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5113          !!!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) {  
           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  
5114            !!!next-token;            !!!next-token;
5115            next B;          } else { # html
5116              ## Reprocess.
5117          }          }
5118            next B;
5119        } elsif ({        } elsif ({
5120                  ## NOTE: End tags for non-phrasing flow content elements                  ## NOTE: End tags for non-phrasing flow content elements
5121    
# Line 7682  sub _tree_construction_main ($) { Line 5123  sub _tree_construction_main ($) {
5123                  address => 1, article => 1, aside => 1, blockquote => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
5124                  center => 1, datagrid => 1, details => 1, dialog => 1,                  center => 1, datagrid => 1, details => 1, dialog => 1,
5125                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
5126                  footer => 1, header => 1, listing => 1, menu => 1, nav => 1,                  footer => 1, header => 1, hgroup => 1,
5127                    listing => 1, menu => 1, nav => 1,
5128                  ol => 1, pre => 1, section => 1, ul => 1,                  ol => 1, pre => 1, section => 1, ul => 1,
5129    
5130                  ## NOTE: As normal, but ... optional tags                  ## NOTE: As normal, but ... optional tags
# Line 7762  sub _tree_construction_main ($) { Line 5204  sub _tree_construction_main ($) {
5204          my $i;          my $i;
5205          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5206            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5207            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5208              !!!cp ('t418');              !!!cp ('t418');
5209              $i = $_;              $i = $_;
5210              last INSCOPE;              last INSCOPE;
# Line 7810  sub _tree_construction_main ($) { Line 5252  sub _tree_construction_main ($) {
5252          my $i;          my $i;
5253          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5254            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5255            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5256              !!!cp ('t423');              !!!cp ('t423');
5257              $i = $_;              $i = $_;
5258              last INSCOPE;              last INSCOPE;
# Line 7856  sub _tree_construction_main ($) { Line 5298  sub _tree_construction_main ($) {
5298          my $i;          my $i;
5299          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5300            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5301            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5302              !!!cp ('t410.1');              !!!cp ('t410.1');
5303              $i = $_;              $i = $_;
5304              last INSCOPE;              last INSCOPE;
# Line 8033  sub _tree_construction_main ($) { Line 5475  sub _tree_construction_main ($) {
5475    ## TODO: script stuffs    ## TODO: script stuffs
5476  } # _tree_construct_main  } # _tree_construct_main
5477    
5478    ## XXX: How this method is organized is somewhat out of date, although
5479    ## it still does what the current spec documents.
5480  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5481    my $class = shift;    my $class = shift;
5482    my $node = shift;    my $node = shift; # /context/
5483    #my $s = \$_[0];    #my $s = \$_[0];
5484    my $onerror = $_[1];    my $onerror = $_[1];
5485    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
5486    
   ## ISSUE: Should {confident} be true?  
   
5487    my $nt = $node->node_type;    my $nt = $node->node_type;
5488    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5489      # MUST      # MUST
5490            
5491      ## Step 1 # MUST      ## Step 1 # MUST
# Line 8058  sub set_inner_html ($$$$;$) { Line 5500  sub set_inner_html ($$$$;$) {
5500    
5501      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5502      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5503    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5504      ## TODO: If non-html element      ## TODO: If non-html element
5505    
5506      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5507    
5508  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5509    
5510      ## Step 1 # MUST      ## F1. Create an HTML document.
5511      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5512      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5513      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5514    
5515        ## F2. Propagate quirkness flag
5516        my $node_doc = $node->owner_document;
5517        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5518    
5519        ## F3. Create an HTML parser
5520      my $p = $class->new;      my $p = $class->new;
5521      $p->{document} = $doc;      $p->{document} = $doc;
5522    
# Line 8196  sub set_inner_html ($$$$;$) { Line 5644  sub set_inner_html ($$$$;$) {
5644      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5645      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5646    
5647      ## Step 2      ## F4. If /context/ is not undef...
5648    
5649        ## F4.1. content model flag
5650      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5651      $p->{content_model} = {      $p->{content_model} = {
5652        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8212  sub set_inner_html ($$$$;$) { Line 5662  sub set_inner_html ($$$$;$) {
5662      }->{$node_ln};      }->{$node_ln};
5663      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5664          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5665    
5666      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5667        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5668    
5669      ## Step 3      ## F4.2. Root |html| element
5670      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5671        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5672    
5673      ## Step 4 # MUST      ## F4.3.
5674      $doc->append_child ($root);      $doc->append_child ($root);
5675    
5676      ## Step 5 # MUST      ## F4.4.
5677      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5678    
5679      undef $p->{head_element};      undef $p->{head_element};
5680      undef $p->{head_element_inserted};      undef $p->{head_element_inserted};
5681    
5682      ## Step 6 # MUST      ## F4.5.
5683      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5684    
5685      ## Step 7 # MUST      ## F4.6.
5686      my $anode = $node;      my $anode = $node;
5687      AN: while (defined $anode) {      AN: while (defined $anode) {
5688        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8248  sub set_inner_html ($$$$;$) { Line 5697  sub set_inner_html ($$$$;$) {
5697        }        }
5698        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5699      } # AN      } # AN
5700        
5701      ## Step 9 # MUST      ## F.5. Set the input stream.
5702        $p->{confident} = 1; ## Confident: irrelevant.
5703    
5704        ## F.6. Start the parser.
5705      {      {
5706        my $self = $p;        my $self = $p;
5707        !!!next-token;        !!!next-token;
5708      }      }
5709      $p->_tree_construction_main;      $p->_tree_construction_main;
5710    
5711      ## Step 10 # MUST      ## F.7.
5712      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5713      for (@cn) {      for (@cn) {
5714        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.204  
changed lines
  Added in v.1.243

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24