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

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

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

revision 1.193 by wakaba, Sat Oct 4 04:06:33 2008 UTC revision 1.232 by wakaba, Sun Sep 6 01:30:08 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
 sub ADDRESS_EL () { 0b10 }  
 sub BODY_EL () { 0b100 }  
 sub BUTTON_EL () { 0b1000 }  
 sub CAPTION_EL () { 0b10000 }  
 sub DD_EL () { 0b100000 }  
 sub DIV_EL () { 0b1000000 }  
 sub DT_EL () { 0b10000000 }  
 sub FORM_EL () { 0b100000000 }  
 sub FORMATTING_EL () { 0b1000000000 }  
 sub FRAMESET_EL () { 0b10000000000 }  
 sub HEADING_EL () { 0b100000000000 }  
 sub HTML_EL () { 0b1000000000000 }  
 sub LI_EL () { 0b10000000000000 }  
 sub NOBR_EL () { 0b100000000000000 }  
 sub OPTION_EL () { 0b1000000000000000 }  
 sub OPTGROUP_EL () { 0b10000000000000000 }  
 sub P_EL () { 0b100000000000000000 }  
 sub SELECT_EL () { 0b1000000000000000000 }  
 sub TABLE_EL () { 0b10000000000000000000 }  
 sub TABLE_CELL_EL () { 0b100000000000000000000 }  
 sub TABLE_ROW_EL () { 0b1000000000000000000000 }  
 sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  
 sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
36    
37  ## NOTE: Used in "generate implied end tags" algorithm.  ## Bits 12-15
38  ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL  sub SPECIAL_EL () { 0b1_000000000000000 }
39  ## is used in "generate implied end tags" implementation (search for the  sub SCOPING_EL () { 0b1_00000000000000 }
40  ## function mae).  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub END_TAG_OPTIONAL_EL () {  sub PHRASING_EL () { 0b1_000000000000 }
42    DD_EL |  
43    DT_EL |  ## Bits 10-11
44    LI_EL |  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45    P_EL |  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46    RUBY_COMPONENT_EL  
47  }  ## Bits 6-9
48    sub TABLE_SCOPING_EL () { 0b1_000000000 }
49    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51    sub TABLE_ROWS_EL () { 0b1_000000 }
52    
53    ## Bit 5
54    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55    
56  ## NOTE: Used in </body> and EOF algorithms.  ## NOTE: Used in </body> and EOF algorithms.
57  sub ALL_END_TAG_OPTIONAL_EL () {  ## Bit 4
58    DD_EL |  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  sub SCOPING_EL () {  ## NOTE: Used in "generate implied end tags" algorithm.
61    BUTTON_EL |  ## NOTE: There is a code where a modified version of
62    CAPTION_EL |  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63    HTML_EL |  ## implementation (search for the algorithm name).
64    TABLE_EL |  ## Bit 3
65    TABLE_CELL_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
66    MISC_SCOPING_EL  
67    ## Bits 0-2
68    
69    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    sub FORM_EL () { SPECIAL_EL | 0b001 }
71    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    FORM_EL |  }
125    FRAMESET_EL |  sub TABLE_EL () {
126    HEADING_EL |    SCOPING_EL |
127    OPTION_EL |    TABLE_ROWS_EL |
128    OPTGROUP_EL |    TABLE_SCOPING_EL |
129    SELECT_EL |    0b001
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
130  }  }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 158  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
   eventsource => MISC_SPECIAL_EL,  
182    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
183    figure => MISC_SPECIAL_EL,    figure => MISC_SPECIAL_EL,
184    font => FORMATTING_EL,    font => FORMATTING_EL,
# Line 191  my $el_category = { Line 202  my $el_category = {
202    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
203    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
204    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
205      ## XXX keygen? (Whether a void element is in Special or not does not
206      ## affect to the processing, however.)
207    li => LI_EL,    li => LI_EL,
208    link => MISC_SPECIAL_EL,    link => MISC_SPECIAL_EL,
209    listing => MISC_SPECIAL_EL,    listing => MISC_SPECIAL_EL,
# Line 198  my $el_category = { Line 211  my $el_category = {
211    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
212    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
213    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
214    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
215    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
216    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
217    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 240  my $el_category = { Line 253  my $el_category = {
253  my $el_category_f = {  my $el_category_f = {
254    $MML_NS => {    $MML_NS => {
255      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
256      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
261    },    },
262    $SVG_NS => {    $SVG_NS => {
263      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
266    },    },
267    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
268  };  };
# Line 336  my $foreign_attr_xname = { Line 349  my $foreign_attr_xname = {
349    
350  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
351    
 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;  
   
352  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
353  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
354    
# Line 486  sub parse_byte_stream ($$$$;$$) { Line 455  sub parse_byte_stream ($$$$;$$) {
455      if (defined $charset_name) {      if (defined $charset_name) {
456        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
457    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
458        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
459        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
460            ($byte_stream);            ($byte_stream);
# Line 558  sub parse_byte_stream ($$$$;$$) { Line 526  sub parse_byte_stream ($$$$;$$) {
526            
527      if ($char_stream) { # if supported      if ($char_stream) { # if supported
528        ## "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;  
529                
530        ## Step 2        ## Step 1
531        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
532            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
533          !!!parse-error (type => 'charset label:matching',          !!!parse-error (type => 'charset label:matching',
# Line 579  sub parse_byte_stream ($$$$;$$) { Line 537  sub parse_byte_stream ($$$$;$$) {
537          return;          return;
538        }        }
539    
540          ## Step 2 (HTML5 revision 3205)
541          if (defined $self->{input_encoding} and
542              Message::Charset::Info->get_by_html_name ($self->{input_encoding})
543              ->{category} & Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
544            $self->{confident} = 1;
545            return;
546          }
547    
548          ## Step 3
549          if ($charset->{category} &
550              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
551            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
552            ($char_stream, $e_status) = $charset->get_decode_handle
553                ($byte_stream,
554                 byte_buffer => \ $buffer->{buffer});
555          }
556          $charset_name = $charset->get_iana_name;
557    
558        !!!parse-error (type => 'charset label detected',        !!!parse-error (type => 'charset label detected',
559                        text => $self->{input_encoding},                        text => $self->{input_encoding},
560                        value => $charset_name,                        value => $charset_name,
561                        level => $self->{level}->{warn},                        level => $self->{level}->{warn},
562                        token => $token);                        token => $token);
563                
564        ## Step 3        ## Step 4
565        # if (can) {        # if (can) {
566          ## change the encoding on the fly.          ## change the encoding on the fly.
567          #$self->{confident} = 1;          #$self->{confident} = 1;
568          #return;          #return;
569        # }        # }
570                
571        ## Step 4        ## Step 5
572        throw Whatpm::HTML::RestartParser ();        throw Whatpm::HTML::RestartParser ();
573      }      }
574    }; # $self->{change_encoding}    }; # $self->{change_encoding}
# Line 833  sub new ($) { Line 809  sub new ($) {
809    return $self;    return $self;
810  } # new  } # new
811    
812  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 }  
813    
814  sub AFTER_HTML_IMS () { 0b100 }  sub AFTER_HTML_IMS () { 0b100 }
815  sub HEAD_IMS ()       { 0b1000 }  sub HEAD_IMS ()       { 0b1000 }
# Line 913  sub ROW_IMS ()        { 0b10000000 } Line 820  sub ROW_IMS ()        { 0b10000000 }
820  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
821  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
822  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
823  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
824      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
825      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
826      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
827    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
828        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
829        ## combined with the original insertion mode.  In thie parser,
830        ## they are stored together in the bit-or'ed form.
831    
832    sub IM_MASK () { 0b11111111111 }
833    
834  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
835    
# Line 943  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 856  sub IN_SELECT_IM () { SELECT_IMS | 0b01
856  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
857  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
858    
 ## Implementations MUST act as if state machine in the spec  
   
 sub _initialize_tokenizer ($) {  
   my $self = shift;  
   $self->{state} = DATA_STATE; # MUST  
   #$self->{s_kwd}; # state keyword - initialized when used  
   #$self->{entity__value}; # initialized when used  
   #$self->{entity__match}; # initialized when used  
   $self->{content_model} = PCDATA_CONTENT_MODEL; # be  
   undef $self->{ct}; # current token  
   undef $self->{ca}; # current attribute  
   undef $self->{last_stag_name}; # last emitted start tag name  
   #$self->{prev_state}; # initialized when used  
   delete $self->{self_closing};  
   $self->{char_buffer} = '';  
   $self->{char_buffer_pos} = 0;  
   $self->{nc} = -1; # next input character  
   #$self->{next_nc}  
   !!!next-input-character;  
   $self->{token} = [];  
   # $self->{escape}  
 } # _initialize_tokenizer  
   
 ## A token has:  
 ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,  
 ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  
 ##   ->{name} (DOCTYPE_TOKEN)  
 ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##   ->{pubid} (DOCTYPE_TOKEN)  
 ##   ->{sysid} (DOCTYPE_TOKEN)  
 ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  
 ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##        ->{name}  
 ##        ->{value}  
 ##        ->{has_reference} == 1 or 0  
 ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  
 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.  
 ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  
 ##     while the token is pushed back to the stack.  
   
 ## Emitted token MUST immediately be handled by the tree construction state.  
   
 ## Before each step, UA MAY check to see if either one of the scripts in  
 ## "list of scripts that will execute as soon as possible" or the first  
 ## script in the "list of scripts that will execute asynchronously",  
 ## has completed loading.  If one has, then it MUST be executed  
 ## and removed from the list.  
   
 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)  
 ## (This requirement was dropped from HTML5 spec, unfortunately.)  
   
 my $is_space = {  
   0x0009 => 1, # CHARACTER TABULATION (HT)  
   0x000A => 1, # LINE FEED (LF)  
   #0x000B => 0, # LINE TABULATION (VT)  
   0x000C => 1, # FORM FEED (FF)  
   #0x000D => 1, # CARRIAGE RETURN (CR)  
   0x0020 => 1, # SPACE (SP)  
 };  
   
 sub _get_next_token ($) {  
   my $self = shift;  
   
   if ($self->{self_closing}) {  
     !!!parse-error (type => 'nestc', token => $self->{ct});  
     ## NOTE: The |self_closing| flag is only set by start tag token.  
     ## In addition, when a start tag token is emitted, it is always set to  
     ## |ct|.  
     delete $self->{self_closing};  
   }  
   
   if (@{$self->{token}}) {  
     $self->{self_closing} = $self->{token}->[0]->{self_closing};  
     return shift @{$self->{token}};  
   }  
   
   A: {  
     if ($self->{state} == PCDATA_STATE) {  
       ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.  
   
       if ($self->{nc} == 0x0026) { # &  
         !!!cp (0.1);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity data state".  In this implementation, the tokenizer  
         ## is switched to the |ENTITY_STATE|, which is an implementation  
         ## of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = DATA_STATE;  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003C) { # <  
         !!!cp (0.2);  
         $self->{state} = TAG_OPEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (0.3);  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (0.4);  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       $self->{read_until}->($token->{data}, q[<&], length $token->{data});  
   
       ## Stay in the state.  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == DATA_STATE) {  
       $self->{s_kwd} = '' unless defined $self->{s_kwd};  
       if ($self->{nc} == 0x0026) { # &  
         $self->{s_kwd} = '';  
         if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA  
             not $self->{escape}) {  
           !!!cp (1);  
           ## NOTE: In the spec, the tokenizer is switched to the  
           ## "entity data state".  In this implementation, the tokenizer  
           ## is switched to the |ENTITY_STATE|, which is an implementation  
           ## of the "consume a character reference" algorithm.  
           $self->{entity_add} = -1;  
           $self->{prev_state} = DATA_STATE;  
           $self->{state} = ENTITY_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (2);  
           #  
         }  
       } elsif ($self->{nc} == 0x002D) { # -  
         if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
           $self->{s_kwd} .= '-';  
             
           if ($self->{s_kwd} eq '<!--') {  
             !!!cp (3);  
             $self->{escape} = 1; # unless $self->{escape};  
             $self->{s_kwd} = '--';  
             #  
           } elsif ($self->{s_kwd} eq '---') {  
             !!!cp (4);  
             $self->{s_kwd} = '--';  
             #  
           } else {  
             !!!cp (5);  
             #  
           }  
         }  
           
         #  
       } elsif ($self->{nc} == 0x0021) { # !  
         if (length $self->{s_kwd}) {  
           !!!cp (5.1);  
           $self->{s_kwd} .= '!';  
           #  
         } else {  
           !!!cp (5.2);  
           #$self->{s_kwd} = '';  
           #  
         }  
         #  
       } elsif ($self->{nc} == 0x003C) { # <  
         if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA  
             (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA  
              not $self->{escape})) {  
           !!!cp (6);  
           $self->{state} = TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (7);  
           $self->{s_kwd} = '';  
           #  
         }  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{escape} and  
             ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA  
           if ($self->{s_kwd} eq '--') {  
             !!!cp (8);  
             delete $self->{escape};  
           } else {  
             !!!cp (9);  
           }  
         } else {  
           !!!cp (10);  
         }  
           
         $self->{s_kwd} = '';  
         #  
       } elsif ($self->{nc} == -1) {  
         !!!cp (11);  
         $self->{s_kwd} = '';  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (12);  
         $self->{s_kwd} = '';  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       if ($self->{read_until}->($token->{data}, q[-!<>&],  
                                 length $token->{data})) {  
         $self->{s_kwd} = '';  
       }  
   
       ## Stay in the data state.  
       if ($self->{content_model} == PCDATA_CONTENT_MODEL) {  
         !!!cp (13);  
         $self->{state} = PCDATA_STATE;  
       } else {  
         !!!cp (14);  
         ## Stay in the state.  
       }  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == TAG_OPEN_STATE) {  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if ($self->{nc} == 0x002F) { # /  
           !!!cp (15);  
           !!!next-input-character;  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           redo A;  
         } elsif ($self->{nc} == 0x0021) { # !  
           !!!cp (15.1);  
           $self->{s_kwd} = '<' unless $self->{escape};  
           #  
         } else {  
           !!!cp (16);  
           #  
         }  
   
         ## reconsume  
         $self->{state} = DATA_STATE;  
         !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA  
         if ($self->{nc} == 0x0021) { # !  
           !!!cp (17);  
           $self->{state} = MARKUP_DECLARATION_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x002F) { # /  
           !!!cp (18);  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0041 <= $self->{nc} and  
                  $self->{nc} <= 0x005A) { # A..Z  
           !!!cp (19);  
           $self->{ct}  
             = {type => START_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $self->{line_prev},  
                column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0061 <= $self->{nc} and  
                  $self->{nc} <= 0x007A) { # a..z  
           !!!cp (20);  
           $self->{ct} = {type => START_TAG_TOKEN,  
                                     tag_name => chr ($self->{nc}),  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x003E) { # >  
           !!!cp (21);  
           !!!parse-error (type => 'empty start tag',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<>',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         } elsif ($self->{nc} == 0x003F) { # ?  
           !!!cp (22);  
           !!!parse-error (type => 'pio',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = BOGUS_COMMENT_STATE;  
           $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev},  
                                    };  
           ## $self->{nc} is intentionally left as is  
           redo A;  
         } else {  
           !!!cp (23);  
           !!!parse-error (type => 'bare stago',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           ## reconsume  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         }  
       } else {  
         die "$0: $self->{content_model} in tag open";  
       }  
     } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {  
       ## NOTE: The "close tag open state" in the spec is implemented as  
       ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if (defined $self->{last_stag_name}) {  
           $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;  
           $self->{s_kwd} = '';  
           ## Reconsume.  
           redo A;  
         } else {  
           ## No start tag token has ever been emitted  
           ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.  
           !!!cp (28);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                     line => $l, column => $c,  
                    });  
           redo A;  
         }  
       }  
   
       if (0x0041 <= $self->{nc} and  
           $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (29);  
         $self->{ct}  
             = {type => END_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x007A) { # a..z  
         !!!cp (30);  
         $self->{ct} = {type => END_TAG_TOKEN,  
                                   tag_name => chr ($self->{nc}),  
                                   line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (31);  
         !!!parse-error (type => 'empty end tag',  
                         line => $self->{line_prev}, ## "<" in "</>"  
                         column => $self->{column_prev} - 1);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (32);  
         !!!parse-error (type => 'bare etago');  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                   line => $l, column => $c,  
                  });  
   
         redo A;  
       } else {  
         !!!cp (33);  
         !!!parse-error (type => 'bogus end tag');  
         $self->{state} = BOGUS_COMMENT_STATE;  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev}, # "<" of "</"  
                                   column => $self->{column_prev} - 1,  
                                  };  
         ## NOTE: $self->{nc} is intentionally left as is.  
         ## Although the "anything else" case of the spec not explicitly  
         ## states that the next input character is to be reconsumed,  
         ## it will be included to the |data| of the comment token  
         ## generated from the bogus end tag, as defined in the  
         ## "bogus comment state" entry.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {  
       my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;  
       if (length $ch) {  
         my $CH = $ch;  
         $ch =~ tr/a-z/A-Z/;  
         my $nch = chr $self->{nc};  
         if ($nch eq $ch or $nch eq $CH) {  
           !!!cp (24);  
           ## Stay in the state.  
           $self->{s_kwd} .= $nch;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (25);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         }  
       } else { # after "<{tag-name}"  
         unless ($is_space->{$self->{nc}} or  
                 {  
                  0x003E => 1, # >  
                  0x002F => 1, # /  
                  -1 => 1, # EOF  
                 }->{$self->{nc}}) {  
           !!!cp (26);  
           ## Reconsume.  
           $self->{state} = DATA_STATE;  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (27);  
           $self->{ct}  
               = {type => END_TAG_TOKEN,  
                  tag_name => $self->{last_stag_name},  
                  line => $self->{line_prev},  
                  column => $self->{column_prev} - 1 - length $self->{s_kwd}};  
           $self->{state} = TAG_NAME_STATE;  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == TAG_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (34);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (35);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This should never be reached.  
           #  !!! cp (36);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (37);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (38);  
         $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);  
           # start tag or end tag  
         ## Stay in this state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (39);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This state should never be reached.  
           #  !!! cp (40);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (41);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (42);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (44);  
         $self->{ct}->{tag_name} .= chr $self->{nc};  
           # start tag or end tag  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (45);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (46);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (47);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (48);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (49);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (50);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (52);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (53);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (54);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (55);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (56);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {  
       my $before_leave = sub {  
         if (exists $self->{ct}->{attributes} # start tag or end tag  
             ->{$self->{ca}->{name}}) { # MUST  
           !!!cp (57);  
           !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});  
           ## Discard $self->{ca} # MUST  
         } else {  
           !!!cp (58);  
           $self->{ct}->{attributes}->{$self->{ca}->{name}}  
             = $self->{ca};  
         }  
       }; # $before_leave  
   
       if ($is_space->{$self->{nc}}) {  
         !!!cp (59);  
         $before_leave->();  
         $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (60);  
         $before_leave->();  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (61);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp (62);  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!parse-error (type => 'end tag attribute');  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (63);  
         $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (64);  
         $before_leave->();  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (66);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (67);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (68);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (69);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (70);  
         }  
         $self->{ca}->{name} .= chr ($self->{nc});  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (71);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (72);  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (73);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (74);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (75);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (76);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (77);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (79);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (80);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (81);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (78);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (82);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;          
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (83);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (84);  
         $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (85);  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         ## reconsume  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (86);  
         $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!parse-error (type => 'empty unquoted attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (87);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (88);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (89);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (90);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (91);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (92);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x003D) { # =  
           !!!cp (93);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (94);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (95);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (96);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{prev_state} = $self->{state};  
         $self->{entity_add} = 0x0022; # "  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (97);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (98);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (99);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (100);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (101);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (102);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = 0x0027; # '  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (103);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (104);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (105);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (106);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q['&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (107);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (108);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (109);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (110);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (111);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (112);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (113);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (114);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (115);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (116);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["'=& >],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (118);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (119);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (120);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (121);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (122);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (122.3);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (122.1);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (122.2);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.1');  
         !!!parse-error (type => 'no space between attributes');  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp ('124.2');  
           !!!parse-error (type => 'nestc', token => $self->{ct});  
           ## TODO: Different type than slash in start tag  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp ('124.4');  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp ('124.5');  
           }  
           ## TODO: Test |<title></title/>|  
         } else {  
           !!!cp ('124.3');  
           $self->{self_closing} = 1;  
         }  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (124.7);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (124.5);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (124.6);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.4');  
         !!!parse-error (type => 'nestc');  
         ## TODO: This error type is wrong.  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_COMMENT_STATE) {  
       ## (only happen if PCDATA state)  
   
       ## NOTE: Unlike spec's "bogus comment state", this implementation  
       ## consumes characters one-by-one basis.  
         
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (124);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (125);  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } else {  
         !!!cp (126);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[>],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {  
       ## (only happen if PCDATA state)  
         
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (133);  
         $self->{state} = MD_HYPHEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0044 or # D  
                $self->{nc} == 0x0064) { # d  
         ## ASCII case-insensitive.  
         !!!cp (130);  
         $self->{state} = MD_DOCTYPE_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and  
                $self->{open_elements}->[-1]->[1] & FOREIGN_EL and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.4);                  
         $self->{state} = MD_CDATA_STATE;  
         $self->{s_kwd} = '[';  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (136);  
       }  
   
       !!!parse-error (type => 'bogus comment',  
                       line => $self->{line_prev},  
                       column => $self->{column_prev} - 1);  
       ## Reconsume.  
       $self->{state} = BOGUS_COMMENT_STATE;  
       $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                 line => $self->{line_prev},  
                                 column => $self->{column_prev} - 1,  
                                };  
       redo A;  
     } elsif ($self->{state} == MD_HYPHEN_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (127);  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         $self->{state} = COMMENT_START_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (128);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => '-',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_DOCTYPE_STATE) {  
       ## ASCII case-insensitive.  
       if ($self->{nc} == [  
             undef,  
             0x004F, # O  
             0x0043, # C  
             0x0054, # T  
             0x0059, # Y  
             0x0050, # P  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x006F, # o  
             0x0063, # c  
             0x0074, # t  
             0x0079, # y  
             0x0070, # p  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (131);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 6 and  
                ($self->{nc} == 0x0045 or # E  
                 $self->{nc} == 0x0065)) { # e  
         !!!cp (129);  
         $self->{state} = DOCTYPE_STATE;  
         $self->{ct} = {type => DOCTYPE_TOKEN,  
                                   quirks => 1,  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7,  
                                  };  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (132);          
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_CDATA_STATE) {  
       if ($self->{nc} == {  
             '[' => 0x0043, # C  
             '[C' => 0x0044, # D  
             '[CD' => 0x0041, # A  
             '[CDA' => 0x0054, # T  
             '[CDAT' => 0x0041, # A  
           }->{$self->{s_kwd}}) {  
         !!!cp (135.1);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{s_kwd} eq '[CDATA' and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.2);  
         $self->{ct} = {type => CHARACTER_TOKEN,  
                                   data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7};  
         $self->{state} = CDATA_SECTION_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (135.3);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (137);  
         $self->{state} = COMMENT_START_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (138);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (139);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (140);  
         $self->{ct}->{data} # comment  
             .= chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (141);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (142);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (143);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (144);  
         $self->{ct}->{data} # comment  
             .= '-' . chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (145);  
         $self->{state} = COMMENT_END_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (146);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (147);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[-],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (148);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (149);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (150);  
         $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (151);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == 0x002D) { # -  
         !!!cp (152);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '-'; # comment  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (153);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (154);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (155);  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (156);  
         !!!parse-error (type => 'no space before DOCTYPE name');  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (157);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (158);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (159);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } else {  
         !!!cp (160);  
         $self->{ct}->{name} = chr $self->{nc};  
         delete $self->{ct}->{quirks};  
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_NAME_STATE) {  
 ## ISSUE: Redundant "First," in the spec.  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (161);  
         $self->{state} = AFTER_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (162);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (163);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (164);  
         $self->{ct}->{name}  
           .= chr ($self->{nc}); # DOCTYPE  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (165);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (166);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (167);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == 0x0050 or # P  
                $self->{nc} == 0x0070) { # p  
         $self->{state} = PUBLIC_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0053 or # S  
                $self->{nc} == 0x0073) { # s  
         $self->{state} = SYSTEM_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (180);  
         !!!parse-error (type => 'string after DOCTYPE name');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == PUBLIC_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0055, # U  
             0x0042, # B  
             0x004C, # L  
             0x0049, # I  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0075, # u  
             0x0062, # b  
             0x006C, # l  
             0x0069, # i  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (175);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x0043 or # C  
                 $self->{nc} == 0x0063)) { # c  
         !!!cp (168);  
         $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (169);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == SYSTEM_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0059, # Y  
             0x0053, # S  
             0x0054, # T  
             0x0045, # E  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0079, # y  
             0x0073, # s  
             0x0074, # t  
             0x0065, # e  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (170);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x004D or # M  
                 $self->{nc} == 0x006D)) { # m  
         !!!cp (171);  
         $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (172);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (181);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0022) { # "  
         !!!cp (182);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0027) { # '  
         !!!cp (183);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x003E) { # >  
         !!!cp (184);  
         !!!parse-error (type => 'no PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (185);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (186);  
         !!!parse-error (type => 'string after PUBLIC');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (187);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (188);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (189);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (190);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q[">],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (191);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (192);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (193);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (194);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q['>],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (195);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (196);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (197);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (198);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (199);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (200);  
         !!!parse-error (type => 'string after PUBLIC literal');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (201);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (202);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (203);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (204);  
         !!!parse-error (type => 'no SYSTEM literal');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (205);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (206);  
         !!!parse-error (type => 'string after SYSTEM');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (207);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (208);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (209);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (210);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q[">],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (211);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (212);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (213);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (214);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q['>],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (215);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (216);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (217);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (218);  
         !!!parse-error (type => 'string after SYSTEM literal');  
         #$self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (219);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (220);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (221);  
         my $s = '';  
         $self->{read_until}->($s, q[>], 0);  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_STATE) {  
       ## NOTE: "CDATA section state" in the state is jointly implemented  
       ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,  
       ## and |CDATA_SECTION_MSE2_STATE|.  
         
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.1);  
         $self->{state} = CDATA_SECTION_MSE1_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.2);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.3);  
           ## No token to emit. $self->{ct} is discarded.  
         }          
         redo A;  
       } else {  
         !!!cp (221.4);  
         $self->{ct}->{data} .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{data},  
                               q<]>,  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
   
       ## ISSUE: "text tokens" in spec.  
     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {  
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.5);  
         $self->{state} = CDATA_SECTION_MSE2_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.6);  
         $self->{ct}->{data} .= ']';  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.7);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.8);  
           ## No token to emit. $self->{ct} is discarded.  
         }  
         redo A;  
       } elsif ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.9); # character  
         $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.11);  
         $self->{ct}->{data} .= ']]'; # character  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_STATE) {  
       if ($is_space->{$self->{nc}} or  
           {  
             0x003C => 1, 0x0026 => 1, -1 => 1, # <, &  
             $self->{entity_add} => 1,  
           }->{$self->{nc}}) {  
         !!!cp (1001);  
         ## Don't consume  
         ## No error  
         ## Return nothing.  
         #  
       } elsif ($self->{nc} == 0x0023) { # #  
         !!!cp (999);  
         $self->{state} = ENTITY_HASH_STATE;  
         $self->{s_kwd} = '#';  
         !!!next-input-character;  
         redo A;  
       } elsif ((0x0041 <= $self->{nc} and  
                 $self->{nc} <= 0x005A) or # A..Z  
                (0x0061 <= $self->{nc} and  
                 $self->{nc} <= 0x007A)) { # a..z  
         !!!cp (998);  
         require Whatpm::_NamedEntityList;  
         $self->{state} = ENTITY_NAME_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         $self->{entity__value} = $self->{s_kwd};  
         $self->{entity__match} = 0;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (1027);  
         !!!parse-error (type => 'bare ero');  
         ## Return nothing.  
         #  
       }  
   
       ## NOTE: No character is consumed by the "consume a character  
       ## reference" algorithm.  In other word, there is an "&" character  
       ## that does not introduce a character reference, which would be  
       ## appended to the parent element or the attribute value in later  
       ## process of the tokenizer.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (997);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } else {  
         !!!cp (996);  
         $self->{ca}->{value} .= '&';  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_HASH_STATE) {  
       if ($self->{nc} == 0x0078 or # x  
           $self->{nc} == 0x0058) { # X  
         !!!cp (995);  
         $self->{state} = HEXREF_X_STATE;  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0030 <= $self->{nc} and  
                $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (994);  
         $self->{state} = NCR_NUM_STATE;  
         $self->{s_kwd} = $self->{nc} - 0x0030;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare nero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" is appended to the parent element or the attribute  
         ## value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1019);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&#',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1,  
                    });  
           redo A;  
         } else {  
           !!!cp (993);  
           $self->{ca}->{value} .= '&#';  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == NCR_NUM_STATE) {  
       if (0x0030 <= $self->{nc} and  
           $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (1012);  
         $self->{s_kwd} *= 10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
           
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1013);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1014);  
         !!!parse-error (type => 'no refc');  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1015);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1016);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (992);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (991);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == HEXREF_X_STATE) {  
       if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or  
           (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or  
           (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {  
         # 0..9, A..F, a..f  
         !!!cp (990);  
         $self->{state} = HEXREF_HEX_STATE;  
         $self->{s_kwd} = 0;  
         ## Reconsume.  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare hcro',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" followed by "X" or "x" is appended to the parent  
         ## element or the attribute value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1005);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (989);  
           $self->{ca}->{value} .= '&' . $self->{s_kwd};  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == HEXREF_HEX_STATE) {  
       if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {  
         # 0..9  
         !!!cp (1002);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x0066) { # a..f  
         !!!cp (1003);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0060 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x0046) { # A..F  
         !!!cp (1004);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0040 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1006);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1007);  
         !!!parse-error (type => 'no refc',  
                         line => $self->{line},  
                         column => $self->{column});  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1008);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1009);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (988);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (987);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_NAME_STATE) {  
       if (length $self->{s_kwd} < 30 and  
           ## NOTE: Some number greater than the maximum length of entity name  
           ((0x0041 <= $self->{nc} and # a  
             $self->{nc} <= 0x005A) or # x  
            (0x0061 <= $self->{nc} and # a  
             $self->{nc} <= 0x007A) or # z  
            (0x0030 <= $self->{nc} and # 0  
             $self->{nc} <= 0x0039) or # 9  
            $self->{nc} == 0x003B)) { # ;  
         our $EntityChar;  
         $self->{s_kwd} .= chr $self->{nc};  
         if (defined $EntityChar->{$self->{s_kwd}}) {  
           if ($self->{nc} == 0x003B) { # ;  
             !!!cp (1020);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = 1;  
             !!!next-input-character;  
             #  
           } else {  
             !!!cp (1021);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = -1;  
             ## Stay in the state.  
             !!!next-input-character;  
             redo A;  
           }  
         } else {  
           !!!cp (1022);  
           $self->{entity__value} .= chr $self->{nc};  
           $self->{entity__match} *= 2;  
           ## Stay in the state.  
           !!!next-input-character;  
           redo A;  
         }  
       }  
   
       my $data;  
       my $has_ref;  
       if ($self->{entity__match} > 0) {  
         !!!cp (1023);  
         $data = $self->{entity__value};  
         $has_ref = 1;  
         #  
       } elsif ($self->{entity__match} < 0) {  
         !!!parse-error (type => 'no refc');  
         if ($self->{prev_state} != DATA_STATE and # in attribute  
             $self->{entity__match} < -1) {  
           !!!cp (1024);  
           $data = '&' . $self->{s_kwd};  
           #  
         } else {  
           !!!cp (1025);  
           $data = $self->{entity__value};  
           $has_ref = 1;  
           #  
         }  
       } else {  
         !!!cp (1026);  
         !!!parse-error (type => 'bare ero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - length $self->{s_kwd});  
         $data = '&' . $self->{s_kwd};  
         #  
       }  
     
       ## NOTE: In these cases, when a character reference is found,  
       ## it is consumed and a character token is returned, or, otherwise,  
       ## nothing is consumed and returned, according to the spec algorithm.  
       ## In this implementation, anything that has been examined by the  
       ## tokenizer is appended to the parent element or the attribute value  
       ## as string, either literal string when no character reference or  
       ## entity-replaced string otherwise, in this stage, since any characters  
       ## that would not be consumed are appended in the data state or in an  
       ## appropriate attribute value state anyway.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (986);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN,  
                   data => $data,  
                   line => $self->{line_prev},  
                   column => $self->{column_prev} + 1 - length $self->{s_kwd},  
                  });  
         redo A;  
       } else {  
         !!!cp (985);  
         $self->{ca}->{value} .= $data;  
         $self->{ca}->{has_reference} = 1 if $has_ref;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } else {  
       die "$0: $self->{state}: Unknown state";  
     }  
   } # A    
   
   die "$0: _get_next_token: unexpected case";  
 } # _get_next_token  
   
859  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
860    my $self = shift;    my $self = shift;
861    ## NOTE: $self->{document} MUST be specified before this method is called    ## NOTE: $self->{document} MUST be specified before this method is called
# Line 3467  sub _construct_tree ($) { Line 884  sub _construct_tree ($) {
884    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
885    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
886    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
887        
888    !!!next-token;    !!!next-token;
889    
890    undef $self->{form_element};    undef $self->{form_element};
891    undef $self->{head_element};    undef $self->{head_element};
892      undef $self->{head_element_inserted};
893    $self->{open_elements} = [];    $self->{open_elements} = [];
894    undef $self->{inner_html_node};    undef $self->{inner_html_node};
895      undef $self->{ignore_newline};
896    
897    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
898    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3497  sub _tree_construction_initial ($) { Line 912  sub _tree_construction_initial ($) {
912    
913    INITIAL: {    INITIAL: {
914      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
915        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
916        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
917        ## language.        ## another language.  (We don't support such mode switchings; it
918          ## is nonsense to do anything different from what browsers do.)
919        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
920        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
921        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        my $doctype = $self->{document}->create_document_type_definition
922        if (not defined $token->{name} or # <!DOCTYPE>            ($doctype_name);
923            defined $token->{sysid}) {  
924          $doctype_name =~ tr/A-Z/a-z/; # ASCII case-insensitive
925          if ($doctype_name ne 'html') {
926          !!!cp ('t1');          !!!cp ('t1');
927          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
928        } elsif ($doctype_name ne 'HTML') {        } elsif (defined $token->{pubid}) {
929          !!!cp ('t2');          !!!cp ('t2');
930            ## XXX Obsolete permitted DOCTYPEs
931          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
932        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{sysid}) {
933          if ($token->{pubid} eq 'XSLT-compat') {          if ($token->{sysid} eq 'about:legacy-compat') {
934            !!!cp ('t1.2');            !!!cp ('t1.2'); ## <!DOCTYPE HTML SYSTEM "about:legacy-compat">
935            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
936                            level => $self->{level}->{should});                            level => $self->{level}->{should});
937          } else {          } else {
938            !!!parse-error (type => 'not HTML5', token => $token);            !!!parse-error (type => 'not HTML5', token => $token);
939          }          }
940        } else {        } else { ## <!DOCTYPE HTML>
941          !!!cp ('t3');          !!!cp ('t3');
942          #          #
943        }        }
944                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
945        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
946        ## 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.
947        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
948        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
949    
950        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
951        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
952          ## string, while |null| is an allowed value for the attribute
953          ## according to DOM3 Core.
954        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
955                
956        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'html') {
957          !!!cp ('t4');          !!!cp ('t4');
958          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
959        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{pubid}) {
# Line 3783  sub _tree_construction_root_element ($) Line 1203  sub _tree_construction_root_element ($)
1203      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
1204      !!!ack-later;      !!!ack-later;
1205      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
1206    } # B    } # B
1207    
1208    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3820  sub _reset_insertion_mode ($) { Line 1238  sub _reset_insertion_mode ($) {
1238          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1239          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1240          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1241        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1242          if ($last) {          if ($last) {
1243            !!!cp ('t28.2');            !!!cp ('t28.2');
1244            #            #
# Line 3849  sub _reset_insertion_mode ($) { Line 1267  sub _reset_insertion_mode ($) {
1267        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1268                
1269        ## Step 15        ## Step 15
1270        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1271          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1272            !!!cp ('t29');            !!!cp ('t29');
1273            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3981  sub _tree_construction_main ($) { Line 1399  sub _tree_construction_main ($) {
1399    
1400      ## Step 1      ## Step 1
1401      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1402      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1403    
1404      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1405      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1406      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1407    
1408      ## Step 4      ## Step 3, 4
1409      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!nack ('t40.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
1410    
1411      ## Step 6      !!!nack ('t40.1');
     $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";  
       }  
     }  
1412      !!!next-token;      !!!next-token;
1413    }; # $parse_rcdata    }; # $parse_rcdata
1414    
1415    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1416        ## Step 1
1417      my $script_el;      my $script_el;
1418      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1419    
1420        ## Step 2
1421      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1422    
1423        ## Step 3
1424        ## TODO: Mark as "already executed", if ...
1425    
1426        ## Step 4 (HTML5 revision 2702)
1427        $insert->($script_el);
1428        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1429    
1430        ## Step 5
1431      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1432      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;  
1433    
1434      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1435          $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  
1436    
1437        $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...  
     }  
       
1438      !!!next-token;      !!!next-token;
1439    }; # $script_start_tag    }; # $script_start_tag
1440    
1441    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1442    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag (OBSOLETE; unused).
1443      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1444    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1445    
1446    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4169  sub _tree_construction_main ($) { Line 1525  sub _tree_construction_main ($) {
1525            !!!cp ('t59');            !!!cp ('t59');
1526            $furthest_block = $node;            $furthest_block = $node;
1527            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1528              ## NOTE: The topmost (eldest) node.
1529          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1530            !!!cp ('t60');            !!!cp ('t60');
1531            last OE;            last OE;
# Line 4255  sub _tree_construction_main ($) { Line 1612  sub _tree_construction_main ($) {
1612          my $foster_parent_element;          my $foster_parent_element;
1613          my $next_sibling;          my $next_sibling;
1614          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1615            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1616                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1617                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1618                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 4315  sub _tree_construction_main ($) { Line 1672  sub _tree_construction_main ($) {
1672            $i = $_;            $i = $_;
1673          }          }
1674        } # OE        } # OE
1675        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1676                
1677        ## Step 14        ## Step 14
1678        redo FET;        redo FET;
# Line 4333  sub _tree_construction_main ($) { Line 1690  sub _tree_construction_main ($) {
1690        my $foster_parent_element;        my $foster_parent_element;
1691        my $next_sibling;        my $next_sibling;
1692        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1693          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1694                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1695                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1696                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 4358  sub _tree_construction_main ($) { Line 1715  sub _tree_construction_main ($) {
1715      }      }
1716    }; # $insert_to_foster    }; # $insert_to_foster
1717    
1718      ## NOTE: Insert a character (MUST): When a character is inserted, if
1719      ## the last node that was inserted by the parser is a Text node and
1720      ## the character has to be inserted after that node, then the
1721      ## character is appended to the Text node.  However, if any other
1722      ## node is inserted by the parser, then a new Text node is created
1723      ## and the character is appended as that Text node.  If I'm not
1724      ## wrong, for a parser with scripting disabled, there are only two
1725      ## cases where this occurs.  One is the case where an element node
1726      ## is inserted to the |head| element.  This is covered by using the
1727      ## |$self->{head_element_inserted}| flag.  Another is the case where
1728      ## an element or comment is inserted into the |table| subtree while
1729      ## foster parenting happens.  This is covered by using the [2] flag
1730      ## of the |$open_tables| structure.  All other cases are handled
1731      ## simply by calling |manakai_append_text| method.
1732    
1733      ## TODO: |<body><script>document.write("a<br>");
1734      ## document.body.removeChild (document.body.lastChild);
1735      ## document.write ("b")</script>|
1736    
1737    B: while (1) {    B: while (1) {
1738    
1739        ## The "in table text" insertion mode.
1740        if ($self->{insertion_mode} & TABLE_IMS and
1741            not $self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
1742            not $self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1743          C: {
1744            my $s;
1745            if ($token->{type} == CHARACTER_TOKEN) {
1746              !!!cp ('t194');
1747              $self->{pending_chars} ||= [];
1748              push @{$self->{pending_chars}}, $token;
1749              !!!next-token;
1750              next B;
1751            } else {
1752              if ($self->{pending_chars}) {
1753                $s = join '', map { $_->{data} } @{$self->{pending_chars}};
1754                delete $self->{pending_chars};
1755                if ($s =~ /[^\x09\x0A\x0C\x0D\x20]/) {
1756                  !!!cp ('t195');
1757                  #
1758                } else {
1759                  !!!cp ('t195.1');
1760                  #$self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1761                  $self->{open_elements}->[-1]->[0]->append_child
1762                      ($self->{document}->create_text_node ($s));
1763                  last C;
1764                }
1765              } else {
1766                !!!cp ('t195.2');
1767                last C;
1768              }
1769            }
1770    
1771            ## Foster parenting
1772            !!!parse-error (type => 'in table:#text', token => $token);
1773    
1774            ## NOTE: As if in body, but insert into the foster parent element.
1775            $reconstruct_active_formatting_elements->($insert_to_foster);
1776                
1777            if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
1778              # MUST
1779              my $foster_parent_element;
1780              my $next_sibling;
1781              #my $prev_sibling;
1782              OE: for (reverse 0..$#{$self->{open_elements}}) {
1783                if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1784                  my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1785                  if (defined $parent and $parent->node_type == 1) {
1786                    $foster_parent_element = $parent;
1787                    !!!cp ('t196');
1788                    $next_sibling = $self->{open_elements}->[$_]->[0];
1789              #      $prev_sibling = $next_sibling->previous_sibling;
1790                    #
1791                  } else {
1792                    !!!cp ('t197');
1793                    $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1794              #      $prev_sibling = $foster_parent_element->last_child;
1795                    #
1796                  }
1797                  last OE;
1798                }
1799              } # OE
1800              $foster_parent_element = $self->{open_elements}->[0]->[0] #and
1801              #$prev_sibling = $foster_parent_element->last_child
1802                  unless defined $foster_parent_element;
1803              #undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
1804              #if (defined $prev_sibling and
1805              #    $prev_sibling->node_type == 3) {
1806              #  !!! cp ('t198');
1807              #  $prev_sibling->manakai_append_text ($s);
1808              #} else {
1809                !!!cp ('t199');
1810                $foster_parent_element->insert_before
1811                    ($self->{document}->create_text_node ($s), $next_sibling);
1812              #}
1813              $open_tables->[-1]->[1] = 1; # tainted
1814              $open_tables->[-1]->[2] = 1; # ~node inserted
1815            } else {
1816              ## NOTE: Fragment case or in a foster parent'ed element
1817              ## (e.g. |<table><span>a|).  In fragment case, whether the
1818              ## character is appended to existing node or a new node is
1819              ## created is irrelevant, since the foster parent'ed nodes
1820              ## are discarded and fragment parsing does not invoke any
1821              ## script.
1822              !!!cp ('t200');
1823              $self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1824            }
1825          } # C
1826        } # TABLE_IMS
1827    
1828      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1829        !!!cp ('t73');        !!!cp ('t73');
1830        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
# Line 4405  sub _tree_construction_main ($) { Line 1871  sub _tree_construction_main ($) {
1871        } else {        } else {
1872          !!!cp ('t87');          !!!cp ('t87');
1873          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1874            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1875        }        }
1876        !!!next-token;        !!!next-token;
1877        next B;        next B;
1878        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1879          if ($token->{type} == CHARACTER_TOKEN) {
1880            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1881            delete $self->{ignore_newline};
1882    
1883            if (length $token->{data}) {
1884              !!!cp ('t43');
1885              $self->{open_elements}->[-1]->[0]->manakai_append_text
1886                  ($token->{data});
1887            } else {
1888              !!!cp ('t43.1');
1889            }
1890            !!!next-token;
1891            next B;
1892          } elsif ($token->{type} == END_TAG_TOKEN) {
1893            delete $self->{ignore_newline};
1894    
1895            if ($token->{tag_name} eq 'script') {
1896              !!!cp ('t50');
1897              
1898              ## Para 1-2
1899              my $script = pop @{$self->{open_elements}};
1900              
1901              ## Para 3
1902              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1903    
1904              ## Para 4
1905              ## TODO: $old_insertion_point = $current_insertion_point;
1906              ## TODO: $current_insertion_point = just before $self->{nc};
1907    
1908              ## Para 5
1909              ## TODO: Run the $script->[0].
1910    
1911              ## Para 6
1912              ## TODO: $current_insertion_point = $old_insertion_point;
1913    
1914              ## Para 7
1915              ## TODO: if ($pending_external_script) {
1916                ## TODO: ...
1917              ## TODO: }
1918    
1919              !!!next-token;
1920              next B;
1921            } else {
1922              !!!cp ('t42');
1923    
1924              pop @{$self->{open_elements}};
1925    
1926              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1927              !!!next-token;
1928              next B;
1929            }
1930          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1931            delete $self->{ignore_newline};
1932    
1933            !!!cp ('t44');
1934            !!!parse-error (type => 'not closed',
1935                            text => $self->{open_elements}->[-1]->[0]
1936                                ->manakai_local_name,
1937                            token => $token);
1938    
1939            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1940            #  ## TODO: Mark as "already executed"
1941            #}
1942    
1943            pop @{$self->{open_elements}};
1944    
1945            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1946            ## Reprocess.
1947            next B;
1948          } else {
1949            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1950          }
1951      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1952        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1953          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4419  sub _tree_construction_main ($) { Line 1959  sub _tree_construction_main ($) {
1959               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1960              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1961              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1962               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1963            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1964            !!!cp ('t87.2');            !!!cp ('t87.2');
1965            #            #
1966          } elsif ({          } elsif ({
1967                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1968                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1969                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1970                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1971                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1972                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1973                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1974                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1975                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1976                     ($token->{tag_name} eq 'font' and
1977                      ($token->{attributes}->{color} or
1978                       $token->{attributes}->{face} or
1979                       $token->{attributes}->{size}))) {
1980            !!!cp ('t87.2');            !!!cp ('t87.2');
1981            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1982                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4508  sub _tree_construction_main ($) { Line 2052  sub _tree_construction_main ($) {
2052          }          }
2053        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2054          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
2055          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
2056          #            !!!cp ('t87.41');
2057              #
2058              ## XXXscript: Execute script here.
2059            } else {
2060              !!!cp ('t87.5');
2061              #
2062            }
2063        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2064          !!!cp ('t87.6');          !!!cp ('t87.6');
2065          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4520  sub _tree_construction_main ($) { Line 2070  sub _tree_construction_main ($) {
2070          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
2071              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
2072    
2073            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
2074    
2075          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
2076          ## Reprocess.          ## Reprocess.
2077          next B;          next B;
# Line 4532  sub _tree_construction_main ($) { Line 2084  sub _tree_construction_main ($) {
2084        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
2085          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
2086            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2087              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
2088              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
2089              #                $self->{open_elements}->[-1]->[0]->append_child
2090                    ($self->{document}->create_text_node ($1));
2091                  delete $self->{head_element_inserted};
2092                  ## NOTE: |</head> <link> |
2093                  #
2094                } else {
2095                  !!!cp ('t88.2');
2096                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2097                  ## NOTE: |</head> &#x20;|
2098                  #
2099                }
2100            } else {            } else {
2101              !!!cp ('t88.1');              !!!cp ('t88.1');
2102              ## Ignore the token.              ## Ignore the token.
# Line 4630  sub _tree_construction_main ($) { Line 2192  sub _tree_construction_main ($) {
2192            !!!cp ('t97');            !!!cp ('t97');
2193          }          }
2194    
2195              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2196                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2197                  !!!cp ('t98');              !!!cp ('t98');
2198                  ## As if </noscript>              ## As if </noscript>
2199                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2200                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2201                                  token => $token);                              token => $token);
2202                            
2203                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2204                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2205                } else {            } else {
2206                  !!!cp ('t99');              !!!cp ('t99');
2207                }            }
2208    
2209                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2210                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2211                  !!!cp ('t100');              !!!cp ('t100');
2212                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2213                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2214                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2215                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2216                } else {              $self->{head_element_inserted} = 1;
2217                  !!!cp ('t101');            } else {
2218                }              !!!cp ('t101');
2219                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2220                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2221                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2222                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2223                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2224                !!!next-token;            !!!nack ('t101.1');
2225                next B;            !!!next-token;
2226              } elsif ($token->{tag_name} eq 'link') {            next B;
2227                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'link') {
2228                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: There is a "as if in head" code clone.
2229                  !!!cp ('t102');            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2230                  !!!parse-error (type => 'after head',              !!!cp ('t102');
2231                                  text => $token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
2232                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
2233                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
2234                } else {                  [$self->{head_element}, $el_category->{head}];
2235                  !!!cp ('t103');              $self->{head_element_inserted} = 1;
2236                }            } else {
2237                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!cp ('t103');
2238                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            }
2239                pop @{$self->{open_elements}} # <head>            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2240                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}};
2241                !!!ack ('t103.1');            pop @{$self->{open_elements}} # <head>
2242                !!!next-token;                if $self->{insertion_mode} == AFTER_HEAD_IM;
2243                next B;            !!!ack ('t103.1');
2244              } elsif ($token->{tag_name} eq 'meta') {            !!!next-token;
2245                ## NOTE: There is a "as if in head" code clone.            next B;
2246                if ($self->{insertion_mode} == AFTER_HEAD_IM) {          } elsif ($token->{tag_name} eq 'command') {
2247                  !!!cp ('t104');            if ($self->{insertion_mode} == IN_HEAD_IM) {
2248                  !!!parse-error (type => 'after head',              ## NOTE: If the insertion mode at the time of the emission
2249                                  text => $token->{tag_name}, token => $token);              ## of the token was "before head", $self->{insertion_mode}
2250                  push @{$self->{open_elements}},              ## is already changed to |IN_HEAD_IM|.
2251                      [$self->{head_element}, $el_category->{head}];  
2252                } else {              ## NOTE: There is a "as if in head" code clone.
2253                  !!!cp ('t105');              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2254                }              pop @{$self->{open_elements}};
2255                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              pop @{$self->{open_elements}} # <head>
2256                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                  if $self->{insertion_mode} == AFTER_HEAD_IM;
2257                !!!ack ('t103.2');
2258                !!!next-token;
2259                next B;
2260              } else {
2261                ## NOTE: "in head noscript" or "after head" insertion mode
2262                ## - in these cases, these tags are treated as same as
2263                ## normal in-body tags.
2264                !!!cp ('t103.3');
2265                #
2266              }
2267            } elsif ($token->{tag_name} eq 'meta') {
2268              ## NOTE: There is a "as if in head" code clone.
2269              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2270                !!!cp ('t104');
2271                !!!parse-error (type => 'after head',
2272                                text => $token->{tag_name}, token => $token);
2273                push @{$self->{open_elements}},
2274                    [$self->{head_element}, $el_category->{head}];
2275                $self->{head_element_inserted} = 1;
2276              } else {
2277                !!!cp ('t105');
2278              }
2279              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2280              my $meta_el = pop @{$self->{open_elements}};
2281    
2282                unless ($self->{confident}) {                unless ($self->{confident}) {
2283                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4749  sub _tree_construction_main ($) { Line 2335  sub _tree_construction_main ($) {
2335                !!!ack ('t110.1');                !!!ack ('t110.1');
2336                !!!next-token;                !!!next-token;
2337                next B;                next B;
2338              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2339                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2340                  !!!cp ('t111');              !!!cp ('t111');
2341                  ## As if </noscript>              ## As if </noscript>
2342                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2343                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2344                                  token => $token);                              token => $token);
2345                            
2346                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2347                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2348                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2349                  !!!cp ('t112');              !!!cp ('t112');
2350                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2351                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2352                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2353                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2354                } else {              $self->{head_element_inserted} = 1;
2355                  !!!cp ('t113');            } else {
2356                }              !!!cp ('t113');
2357              }
2358    
2359                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2360                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2361                    : $self->{open_elements}->[-1]->[0];  
2362                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2363                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2364                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2365                next B;            ## |head| element is inserted only for the purpose of
2366              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2367                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2368                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2369                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2370                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2371                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2372                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2373                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2374                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2375                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2376                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2377                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2378                  !!!cp ('t115');              !!!cp ('t114');
2379                }              !!!parse-error (type => 'after head',
2380                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2381                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2382                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2383                next B;              $self->{head_element_inserted} = 1;
2384              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2385                !!!cp ('t115');
2386              }
2387              $parse_rcdata->(CDATA_CONTENT_MODEL);
2388              ## ISSUE: A spec bug [Bug 6038]
2389              splice @{$self->{open_elements}}, -2, 1, () # <head>
2390                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2391              next B;
2392            } elsif ($token->{tag_name} eq 'noscript') {
2393                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2394                  !!!cp ('t116');                  !!!cp ('t116');
2395                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4815  sub _tree_construction_main ($) { Line 2410  sub _tree_construction_main ($) {
2410                  !!!cp ('t118');                  !!!cp ('t118');
2411                  #                  #
2412                }                }
2413              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2414                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2415                  !!!cp ('t119');              !!!cp ('t119');
2416                  ## As if </noscript>              ## As if </noscript>
2417                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2418                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2419                                  token => $token);                              token => $token);
2420                            
2421                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2422                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2423                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2424                  !!!cp ('t120');              !!!cp ('t120');
2425                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2426                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2427                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2428                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2429                } else {              $self->{head_element_inserted} = 1;
2430                  !!!cp ('t121');            } else {
2431                }              !!!cp ('t121');
2432              }
2433    
2434                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2435                $script_start_tag->();            $script_start_tag->();
2436                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2437                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2438                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2439              } elsif ($token->{tag_name} eq 'body' or            next B;
2440                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2441                     $token->{tag_name} eq 'frameset') {
2442                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2443                  !!!cp ('t122');                  !!!cp ('t122');
2444                  ## As if </noscript>                  ## As if </noscript>
# Line 4976  sub _tree_construction_main ($) { Line 2573  sub _tree_construction_main ($) {
2573              } elsif ({              } elsif ({
2574                        body => 1, html => 1,                        body => 1, html => 1,
2575                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2576                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2577                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2578                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2579                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2580                  !!!cp ('t140');                  !!!cp ('t140');
# Line 5002  sub _tree_construction_main ($) { Line 2600  sub _tree_construction_main ($) {
2600                ## Ignore the token                ## Ignore the token
2601                !!!next-token;                !!!next-token;
2602                next B;                next B;
2603              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2604                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2605                  !!!cp ('t142.2');              !!!cp ('t142.2');
2606                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2607                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2608                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2609                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2610        
2611                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2612                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2613                  !!!cp ('t143.2');              !!!cp ('t143.2');
2614                  ## As if </head>              ## As if </head>
2615                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2616                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2617        
2618                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2619                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2620                  !!!cp ('t143.3');              !!!cp ('t143.3');
2621                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2622                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2623                                  text => 'br', token => $token);                              text => 'br', token => $token);
2624                  ## As if </noscript>              ## As if </noscript>
2625                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2626                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2627    
2628                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2629                  ## As if </head>              ## As if </head>
2630                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2631                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2632    
2633                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2634                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2635                  !!!cp ('t143.4');              !!!cp ('t143.4');
2636                  #              #
2637                } else {            } else {
2638                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2639                }            }
2640    
2641                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2642                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2643                !!!cp ('t145');                !!!cp ('t145');
2644                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2645                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5090  sub _tree_construction_main ($) { Line 2683  sub _tree_construction_main ($) {
2683              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2684              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2685              ## reprocess              ## reprocess
2686              next B;          next B;
2687        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2688          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2689            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5148  sub _tree_construction_main ($) { Line 2741  sub _tree_construction_main ($) {
2741        } else {        } else {
2742          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2743        }        }
   
           ## ISSUE: An issue in the spec.  
2744      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2745            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
2746              !!!cp ('t150');              !!!cp ('t150');
# Line 5165  sub _tree_construction_main ($) { Line 2756  sub _tree_construction_main ($) {
2756                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2757                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2758                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2759                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2760                  ## have an element in table scope                  ## have an element in table scope
2761                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2762                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2763                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2764                      !!!cp ('t151');                      !!!cp ('t151');
2765    
2766                      ## Close the cell                      ## Close the cell
# Line 5193  sub _tree_construction_main ($) { Line 2784  sub _tree_construction_main ($) {
2784                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2785                  !!!next-token;                  !!!next-token;
2786                  next B;                  next B;
2787                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2788                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2789                                  token => $token);                                  token => $token);
2790                                    
# Line 5203  sub _tree_construction_main ($) { Line 2794  sub _tree_construction_main ($) {
2794                  INSCOPE: {                  INSCOPE: {
2795                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2796                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2797                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2798                        !!!cp ('t155');                        !!!cp ('t155');
2799                        $i = $_;                        $i = $_;
2800                        last INSCOPE;                        last INSCOPE;
# Line 5229  sub _tree_construction_main ($) { Line 2820  sub _tree_construction_main ($) {
2820                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2821                  }                  }
2822    
2823                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2824                    !!!cp ('t159');                    !!!cp ('t159');
2825                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2826                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5258  sub _tree_construction_main ($) { Line 2849  sub _tree_construction_main ($) {
2849              }              }
2850            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2851              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2852                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2853                  ## have an element in table scope                  ## have an element in table scope
2854                  my $i;                  my $i;
2855                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5308  sub _tree_construction_main ($) { Line 2899  sub _tree_construction_main ($) {
2899                                    
2900                  !!!next-token;                  !!!next-token;
2901                  next B;                  next B;
2902                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2903                  !!!cp ('t169');                  !!!cp ('t169');
2904                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2905                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5320  sub _tree_construction_main ($) { Line 2911  sub _tree_construction_main ($) {
2911                  #                  #
2912                }                }
2913              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2914                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2915                  ## have a table element in table scope                  ## have a table element in table scope
2916                  my $i;                  my $i;
2917                  INSCOPE: {                  INSCOPE: {
2918                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2919                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2920                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2921                        !!!cp ('t171');                        !!!cp ('t171');
2922                        $i = $_;                        $i = $_;
2923                        last INSCOPE;                        last INSCOPE;
# Line 5351  sub _tree_construction_main ($) { Line 2942  sub _tree_construction_main ($) {
2942                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2943                  }                  }
2944                                    
2945                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2946                    !!!cp ('t175');                    !!!cp ('t175');
2947                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2948                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5369  sub _tree_construction_main ($) { Line 2960  sub _tree_construction_main ($) {
2960                                    
2961                  !!!next-token;                  !!!next-token;
2962                  next B;                  next B;
2963                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2964                  !!!cp ('t177');                  !!!cp ('t177');
2965                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2966                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5384  sub _tree_construction_main ($) { Line 2975  sub _tree_construction_main ($) {
2975                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2976                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2977                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2978                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2979                ## have an element in table scope                ## have an element in table scope
2980                my $i;                my $i;
2981                my $tn;                my $tn;
# Line 5401  sub _tree_construction_main ($) { Line 2992  sub _tree_construction_main ($) {
2992                                line => $token->{line},                                line => $token->{line},
2993                                column => $token->{column}};                                column => $token->{column}};
2994                      next B;                      next B;
2995                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2996                      !!!cp ('t180');                      !!!cp ('t180');
2997                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2998                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5421  sub _tree_construction_main ($) { Line 3012  sub _tree_construction_main ($) {
3012                  next B;                  next B;
3013                } # INSCOPE                } # INSCOPE
3014              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
3015                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
3016                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
3017                                token => $token);                                token => $token);
3018    
# Line 5430  sub _tree_construction_main ($) { Line 3021  sub _tree_construction_main ($) {
3021                my $i;                my $i;
3022                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3023                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3024                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
3025                    !!!cp ('t184');                    !!!cp ('t184');
3026                    $i = $_;                    $i = $_;
3027                    last INSCOPE;                    last INSCOPE;
# Line 5441  sub _tree_construction_main ($) { Line 3032  sub _tree_construction_main ($) {
3032                } # INSCOPE                } # INSCOPE
3033                unless (defined $i) {                unless (defined $i) {
3034                  !!!cp ('t186');                  !!!cp ('t186');
3035            ## TODO: Wrong error type?
3036                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3037                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
3038                  ## Ignore the token                  ## Ignore the token
# Line 5454  sub _tree_construction_main ($) { Line 3046  sub _tree_construction_main ($) {
3046                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3047                }                }
3048    
3049                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
3050                  !!!cp ('t188');                  !!!cp ('t188');
3051                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
3052                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5486  sub _tree_construction_main ($) { Line 3078  sub _tree_construction_main ($) {
3078                  !!!cp ('t191');                  !!!cp ('t191');
3079                  #                  #
3080                }                }
3081              } elsif ({          } elsif ({
3082                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
3083                        thead => 1, tr => 1,                    thead => 1, tr => 1,
3084                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
3085                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
3086                !!!cp ('t192');            !!!cp ('t192');
3087                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
3088                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
3089                ## Ignore the token            ## Ignore the token
3090                !!!next-token;            !!!next-token;
3091                next B;            next B;
3092              } else {          } else {
3093                !!!cp ('t193');            !!!cp ('t193');
3094                #            #
3095              }          }
3096        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3097          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3098            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5519  sub _tree_construction_main ($) { Line 3111  sub _tree_construction_main ($) {
3111        $insert = $insert_to_current;        $insert = $insert_to_current;
3112        #        #
3113      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3114        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == START_TAG_TOKEN) {
         if (not $open_tables->[-1]->[1] and # tainted  
             $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {  
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
           unless (length $token->{data}) {  
             !!!cp ('t194');  
             !!!next-token;  
             next B;  
           } else {  
             !!!cp ('t195');  
           }  
         }  
   
         !!!parse-error (type => 'in table:#text', token => $token);  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     !!!cp ('t196');  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 !!!cp ('t199');  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
           $open_tables->[-1]->[1] = 1; # tainted  
         } else {  
           !!!cp ('t200');  
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
         }  
               
         !!!next-token;  
         next B;  
       } elsif ($token->{type} == START_TAG_TOKEN) {  
3115          if ({          if ({
3116               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3117               th => 1, td => 1,               th => 1, td => 1,
3118              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3119            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3120              ## Clear back to table context              ## Clear back to table context
3121              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3122                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5601  sub _tree_construction_main ($) { Line 3129  sub _tree_construction_main ($) {
3129              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3130            }            }
3131                        
3132            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3133              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3134                !!!cp ('t202');                !!!cp ('t202');
3135                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5615  sub _tree_construction_main ($) { Line 3143  sub _tree_construction_main ($) {
3143                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3144              }              }
3145                                    
3146                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3147                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3148                    !!!cp ('t204');                !!!cp ('t204');
3149                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3150                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3151                    !!!next-token;                !!!nack ('t204');
3152                    next B;                !!!next-token;
3153                  } else {                next B;
3154                    !!!cp ('t205');              } else {
3155                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3156                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3157                  }                ## reprocess in the "in row" insertion mode
3158                } else {              }
3159                  !!!cp ('t206');            } else {
3160                }              !!!cp ('t206');
3161              }
3162    
3163                ## Clear back to table row context                ## Clear back to table row context
3164                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5638  sub _tree_construction_main ($) { Line 3167  sub _tree_construction_main ($) {
3167                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3168                }                }
3169                                
3170                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3171                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3172              $self->{insertion_mode} = IN_CELL_IM;
3173    
3174                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3175                                
3176                !!!nack ('t207.1');            !!!nack ('t207.1');
3177              !!!next-token;
3178              next B;
3179            } elsif ({
3180                      caption => 1, col => 1, colgroup => 1,
3181                      tbody => 1, tfoot => 1, thead => 1,
3182                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3183                     }->{$token->{tag_name}}) {
3184              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3185                ## As if </tr>
3186                ## have an element in table scope
3187                my $i;
3188                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3189                  my $node = $self->{open_elements}->[$_];
3190                  if ($node->[1] == TABLE_ROW_EL) {
3191                    !!!cp ('t208');
3192                    $i = $_;
3193                    last INSCOPE;
3194                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3195                    !!!cp ('t209');
3196                    last INSCOPE;
3197                  }
3198                } # INSCOPE
3199                unless (defined $i) {
3200                  !!!cp ('t210');
3201                  ## TODO: This type is wrong.
3202                  !!!parse-error (type => 'unmacthed end tag',
3203                                  text => $token->{tag_name}, token => $token);
3204                  ## Ignore the token
3205                  !!!nack ('t210.1');
3206                !!!next-token;                !!!next-token;
3207                next B;                next B;
3208              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] & TABLE_ROW_EL) {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ($node->[1] & TABLE_SCOPING_EL) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                   !!!cp ('t210');  
 ## TODO: This type is wrong.  
                   !!!parse-error (type => 'unmacthed end tag',  
                                   text => $token->{tag_name}, token => $token);  
                   ## Ignore the token  
                   !!!nack ('t210.1');  
                   !!!next-token;  
                   next B;  
                 }  
3209                                    
3210                  ## Clear back to table row context                  ## Clear back to table row context
3211                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5698  sub _tree_construction_main ($) { Line 3228  sub _tree_construction_main ($) {
3228                  }                  }
3229                }                }
3230    
3231                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3232                  ## have an element in table scope                  ## have an element in table scope
3233                  my $i;                  my $i;
3234                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3235                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3236                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3237                      !!!cp ('t214');                      !!!cp ('t214');
3238                      $i = $_;                      $i = $_;
3239                      last INSCOPE;                      last INSCOPE;
# Line 5745  sub _tree_construction_main ($) { Line 3275  sub _tree_construction_main ($) {
3275                  !!!cp ('t218');                  !!!cp ('t218');
3276                }                }
3277    
3278                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3279                  ## Clear back to table context              ## Clear back to table context
3280                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3281                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3282                    !!!cp ('t219');                !!!cp ('t219');
3283                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3284                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3285                  }              }
3286                                
3287                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3288                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3289                  ## reprocess              ## reprocess
3290                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3291                  next B;              !!!ack-later;
3292                } elsif ({              next B;
3293                          caption => 1,            } elsif ({
3294                          colgroup => 1,                      caption => 1,
3295                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3296                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3297                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3298                ## Clear back to table context
3299                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3300                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3301                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5772  sub _tree_construction_main ($) { Line 3303  sub _tree_construction_main ($) {
3303                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3304                  }                  }
3305                                    
3306                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3307                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3308                                    
3309                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3310                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3311                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3312                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3313                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3314                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3315                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3316                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3317                  !!!next-token;                                        }->{$token->{tag_name}};
3318                  !!!nack ('t220.1');              !!!next-token;
3319                  next B;              !!!nack ('t220.1');
3320                } else {              next B;
3321                  die "$0: in table: <>: $token->{tag_name}";            } else {
3322                }              die "$0: in table: <>: $token->{tag_name}";
3323              }
3324              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3325                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3326                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5800  sub _tree_construction_main ($) { Line 3332  sub _tree_construction_main ($) {
3332                my $i;                my $i;
3333                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3334                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3335                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3336                    !!!cp ('t221');                    !!!cp ('t221');
3337                    $i = $_;                    $i = $_;
3338                    last INSCOPE;                    last INSCOPE;
# Line 5827  sub _tree_construction_main ($) { Line 3359  sub _tree_construction_main ($) {
3359                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3360                }                }
3361    
3362                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3363                  !!!cp ('t225');                  !!!cp ('t225');
3364                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3365                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5851  sub _tree_construction_main ($) { Line 3383  sub _tree_construction_main ($) {
3383              !!!cp ('t227.8');              !!!cp ('t227.8');
3384              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3385              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
3386                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3387              next B;              next B;
3388            } else {            } else {
3389              !!!cp ('t227.7');              !!!cp ('t227.7');
# Line 5861  sub _tree_construction_main ($) { Line 3394  sub _tree_construction_main ($) {
3394              !!!cp ('t227.6');              !!!cp ('t227.6');
3395              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3396              $script_start_tag->();              $script_start_tag->();
3397                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3398              next B;              next B;
3399            } else {            } else {
3400              !!!cp ('t227.5');              !!!cp ('t227.5');
# Line 5876  sub _tree_construction_main ($) { Line 3410  sub _tree_construction_main ($) {
3410                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
3411    
3412                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3413                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3414    
3415                  ## TODO: form element pointer                  ## TODO: form element pointer
3416    
# Line 5907  sub _tree_construction_main ($) { Line 3442  sub _tree_construction_main ($) {
3442          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3443          #          #
3444        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3445              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3446                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3447                ## have an element in table scope            ## have an element in table scope
3448                my $i;                my $i;
3449                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3450                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3451                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3452                    !!!cp ('t228');                    !!!cp ('t228');
3453                    $i = $_;                    $i = $_;
3454                    last INSCOPE;                    last INSCOPE;
# Line 5948  sub _tree_construction_main ($) { Line 3483  sub _tree_construction_main ($) {
3483                !!!nack ('t231.1');                !!!nack ('t231.1');
3484                next B;                next B;
3485              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3486                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3487                  ## As if </tr>                  ## As if </tr>
3488                  ## have an element in table scope                  ## have an element in table scope
3489                  my $i;                  my $i;
3490                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3491                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3492                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3493                      !!!cp ('t233');                      !!!cp ('t233');
3494                      $i = $_;                      $i = $_;
3495                      last INSCOPE;                      last INSCOPE;
# Line 5987  sub _tree_construction_main ($) { Line 3522  sub _tree_construction_main ($) {
3522                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3523                }                }
3524    
3525                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3526                  ## have an element in table scope                  ## have an element in table scope
3527                  my $i;                  my $i;
3528                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3529                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3530                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3531                      !!!cp ('t237');                      !!!cp ('t237');
3532                      $i = $_;                      $i = $_;
3533                      last INSCOPE;                      last INSCOPE;
# Line 6039  sub _tree_construction_main ($) { Line 3574  sub _tree_construction_main ($) {
3574                my $i;                my $i;
3575                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3576                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3577                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3578                    !!!cp ('t241');                    !!!cp ('t241');
3579                    $i = $_;                    $i = $_;
3580                    last INSCOPE;                    last INSCOPE;
# Line 6069  sub _tree_construction_main ($) { Line 3604  sub _tree_construction_main ($) {
3604                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3605                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3606                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3607                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3608                  ## have an element in table scope                  ## have an element in table scope
3609                  my $i;                  my $i;
3610                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6098  sub _tree_construction_main ($) { Line 3633  sub _tree_construction_main ($) {
3633                  my $i;                  my $i;
3634                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3635                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3636                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3637                      !!!cp ('t250');                      !!!cp ('t250');
3638                      $i = $_;                      $i = $_;
3639                      last INSCOPE;                      last INSCOPE;
# Line 6188  sub _tree_construction_main ($) { Line 3723  sub _tree_construction_main ($) {
3723            #            #
3724          }          }
3725        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3726          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3727                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3728            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3729            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6203  sub _tree_construction_main ($) { Line 3738  sub _tree_construction_main ($) {
3738        } else {        } else {
3739          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3740        }        }
3741      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3742            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3743              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3744                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6230  sub _tree_construction_main ($) { Line 3765  sub _tree_construction_main ($) {
3765              }              }
3766            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3767              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3768                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3769                  !!!cp ('t264');                  !!!cp ('t264');
3770                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3771                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6256  sub _tree_construction_main ($) { Line 3791  sub _tree_construction_main ($) {
3791                #                #
3792              }              }
3793        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3794          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3795              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3796            !!!cp ('t270.2');            !!!cp ('t270.2');
3797            ## Stop parsing.            ## Stop parsing.
# Line 6274  sub _tree_construction_main ($) { Line 3809  sub _tree_construction_main ($) {
3809        }        }
3810    
3811            ## As if </colgroup>            ## As if </colgroup>
3812            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3813              !!!cp ('t269');              !!!cp ('t269');
3814  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3815              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6299  sub _tree_construction_main ($) { Line 3834  sub _tree_construction_main ($) {
3834          next B;          next B;
3835        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3836          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3837            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3838              !!!cp ('t272');              !!!cp ('t272');
3839              ## As if </option>              ## As if </option>
3840              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6312  sub _tree_construction_main ($) { Line 3847  sub _tree_construction_main ($) {
3847            !!!next-token;            !!!next-token;
3848            next B;            next B;
3849          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3850            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3851              !!!cp ('t274');              !!!cp ('t274');
3852              ## As if </option>              ## As if </option>
3853              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6320  sub _tree_construction_main ($) { Line 3855  sub _tree_construction_main ($) {
3855              !!!cp ('t275');              !!!cp ('t275');
3856            }            }
3857    
3858            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3859              !!!cp ('t276');              !!!cp ('t276');
3860              ## As if </optgroup>              ## As if </optgroup>
3861              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6333  sub _tree_construction_main ($) { Line 3868  sub _tree_construction_main ($) {
3868            !!!next-token;            !!!next-token;
3869            next B;            next B;
3870          } elsif ({          } elsif ({
3871                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3872                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3873                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3874                          == IN_SELECT_IN_TABLE_IM and
3875                    {                    {
3876                     caption => 1, table => 1,                     caption => 1, table => 1,
3877                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3878                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3879                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3880            ## TODO: The type below is not good - <select> is replaced by </select>  
3881            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3882                            token => $token);            if ($token->{tag_name} eq 'select') {
3883            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3884            ## as if there were </select> (otherwise).                                token => $token);
3885            ## have an element in table scope            } else {
3886                !!!parse-error (type => 'not closed', text => 'select',
3887                                token => $token);
3888              }
3889    
3890              ## 2./<select>-1. Unless "have an element in table scope" (select):
3891            my $i;            my $i;
3892            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3893              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3894              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3895                !!!cp ('t278');                !!!cp ('t278');
3896                $i = $_;                $i = $_;
3897                last INSCOPE;                last INSCOPE;
# Line 6361  sub _tree_construction_main ($) { Line 3902  sub _tree_construction_main ($) {
3902            } # INSCOPE            } # INSCOPE
3903            unless (defined $i) {            unless (defined $i) {
3904              !!!cp ('t280');              !!!cp ('t280');
3905              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3906                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3907              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3908                  ## case two errors, "select in select" and "unmatched
3909                  ## end tags" are reported to the user, the latter might
3910                  ## be confusing but this is what the spec requires.
3911                  !!!parse-error (type => 'unmatched end tag',
3912                                  text => 'select',
3913                                  token => $token);
3914                }
3915                ## Ignore the token.
3916              !!!nack ('t280.1');              !!!nack ('t280.1');
3917              !!!next-token;              !!!next-token;
3918              next B;              next B;
3919            }            }
3920    
3921              ## 3. Otherwise, as if there were <select>:
3922                                
3923            !!!cp ('t281');            !!!cp ('t281');
3924            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6384  sub _tree_construction_main ($) { Line 3935  sub _tree_construction_main ($) {
3935              ## Reprocess the token.              ## Reprocess the token.
3936              next B;              next B;
3937            }            }
3938            } elsif ($token->{tag_name} eq 'script') {
3939              !!!cp ('t281.3');
3940              ## NOTE: This is an "as if in head" code clone
3941              $script_start_tag->();
3942              next B;
3943          } else {          } else {
3944            !!!cp ('t282');            !!!cp ('t282');
3945            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6395  sub _tree_construction_main ($) { Line 3951  sub _tree_construction_main ($) {
3951          }          }
3952        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3953          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3954            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3955                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3956              !!!cp ('t283');              !!!cp ('t283');
3957              ## As if </option>              ## As if </option>
3958              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3959            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3960              !!!cp ('t284');              !!!cp ('t284');
3961              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3962            } else {            } else {
# Line 6413  sub _tree_construction_main ($) { Line 3969  sub _tree_construction_main ($) {
3969            !!!next-token;            !!!next-token;
3970            next B;            next B;
3971          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3972            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3973              !!!cp ('t286');              !!!cp ('t286');
3974              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3975            } else {            } else {
# Line 6430  sub _tree_construction_main ($) { Line 3986  sub _tree_construction_main ($) {
3986            my $i;            my $i;
3987            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3988              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3989              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3990                !!!cp ('t288');                !!!cp ('t288');
3991                $i = $_;                $i = $_;
3992                last INSCOPE;                last INSCOPE;
# Line 6457  sub _tree_construction_main ($) { Line 4013  sub _tree_construction_main ($) {
4013            !!!nack ('t291.1');            !!!nack ('t291.1');
4014            !!!next-token;            !!!next-token;
4015            next B;            next B;
4016          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
4017                         == IN_SELECT_IN_TABLE_IM and
4018                   {                   {
4019                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
4020                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6492  sub _tree_construction_main ($) { Line 4049  sub _tree_construction_main ($) {
4049            undef $i;            undef $i;
4050            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4051              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4052              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4053                !!!cp ('t295');                !!!cp ('t295');
4054                $i = $_;                $i = $_;
4055                last INSCOPE;                last INSCOPE;
# Line 6531  sub _tree_construction_main ($) { Line 4088  sub _tree_construction_main ($) {
4088            next B;            next B;
4089          }          }
4090        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4091          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4092                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4093            !!!cp ('t299.1');            !!!cp ('t299.1');
4094            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6718  sub _tree_construction_main ($) { Line 4275  sub _tree_construction_main ($) {
4275        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4276          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4277              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4278            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4279                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4280              !!!cp ('t325');              !!!cp ('t325');
4281              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6732  sub _tree_construction_main ($) { Line 4289  sub _tree_construction_main ($) {
4289            }            }
4290    
4291            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4292                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4293              !!!cp ('t327');              !!!cp ('t327');
4294              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4295            } else {            } else {
# Line 6764  sub _tree_construction_main ($) { Line 4321  sub _tree_construction_main ($) {
4321            next B;            next B;
4322          }          }
4323        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4324          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4325                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4326            !!!cp ('t331.1');            !!!cp ('t331.1');
4327            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6777  sub _tree_construction_main ($) { Line 4334  sub _tree_construction_main ($) {
4334        } else {        } else {
4335          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4336        }        }
   
       ## ISSUE: An issue in spec here  
4337      } else {      } else {
4338        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4339      }      }
# Line 6796  sub _tree_construction_main ($) { Line 4351  sub _tree_construction_main ($) {
4351          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4352          next B;          next B;
4353        } elsif ({        } elsif ({
4354                  base => 1, link => 1,                  base => 1, command => 1, link => 1,
4355                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4356          !!!cp ('t334');          !!!cp ('t334');
4357          ## 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
4358          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4359          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
4360          !!!ack ('t334.1');          !!!ack ('t334.1');
4361          !!!next-token;          !!!next-token;
4362          next B;          next B;
4363        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
4364          ## 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
4365          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4366          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
4367    
4368          unless ($self->{confident}) {          unless ($self->{confident}) {
4369            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
# Line 6869  sub _tree_construction_main ($) { Line 4424  sub _tree_construction_main ($) {
4424          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4425                                
4426          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4427              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4428            !!!cp ('t342');            !!!cp ('t342');
4429            ## Ignore the token            ## Ignore the token
4430          } else {          } else {
# Line 6887  sub _tree_construction_main ($) { Line 4442  sub _tree_construction_main ($) {
4442          !!!next-token;          !!!next-token;
4443          next B;          next B;
4444        } elsif ({        } elsif ({
4445                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
4446                  div => 1, dl => 1, fieldset => 1,  
4447                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
4448                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
4449                    center => 1, datagrid => 1, details => 1, dialog => 1,
4450                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
4451                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
4452                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
4453                    section => 1, ul => 1,
4454                    ## NOTE: As normal, but drops leading newline
4455                  pre => 1, listing => 1,                  pre => 1, listing => 1,
4456                    ## NOTE: As normal, but interacts with the form element pointer
4457                  form => 1,                  form => 1,
4458                    
4459                  table => 1,                  table => 1,
4460                  hr => 1,                  hr => 1,
4461                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4462    
4463            ## 1. When there is an opening |form| element:
4464          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4465            !!!cp ('t350');            !!!cp ('t350');
4466            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6905  sub _tree_construction_main ($) { Line 4470  sub _tree_construction_main ($) {
4470            next B;            next B;
4471          }          }
4472    
4473          ## has a p element in scope          ## 2. Close the |p| element, if any.
4474          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4475            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4476              !!!cp ('t344');            ## has a p element in scope
4477              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4478              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4479                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4480              next B;                !!!back-token; # <form>
4481            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4482              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4483              last INSCOPE;                next B;
4484                } elsif ($_->[1] & SCOPING_EL) {
4485                  !!!cp ('t345');
4486                  last INSCOPE;
4487                }
4488              } # INSCOPE
4489            }
4490    
4491            ## 3. Close the opening <hn> element, if any.
4492            if ({h1 => 1, h2 => 1, h3 => 1,
4493                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4494              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4495                !!!parse-error (type => 'not closed',
4496                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4497                                token => $token);
4498                pop @{$self->{open_elements}};
4499            }            }
4500          } # INSCOPE          }
4501              
4502            ## 4. Insertion.
4503          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4504          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4505            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6959  sub _tree_construction_main ($) { Line 4540  sub _tree_construction_main ($) {
4540            !!!next-token;            !!!next-token;
4541          }          }
4542          next B;          next B;
4543        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
4544          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
4545    
4546            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4547              ## Interpreted as <li><foo/></li><li/> (non-conforming):
4548              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4549              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
4550              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4551              ## object (Fx)
4552              ## Generate non-tree (non-conforming):
4553              ## basefont (IE7 (where basefont is non-void)), center (IE),
4554              ## form (IE), hn (IE)
4555            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4556              ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4557              ## div (Fx, S)
4558    
4559            my $non_optional;
4560            my $i = -1;
4561    
4562            ## 1.
4563            for my $node (reverse @{$self->{open_elements}}) {
4564              if ($node->[1] == LI_EL) {
4565                ## 2. (a) As if </li>
4566                {
4567                  ## If no </li> - not applied
4568                  #
4569    
4570                  ## Otherwise
4571    
4572                  ## 1. generate implied end tags, except for </li>
4573                  #
4574    
4575                  ## 2. If current node != "li", parse error
4576                  if ($non_optional) {
4577                    !!!parse-error (type => 'not closed',
4578                                    text => $non_optional->[0]->manakai_local_name,
4579                                    token => $token);
4580                    !!!cp ('t355');
4581                  } else {
4582                    !!!cp ('t356');
4583                  }
4584    
4585                  ## 3. Pop
4586                  splice @{$self->{open_elements}}, $i;
4587                }
4588    
4589                last; ## 2. (b) goto 5.
4590              } elsif (
4591                       ## NOTE: not "formatting" and not "phrasing"
4592                       ($node->[1] & SPECIAL_EL or
4593                        $node->[1] & SCOPING_EL) and
4594                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4595                       (not $node->[1] & ADDRESS_DIV_P_EL)
4596                      ) {
4597                ## 3.
4598                !!!cp ('t357');
4599                last; ## goto 5.
4600              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4601                !!!cp ('t358');
4602                #
4603              } else {
4604                !!!cp ('t359');
4605                $non_optional ||= $node;
4606                #
4607              }
4608              ## 4.
4609              ## goto 2.
4610              $i--;
4611            }
4612    
4613            ## 5. (a) has a |p| element in scope
4614          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4615            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4616              !!!cp ('t353');              !!!cp ('t353');
4617    
4618                ## NOTE: |<p><li>|, for example.
4619    
4620              !!!back-token; # <x>              !!!back-token; # <x>
4621              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
4622                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6974  sub _tree_construction_main ($) { Line 4627  sub _tree_construction_main ($) {
4627            }            }
4628          } # INSCOPE          } # INSCOPE
4629    
4630          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## 5. (b) insert
4631            ## Interpreted as <li><foo/></li><li/> (non-conforming)          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4632            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),          !!!nack ('t359.1');
4633            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),          !!!next-token;
4634            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),          next B;
4635            ## object (Fx)        } elsif ($token->{tag_name} eq 'dt' or
4636            ## Generate non-tree (non-conforming)                 $token->{tag_name} eq 'dd') {
4637            ## basefont (IE7 (where basefont is non-void)), center (IE),          ## NOTE: As normal, but imply </dt> or </dd> when ...
4638            ## form (IE), hn (IE)  
4639          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          my $non_optional;
           ## Interpreted as <li><foo><li/></foo></li> (non-conforming)  
           ## div (Fx, S)  
             
         ## Step 1  
4640          my $i = -1;          my $i = -1;
4641          my $node = $self->{open_elements}->[$i];  
4642          my $li_or_dtdd = {li => {li => 1},          ## 1.
4643                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
4644                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] == DTDD_EL) {
4645          LI: {              ## 2. (a) As if </li>
4646            ## Step 2              {
4647            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
4648              if ($i != -1) {                #
4649                !!!cp ('t355');  
4650                !!!parse-error (type => 'not closed',                ## Otherwise
4651                                text => $self->{open_elements}->[-1]->[0]  
4652                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
4653                                token => $token);                #
4654              } else {  
4655                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
4656                  if ($non_optional) {
4657                    !!!parse-error (type => 'not closed',
4658                                    text => $non_optional->[0]->manakai_local_name,
4659                                    token => $token);
4660                    !!!cp ('t355.1');
4661                  } else {
4662                    !!!cp ('t356.1');
4663                  }
4664    
4665                  ## 3. Pop
4666                  splice @{$self->{open_elements}}, $i;
4667              }              }
4668              splice @{$self->{open_elements}}, $i;  
4669              last LI;              last; ## 2. (b) goto 5.
4670              } elsif (
4671                       ## NOTE: not "formatting" and not "phrasing"
4672                       ($node->[1] & SPECIAL_EL or
4673                        $node->[1] & SCOPING_EL) and
4674                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4675    
4676                       (not $node->[1] & ADDRESS_DIV_P_EL)
4677                      ) {
4678                ## 3.
4679                !!!cp ('t357.1');
4680                last; ## goto 5.
4681              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4682                !!!cp ('t358.1');
4683                #
4684            } else {            } else {
4685              !!!cp ('t357');              !!!cp ('t359.1');
4686            }              $non_optional ||= $node;
4687                          #
           ## Step 3  
           if (not ($node->[1] & FORMATTING_EL) and  
               #not $phrasing_category->{$node->[1]} and  
               ($node->[1] & SPECIAL_EL or  
                $node->[1] & SCOPING_EL) and  
               not ($node->[1] & ADDRESS_EL) and  
               not ($node->[1] & DIV_EL)) {  
             !!!cp ('t358');  
             last LI;  
4688            }            }
4689                        ## 4.
4690            !!!cp ('t359');            ## goto 2.
           ## Step 4  
4691            $i--;            $i--;
4692            $node = $self->{open_elements}->[$i];          }
4693            redo LI;  
4694          } # LI          ## 5. (a) has a |p| element in scope
4695                      INSCOPE: for (reverse @{$self->{open_elements}}) {
4696              if ($_->[1] == P_EL) {
4697                !!!cp ('t353.1');
4698                !!!back-token; # <x>
4699                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4700                          line => $token->{line}, column => $token->{column}};
4701                next B;
4702              } elsif ($_->[1] & SCOPING_EL) {
4703                !!!cp ('t354.1');
4704                last INSCOPE;
4705              }
4706            } # INSCOPE
4707    
4708            ## 5. (b) insert
4709          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4710          !!!nack ('t359.1');          !!!nack ('t359.2');
4711          !!!next-token;          !!!next-token;
4712          next B;          next B;
4713        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
4714            ## NOTE: As normal, but effectively ends parsing
4715    
4716          ## has a p element in scope          ## has a p element in scope
4717          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4718            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4719              !!!cp ('t367');              !!!cp ('t367');
4720              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4721              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7058  sub _tree_construction_main ($) { Line 4737  sub _tree_construction_main ($) {
4737        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4738          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4739            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4740            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4741              !!!cp ('t371');              !!!cp ('t371');
4742              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4743                            
# Line 7102  sub _tree_construction_main ($) { Line 4781  sub _tree_construction_main ($) {
4781          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4782          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4783            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4784            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4785              !!!cp ('t376');              !!!cp ('t376');
4786              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4787              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7125  sub _tree_construction_main ($) { Line 4804  sub _tree_construction_main ($) {
4804          ## has a button element in scope          ## has a button element in scope
4805          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4806            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4807            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4808              !!!cp ('t378');              !!!cp ('t378');
4809              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4810              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7190  sub _tree_construction_main ($) { Line 4869  sub _tree_construction_main ($) {
4869                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4870                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4871                           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}},  
4872                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4873                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4874                         );                         );
# Line 7214  sub _tree_construction_main ($) { Line 4891  sub _tree_construction_main ($) {
4891                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4892                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4893                           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}},  
4894                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4895                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4896                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7225  sub _tree_construction_main ($) { Line 4900  sub _tree_construction_main ($) {
4900            next B;            next B;
4901          }          }
4902        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4903          my $tag_name = $token->{tag_name};          ## 1. Insert
4904          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4905                    
4906            ## Step 2 # XXX
4907          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4908    
4909            ## 2. Drop U+000A LINE FEED
4910            $self->{ignore_newline} = 1;
4911    
4912            ## 3. RCDATA
4913          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4914          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4915            
4916          $insert->($el);          ## 4., 6. Insertion mode
4917                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4918          my $text = '';  
4919            ## XXX: 5. frameset-ok flag
4920    
4921          !!!nack ('t392.1');          !!!nack ('t392.1');
4922          !!!next-token;          !!!next-token;
4923          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4924            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4925            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4926              !!!cp ('t392');          ## has an |option| element in scope
4927              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4928            } else {            my $node = $self->{open_elements}->[$_];
4929              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4930                !!!cp ('t397.1');
4931                ## NOTE: As if </option>
4932                !!!back-token; # <option> or <optgroup>
4933                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4934                          line => $token->{line}, column => $token->{column}};
4935                next B;
4936              } elsif ($node->[1] & SCOPING_EL) {
4937                !!!cp ('t397.2');
4938                last INSCOPE;
4939            }            }
4940          } else {          } # INSCOPE
4941            !!!cp ('t394');  
4942          }          $reconstruct_active_formatting_elements->($insert_to_current);
4943          while ($token->{type} == CHARACTER_TOKEN) {  
4944            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4945            $text .= $token->{data};  
4946            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
4947          !!!next-token;          !!!next-token;
4948          next B;          redo B;
4949        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4950                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4951          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4952          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4953            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4954            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4955              !!!cp ('t398.1');              !!!cp ('t398.1');
4956              ## generate implied end tags              ## generate implied end tags
4957              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4958                !!!cp ('t398.2');                !!!cp ('t398.2');
4959                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4960              }              }
4961              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4962                !!!cp ('t398.3');                !!!cp ('t398.3');
4963                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4964                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4965                                    ->manakai_local_name,                                    ->manakai_local_name,
4966                                token => $token);                                token => $token);
4967                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4968                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4969              }              }
4970              last INSCOPE;              last INSCOPE;
4971            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7298  sub _tree_construction_main ($) { Line 4973  sub _tree_construction_main ($) {
4973              last INSCOPE;              last INSCOPE;
4974            }            }
4975          } # INSCOPE          } # INSCOPE
4976              
4977            ## TODO: <non-ruby><rt> is not allowed.
4978    
4979          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4980    
# Line 7318  sub _tree_construction_main ($) { Line 4995  sub _tree_construction_main ($) {
4995                    
4996          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4997            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4998            !!!ack ('t398.1');            !!!ack ('t398.6');
4999          } else {          } else {
5000            !!!cp ('t398.2');            !!!cp ('t398.7');
5001            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
5002            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
5003            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7331  sub _tree_construction_main ($) { Line 5008  sub _tree_construction_main ($) {
5008          next B;          next B;
5009        } elsif ({        } elsif ({
5010                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
5011                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
5012                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
5013                  thead => 1, tr => 1,                  thead => 1, tr => 1,
5014                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7342  sub _tree_construction_main ($) { Line 5019  sub _tree_construction_main ($) {
5019          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
5020          !!!next-token;          !!!next-token;
5021          next B;          next B;
5022                  } elsif ($token->{tag_name} eq 'param' or
5023          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
5024            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5025            pop @{$self->{open_elements}};
5026    
5027            !!!ack ('t398.5');
5028            !!!next-token;
5029            redo B;
5030        } else {        } else {
5031          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
5032            !!!cp ('t384');            !!!cp ('t384');
# Line 7379  sub _tree_construction_main ($) { Line 5062  sub _tree_construction_main ($) {
5062            !!!ack ('t388.2');            !!!ack ('t388.2');
5063          } elsif ({          } elsif ({
5064                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5065                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
5066                    #image => 1,                    keygen => 1,
5067                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5068            !!!cp ('t388.1');            !!!cp ('t388.1');
5069            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7390  sub _tree_construction_main ($) { Line 5073  sub _tree_construction_main ($) {
5073                    
5074            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5075                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5076                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5077              !!!cp ('t400.1');              !!!cp ('t400.1');
5078              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5079            } else {            } else {
# Line 7407  sub _tree_construction_main ($) { Line 5090  sub _tree_construction_main ($) {
5090        }        }
5091      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5092        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5093          ## has a |body| element in scope  
5094            ## 1. If not "have an element in scope":
5095            ## "has a |body| element in scope"
5096          my $i;          my $i;
5097          INSCOPE: {          INSCOPE: {
5098            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5099              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5100                !!!cp ('t405');                !!!cp ('t405');
5101                $i = $_;                $i = $_;
5102                last INSCOPE;                last INSCOPE;
# Line 7421  sub _tree_construction_main ($) { Line 5106  sub _tree_construction_main ($) {
5106              }              }
5107            }            }
5108    
5109            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
5110    
5111              !!!parse-error (type => 'unmatched end tag',
5112                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5113            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
5114            !!!next-token;            !!!next-token;
5115            next B;            next B;
5116          } # INSCOPE          } # INSCOPE
5117    
5118            ## 2. If unclosed elements:
5119          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5120            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5121                      $_->[1] == OPTGROUP_EL ||
5122                      $_->[1] == OPTION_EL ||
5123                      $_->[1] == RUBY_COMPONENT_EL) {
5124              !!!cp ('t403');              !!!cp ('t403');
5125              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5126                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7440  sub _tree_construction_main ($) { Line 5131  sub _tree_construction_main ($) {
5131            }            }
5132          }          }
5133    
5134            ## 3. Switch the insertion mode.
5135          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5136          !!!next-token;          !!!next-token;
5137          next B;          next B;
# Line 7447  sub _tree_construction_main ($) { Line 5139  sub _tree_construction_main ($) {
5139          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5140          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5141          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5142              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5143            ## ISSUE: There is an issue in the spec.            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
5144              !!!cp ('t406');              !!!cp ('t406');
5145              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5146                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7470  sub _tree_construction_main ($) { Line 5161  sub _tree_construction_main ($) {
5161            next B;            next B;
5162          }          }
5163        } elsif ({        } elsif ({
5164                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
5165                  div => 1, dl => 1, fieldset => 1, listing => 1,  
5166                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
5167                    address => 1, article => 1, aside => 1, blockquote => 1,
5168                    center => 1, datagrid => 1, details => 1, dialog => 1,
5169                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
5170                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
5171                    ol => 1, pre => 1, section => 1, ul => 1,
5172    
5173                    ## NOTE: As normal, but ... optional tags
5174                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
5175    
5176                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
5177                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5178            ## NOTE: Code for <li> start tags includes "as if </li>" code.
5179            ## Code for <dt> or <dd> start tags includes "as if </dt> or
5180            ## </dd>" code.
5181    
5182          ## has an element in scope          ## has an element in scope
5183          my $i;          my $i;
5184          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 7502  sub _tree_construction_main ($) { Line 5205  sub _tree_construction_main ($) {
5205                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
5206                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
5207                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
5208                      option => 1,
5209                      optgroup => 1,
5210                    p => 1,                    p => 1,
5211                    rt => 1,                    rt => 1,
5212                    rp => 1,                    rp => 1,
# Line 7534  sub _tree_construction_main ($) { Line 5239  sub _tree_construction_main ($) {
5239          !!!next-token;          !!!next-token;
5240          next B;          next B;
5241        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
5242            ## NOTE: As normal, but interacts with the form element pointer
5243    
5244          undef $self->{form_element};          undef $self->{form_element};
5245    
5246          ## has an element in scope          ## has an element in scope
5247          my $i;          my $i;
5248          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5249            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5250            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5251              !!!cp ('t418');              !!!cp ('t418');
5252              $i = $_;              $i = $_;
5253              last INSCOPE;              last INSCOPE;
# Line 7581  sub _tree_construction_main ($) { Line 5288  sub _tree_construction_main ($) {
5288          !!!next-token;          !!!next-token;
5289          next B;          next B;
5290        } elsif ({        } elsif ({
5291                    ## NOTE: As normal, except acts as a closer for any ...
5292                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5293                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5294          ## has an element in scope          ## has an element in scope
5295          my $i;          my $i;
5296          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5297            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5298            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5299              !!!cp ('t423');              !!!cp ('t423');
5300              $i = $_;              $i = $_;
5301              last INSCOPE;              last INSCOPE;
# Line 7626  sub _tree_construction_main ($) { Line 5334  sub _tree_construction_main ($) {
5334          !!!next-token;          !!!next-token;
5335          next B;          next B;
5336        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
5337            ## NOTE: As normal, except </p> implies <p> and ...
5338    
5339          ## has an element in scope          ## has an element in scope
5340            my $non_optional;
5341          my $i;          my $i;
5342          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5343            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5344            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5345              !!!cp ('t410.1');              !!!cp ('t410.1');
5346              $i = $_;              $i = $_;
5347              last INSCOPE;              last INSCOPE;
5348            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
5349              !!!cp ('t411.1');              !!!cp ('t411.1');
5350              last INSCOPE;              last INSCOPE;
5351              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
5352                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
5353                !!!cp ('t411.2');
5354                #
5355              } else {
5356                !!!cp ('t411.3');
5357                $non_optional ||= $node;
5358                #
5359            }            }
5360          } # INSCOPE          } # INSCOPE
5361    
5362          if (defined $i) {          if (defined $i) {
5363            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
5364                    ne $token->{tag_name}) {            #
5365    
5366              ## 2. If current node != "p", parse error
5367              if ($non_optional) {
5368              !!!cp ('t412.1');              !!!cp ('t412.1');
5369              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5370                              text => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
5371                              token => $token);                              token => $token);
5372            } else {            } else {
5373              !!!cp ('t414.1');              !!!cp ('t414.1');
5374            }            }
5375    
5376              ## 3. Pop
5377            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5378          } else {          } else {
5379            !!!cp ('t413.1');            !!!cp ('t413.1');
# Line 7692  sub _tree_construction_main ($) { Line 5414  sub _tree_construction_main ($) {
5414          ## Ignore the token.          ## Ignore the token.
5415          !!!next-token;          !!!next-token;
5416          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
5417        } else {        } else {
5418            if ($token->{tag_name} eq 'sarcasm') {
5419              sleep 0.001; # take a deep breath
5420            }
5421    
5422          ## Step 1          ## Step 1
5423          my $node_i = -1;          my $node_i = -1;
5424          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
5425    
5426          ## Step 2          ## Step 2
5427          S2: {          S2: {
5428            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5429              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5430              if ($node_tag_name eq $token->{tag_name}) {
5431              ## Step 1              ## Step 1
5432              ## generate implied end tags              ## generate implied end tags
5433              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7733  sub _tree_construction_main ($) { Line 5440  sub _tree_construction_main ($) {
5440              }              }
5441                    
5442              ## Step 2              ## Step 2
5443              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5444                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5445                $current_tag_name =~ tr/A-Z/a-z/;
5446                if ($current_tag_name ne $token->{tag_name}) {
5447                !!!cp ('t431');                !!!cp ('t431');
5448                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5449                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7809  sub _tree_construction_main ($) { Line 5518  sub _tree_construction_main ($) {
5518    ## TODO: script stuffs    ## TODO: script stuffs
5519  } # _tree_construct_main  } # _tree_construct_main
5520    
5521    ## XXX: How this method is organized is somewhat out of date, although
5522    ## it still does what the current spec documents.
5523  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5524    my $class = shift;    my $class = shift;
5525    my $node = shift;    my $node = shift; # /context/
5526    #my $s = \$_[0];    #my $s = \$_[0];
5527    my $onerror = $_[1];    my $onerror = $_[1];
5528    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
# Line 7819  sub set_inner_html ($$$$;$) { Line 5530  sub set_inner_html ($$$$;$) {
5530    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
5531    
5532    my $nt = $node->node_type;    my $nt = $node->node_type;
5533    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5534      # MUST      # MUST
5535            
5536      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7834  sub set_inner_html ($$$$;$) { Line 5545  sub set_inner_html ($$$$;$) {
5545    
5546      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5547      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5548    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5549      ## TODO: If non-html element      ## TODO: If non-html element
5550    
5551      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5552    
5553  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5554    
5555      ## Step 1 # MUST      ## F1. Create an HTML document.
5556      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5557      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5558      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5559    
5560        ## F2. Propagate quirkness flag
5561        my $node_doc = $node->owner_document;
5562        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5563    
5564        ## F3. Create an HTML parser
5565      my $p = $class->new;      my $p = $class->new;
5566      $p->{document} = $doc;      $p->{document} = $doc;
5567    
# Line 7972  sub set_inner_html ($$$$;$) { Line 5689  sub set_inner_html ($$$$;$) {
5689      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5690      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5691    
5692      ## Step 2      ## F4. If /context/ is not undef...
5693    
5694        ## F4.1. content model flag
5695      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5696      $p->{content_model} = {      $p->{content_model} = {
5697        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 7988  sub set_inner_html ($$$$;$) { Line 5707  sub set_inner_html ($$$$;$) {
5707      }->{$node_ln};      }->{$node_ln};
5708      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5709          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5710    
5711      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5712        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5713    
5714      ## Step 3      ## F4.2. Root |html| element
5715      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5716        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5717    
5718      ## Step 4 # MUST      ## F4.3.
5719      $doc->append_child ($root);      $doc->append_child ($root);
5720    
5721      ## Step 5 # MUST      ## F4.4.
5722      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5723    
5724      undef $p->{head_element};      undef $p->{head_element};
5725        undef $p->{head_element_inserted};
5726    
5727      ## Step 6 # MUST      ## F4.5.
5728      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5729    
5730      ## Step 7 # MUST      ## F4.6.
5731      my $anode = $node;      my $anode = $node;
5732      AN: while (defined $anode) {      AN: while (defined $anode) {
5733        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8023  sub set_inner_html ($$$$;$) { Line 5742  sub set_inner_html ($$$$;$) {
5742        }        }
5743        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5744      } # AN      } # AN
5745        
5746      ## Step 9 # MUST      ## F.6. Start the parser.
5747      {      {
5748        my $self = $p;        my $self = $p;
5749        !!!next-token;        !!!next-token;
5750      }      }
5751      $p->_tree_construction_main;      $p->_tree_construction_main;
5752    
5753      ## Step 10 # MUST      ## F.7.
5754      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5755      for (@cn) {      for (@cn) {
5756        $node->remove_child ($_);        $node->remove_child ($_);

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24