/[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.201 by wakaba, Sat Oct 4 12:20:35 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
36  sub ADDRESS_EL () { 0b10 }  
37  sub BODY_EL () { 0b100 }  ## Bits 12-15
38  sub BUTTON_EL () { 0b1000 }  sub SPECIAL_EL () { 0b1_000000000000000 }
39  sub CAPTION_EL () { 0b10000 }  sub SCOPING_EL () { 0b1_00000000000000 }
40  sub DD_EL () { 0b100000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub DIV_EL () { 0b1000000 }  sub PHRASING_EL () { 0b1_000000000000 }
42  sub DT_EL () { 0b10000000 }  
43  sub FORM_EL () { 0b100000000 }  ## Bits 10-11
44  sub FORMATTING_EL () { 0b1000000000 }  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45  sub FRAMESET_EL () { 0b10000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46  sub HEADING_EL () { 0b100000000000 }  
47  sub HTML_EL () { 0b1000000000000 }  ## Bits 6-9
48  sub LI_EL () { 0b10000000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
49  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50  sub OPTION_EL () { 0b1000000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51  sub OPTGROUP_EL () { 0b10000000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
52  sub P_EL () { 0b100000000000000000 }  
53  sub SELECT_EL () { 0b1000000000000000000 }  ## Bit 5
54  sub TABLE_EL () { 0b10000000000000000000 }  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55  sub TABLE_CELL_EL () { 0b100000000000000000000 }  
56  sub TABLE_ROW_EL () { 0b1000000000000000000000 }  ## NOTE: Used in </body> and EOF algorithms.
57  sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  ## Bit 4
58  sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
61  ## NOTE: There is a code where a modified version of  ## NOTE: There is a code where a modified version of
62  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63  ## implementation (search for the algorithm name).  ## implementation (search for the algorithm name).
64  sub END_TAG_OPTIONAL_EL () {  ## Bit 3
65    DD_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
   DT_EL |  
   LI_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
66    
67  ## NOTE: Used in </body> and EOF algorithms.  ## Bits 0-2
 sub ALL_END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   ## ISSUE: option, optgroup, rt, rp?  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
68    
69  sub SCOPING_EL () {  sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    BUTTON_EL |  sub FORM_EL () { SPECIAL_EL | 0b001 }
71    CAPTION_EL |  sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    HTML_EL |  sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    TABLE_EL |  sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    TABLE_CELL_EL |  sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    MISC_SCOPING_EL  
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
   FORM_EL |  
   FRAMESET_EL |  
   HEADING_EL |  
   SELECT_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
124  }  }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 160  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
   eventsource => MISC_SPECIAL_EL,  
182    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
183    figure => MISC_SPECIAL_EL,    figure => MISC_SPECIAL_EL,
184    font => FORMATTING_EL,    font => FORMATTING_EL,
# Line 193  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 200  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 242  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 | MISC_SCOPING_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 338  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 559  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 580  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 834  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 914  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 944  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};  
         $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 3818  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 3847  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 3979  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;  
     }  
1410    
1411      ## Step 5      !!!nack ('t40.1');
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
   
     ## Step 7  
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
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 4167  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 4253  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 4313  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 4331  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 4356  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 4403  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 4417  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 4506  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 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}};            !!!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              next B;
2227          } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
2228            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2229            if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
# Line 4669  sub _tree_construction_main ($) { Line 2232  sub _tree_construction_main ($) {
2232                              text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2233              push @{$self->{open_elements}},              push @{$self->{open_elements}},
2234                  [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2235                $self->{head_element_inserted} = 1;
2236            } else {            } else {
2237              !!!cp ('t103');              !!!cp ('t103');
2238            }            }
# Line 4679  sub _tree_construction_main ($) { Line 2243  sub _tree_construction_main ($) {
2243            !!!ack ('t103.1');            !!!ack ('t103.1');
2244            !!!next-token;            !!!next-token;
2245            next B;            next B;
2246          } elsif ($token->{tag_name} eq 'command' or          } elsif ($token->{tag_name} eq 'command') {
                  $token->{tag_name} eq 'eventsource') {  
2247            if ($self->{insertion_mode} == IN_HEAD_IM) {            if ($self->{insertion_mode} == IN_HEAD_IM) {
2248              ## NOTE: If the insertion mode at the time of the emission              ## NOTE: If the insertion mode at the time of the emission
2249              ## of the token was "before head", $self->{insertion_mode}              ## of the token was "before head", $self->{insertion_mode}
# Line 4701  sub _tree_construction_main ($) { Line 2264  sub _tree_construction_main ($) {
2264              !!!cp ('t103.3');              !!!cp ('t103.3');
2265              #              #
2266            }            }
2267              } elsif ($token->{tag_name} eq 'meta') {          } elsif ($token->{tag_name} eq 'meta') {
2268                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2269                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2270                  !!!cp ('t104');              !!!cp ('t104');
2271                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2272                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2273                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2274                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2275                } else {              $self->{head_element_inserted} = 1;
2276                  !!!cp ('t105');            } else {
2277                }              !!!cp ('t105');
2278                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2279                my $meta_el = pop @{$self->{open_elements}};            !!!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 4771  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 4837  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 4998  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 5024  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 5112  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 5185  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 5213  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 5223  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 5249  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 5278  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 5328  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 5340  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 5371  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 5389  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 5404  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 5421  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 5441  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 5450  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 5461  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 5474  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 5506  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 5539  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 5621  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 5635  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 5658  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 5718  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 5765  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 5792  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 5820  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 5847  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 5871  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 5881  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 5896  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 5927  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 5968  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 6007  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 6059  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 6089  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 6118  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 6208  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 6223  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 6250  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 6276  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 6294  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 6319  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 6332  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 6340  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 6353  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 6381  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 6404  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 6415  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 6433  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 6450  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 6477  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 6512  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 6551  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 6738  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 6752  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 6784  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 6814  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, command => 1, eventsource => 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
# Line 6887  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 6922  sub _tree_construction_main ($) { Line 4459  sub _tree_construction_main ($) {
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 6931  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 6988  sub _tree_construction_main ($) { Line 4543  sub _tree_construction_main ($) {
4543        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4544          ## NOTE: As normal, but imply </li> when there's another <li> ...          ## NOTE: As normal, but imply </li> when there's another <li> ...
4545    
4546          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4547            ## Interpreted as <li><foo/></li><li/> (non-conforming)            ## Interpreted as <li><foo/></li><li/> (non-conforming):
4548            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),            ## 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),            ## 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),            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4551            ## object (Fx)            ## object (Fx)
4552            ## Generate non-tree (non-conforming)            ## Generate non-tree (non-conforming):
4553            ## basefont (IE7 (where basefont is non-void)), center (IE),            ## basefont (IE7 (where basefont is non-void)), center (IE),
4554            ## form (IE), hn (IE)            ## form (IE), hn (IE)
4555          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4556            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)            ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4557            ## div (Fx, S)            ## div (Fx, S)
4558    
4559          my $non_optional;          my $non_optional;
# Line 7006  sub _tree_construction_main ($) { Line 4561  sub _tree_construction_main ($) {
4561    
4562          ## 1.          ## 1.
4563          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4564            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4565              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4566              {              {
4567                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7037  sub _tree_construction_main ($) { Line 4592  sub _tree_construction_main ($) {
4592                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4593                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4594                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4595                       (not $node->[1] & ADDRESS_DIV_P_EL)
4596                     (not $node->[1] & ADDRESS_EL) &                    ) {
                    (not $node->[1] & DIV_EL) &  
                    (not $node->[1] & P_EL)) {  
4597              ## 3.              ## 3.
4598              !!!cp ('t357');              !!!cp ('t357');
4599              last; ## goto 5.              last; ## goto 5.
# Line 7059  sub _tree_construction_main ($) { Line 4612  sub _tree_construction_main ($) {
4612    
4613          ## 5. (a) has a |p| element in scope          ## 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.              ## NOTE: |<p><li>|, for example.
# Line 7088  sub _tree_construction_main ($) { Line 4641  sub _tree_construction_main ($) {
4641    
4642          ## 1.          ## 1.
4643          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4644            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4645              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4646              {              {
4647                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7120  sub _tree_construction_main ($) { Line 4673  sub _tree_construction_main ($) {
4673                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4674                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4675    
4676                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4677                     (not $node->[1] & DIV_EL) &                    ) {
                    (not $node->[1] & P_EL)) {  
4678              ## 3.              ## 3.
4679              !!!cp ('t357.1');              !!!cp ('t357.1');
4680              last; ## goto 5.              last; ## goto 5.
# Line 7141  sub _tree_construction_main ($) { Line 4693  sub _tree_construction_main ($) {
4693    
4694          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4695          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4696            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4697              !!!cp ('t353.1');              !!!cp ('t353.1');
4698              !!!back-token; # <x>              !!!back-token; # <x>
4699              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7163  sub _tree_construction_main ($) { Line 4715  sub _tree_construction_main ($) {
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 7185  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 7229  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 7252  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 7317  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 7341  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 7352  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;
         if ($token->{type} == CHARACTER_TOKEN) {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!cp ('t392');  
             !!!next-token;  
           } else {  
             !!!cp ('t393');  
           }  
         } else {  
           !!!cp ('t394');  
         }  
         while ($token->{type} == CHARACTER_TOKEN) {  
           !!!cp ('t395');  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
         !!!next-token;  
4923          next B;          next B;
4924        } elsif ($token->{tag_name} eq 'optgroup' or        } elsif ($token->{tag_name} eq 'optgroup' or
4925                 $token->{tag_name} eq 'option') {                 $token->{tag_name} eq 'option') {
4926          ## has an |option| element in scope          ## has an |option| element in scope
4927          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4928            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4929            if ($node->[1] & OPTION_EL) {            if ($node->[1] == OPTION_EL) {
4930              !!!cp ('t397.1');              !!!cp ('t397.1');
4931              ## NOTE: As if </option>              ## NOTE: As if </option>
4932              !!!back-token; # <option> or <optgroup>              !!!back-token; # <option> or <optgroup>
# Line 7428  sub _tree_construction_main ($) { Line 4951  sub _tree_construction_main ($) {
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 7450  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 7538  sub _tree_construction_main ($) { Line 5063  sub _tree_construction_main ($) {
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, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
5066                      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 7547  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 7564  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 7587  sub _tree_construction_main ($) { Line 5115  sub _tree_construction_main ($) {
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 7599  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 7606  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            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 7714  sub _tree_construction_main ($) { Line 5247  sub _tree_construction_main ($) {
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 7762  sub _tree_construction_main ($) { Line 5295  sub _tree_construction_main ($) {
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 7808  sub _tree_construction_main ($) { Line 5341  sub _tree_construction_main ($) {
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;
# Line 7985  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 7995  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 8010  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 8148  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 8164  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 8199  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.201  
changed lines
  Added in v.1.232

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24