/[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.198 by wakaba, Sat Oct 4 08:58:02 2008 UTC revision 1.220 by wakaba, Sun Jul 26 09:11:18 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
36  sub ADDRESS_EL () { 0b10 }  
37  sub BODY_EL () { 0b100 }  ## Bits 12-15
38  sub BUTTON_EL () { 0b1000 }  sub SPECIAL_EL () { 0b1_000000000000000 }
39  sub CAPTION_EL () { 0b10000 }  sub SCOPING_EL () { 0b1_00000000000000 }
40  sub DD_EL () { 0b100000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub DIV_EL () { 0b1000000 }  sub PHRASING_EL () { 0b1_000000000000 }
42  sub DT_EL () { 0b10000000 }  
43  sub FORM_EL () { 0b100000000 }  ## Bits 10-11
44  sub FORMATTING_EL () { 0b1000000000 }  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45  sub FRAMESET_EL () { 0b10000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46  sub HEADING_EL () { 0b100000000000 }  
47  sub HTML_EL () { 0b1000000000000 }  ## Bits 6-9
48  sub LI_EL () { 0b10000000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
49  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50  sub OPTION_EL () { 0b1000000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51  sub OPTGROUP_EL () { 0b10000000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
52  sub P_EL () { 0b100000000000000000 }  
53  sub SELECT_EL () { 0b1000000000000000000 }  ## Bit 5
54  sub TABLE_EL () { 0b10000000000000000000 }  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55  sub TABLE_CELL_EL () { 0b100000000000000000000 }  
56  sub TABLE_ROW_EL () { 0b1000000000000000000000 }  ## NOTE: Used in </body> and EOF algorithms.
57  sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  ## Bit 4
58  sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
61  ## NOTE: There is a code where a modified version of  ## NOTE: There is a code where a modified version of
62  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63  ## implementation (search for the algorithm name).  ## implementation (search for the algorithm name).
64  sub END_TAG_OPTIONAL_EL () {  ## Bit 3
65    DD_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
   DT_EL |  
   LI_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
66    
67  ## NOTE: Used in </body> and EOF algorithms.  ## Bits 0-2
 sub ALL_END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
68    
69  sub SCOPING_EL () {  sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    BUTTON_EL |  sub FORM_EL () { SPECIAL_EL | 0b001 }
71    CAPTION_EL |  sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    HTML_EL |  sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    TABLE_EL |  sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    TABLE_CELL_EL |  sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    MISC_SCOPING_EL  
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    FORM_EL |  }
125    FRAMESET_EL |  sub TABLE_EL () {
126    HEADING_EL |    SCOPING_EL |
127    OPTION_EL |    TABLE_ROWS_EL |
128    OPTGROUP_EL |    TABLE_SCOPING_EL |
129    SELECT_EL |    0b001
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
130  }  }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 160  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
182    eventsource => MISC_SPECIAL_EL,    eventsource => MISC_SPECIAL_EL,
# Line 200  my $el_category = { Line 210  my $el_category = {
210    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
211    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
212    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
213    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
214    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
215    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
216    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 242  my $el_category = { Line 252  my $el_category = {
252  my $el_category_f = {  my $el_category_f = {
253    $MML_NS => {    $MML_NS => {
254      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
255      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
256      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260    },    },
261    $SVG_NS => {    $SVG_NS => {
262      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265    },    },
266    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
267  };  };
# Line 338  my $foreign_attr_xname = { Line 348  my $foreign_attr_xname = {
348    
349  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
350    
 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;  
   
351  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
352  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
353    
# Line 559  sub parse_byte_stream ($$$$;$$) { Line 525  sub parse_byte_stream ($$$$;$$) {
525            
526      if ($char_stream) { # if supported      if ($char_stream) { # if supported
527        ## "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;  
528                
529        ## Step 2        ## Step 1
530        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
531            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
532          !!!parse-error (type => 'charset label:matching',          !!!parse-error (type => 'charset label:matching',
# Line 580  sub parse_byte_stream ($$$$;$$) { Line 536  sub parse_byte_stream ($$$$;$$) {
536          return;          return;
537        }        }
538    
539          ## Step 2 (HTML5 revision 3205)
540          if (defined $self->{input_encoding} and
541              Message::Charset::Info->get_by_html_name ($self->{input_encoding})
542              ->{category} & Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
543            $self->{confident} = 1;
544            return;
545          }
546    
547          ## Step 3
548          if ($charset->{category} &
549              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
550            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
551            ($char_stream, $e_status) = $charset->get_decode_handle
552                ($byte_stream,
553                 byte_buffer => \ $buffer->{buffer});
554          }
555          $charset_name = $charset->get_iana_name;
556    
557        !!!parse-error (type => 'charset label detected',        !!!parse-error (type => 'charset label detected',
558                        text => $self->{input_encoding},                        text => $self->{input_encoding},
559                        value => $charset_name,                        value => $charset_name,
560                        level => $self->{level}->{warn},                        level => $self->{level}->{warn},
561                        token => $token);                        token => $token);
562                
563        ## Step 3        ## Step 4
564        # if (can) {        # if (can) {
565          ## change the encoding on the fly.          ## change the encoding on the fly.
566          #$self->{confident} = 1;          #$self->{confident} = 1;
567          #return;          #return;
568        # }        # }
569                
570        ## Step 4        ## Step 5
571        throw Whatpm::HTML::RestartParser ();        throw Whatpm::HTML::RestartParser ();
572      }      }
573    }; # $self->{change_encoding}    }; # $self->{change_encoding}
# Line 834  sub new ($) { Line 808  sub new ($) {
808    return $self;    return $self;
809  } # new  } # new
810    
811  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 }  
812    
813  sub AFTER_HTML_IMS () { 0b100 }  sub AFTER_HTML_IMS () { 0b100 }
814  sub HEAD_IMS ()       { 0b1000 }  sub HEAD_IMS ()       { 0b1000 }
# Line 914  sub ROW_IMS ()        { 0b10000000 } Line 819  sub ROW_IMS ()        { 0b10000000 }
819  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
820  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
821  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
822  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
823      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
824      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
825      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
826    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
827        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
828        ## combined with the original insertion mode.  In thie parser,
829        ## they are stored together in the bit-or'ed form.
830    
831    sub IM_MASK () { 0b11111111111 }
832    
833  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
834    
# Line 944  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 855  sub IN_SELECT_IM () { SELECT_IMS | 0b01
855  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
856  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
857    
 ## 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  
   
858  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
859    my $self = shift;    my $self = shift;
860    ## 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 883  sub _construct_tree ($) {
883    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
884    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
885    ## 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  
886        
887    !!!next-token;    !!!next-token;
888    
889    undef $self->{form_element};    undef $self->{form_element};
890    undef $self->{head_element};    undef $self->{head_element};
891      undef $self->{head_element_inserted};
892    $self->{open_elements} = [];    $self->{open_elements} = [];
893    undef $self->{inner_html_node};    undef $self->{inner_html_node};
894      undef $self->{ignore_newline};
895    
896    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
897    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3530  sub _tree_construction_initial ($) { Line 944  sub _tree_construction_initial ($) {
944        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
945        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
946        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
947        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
948          ## string, while |null| is an allowed value for the attribute
949          ## according to DOM3 Core.
950        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
951                
952        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
# Line 3818  sub _reset_insertion_mode ($) { Line 1234  sub _reset_insertion_mode ($) {
1234          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1235          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1236          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1237        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1238          if ($last) {          if ($last) {
1239            !!!cp ('t28.2');            !!!cp ('t28.2');
1240            #            #
# Line 3847  sub _reset_insertion_mode ($) { Line 1263  sub _reset_insertion_mode ($) {
1263        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1264                
1265        ## Step 15        ## Step 15
1266        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1267          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1268            !!!cp ('t29');            !!!cp ('t29');
1269            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3979  sub _tree_construction_main ($) { Line 1395  sub _tree_construction_main ($) {
1395    
1396      ## Step 1      ## Step 1
1397      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1398      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1399    
1400      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1401      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1402      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1403    
1404      ## Step 4      ## Step 3, 4
1405      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!nack ('t40.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
1406    
1407      ## Step 6      !!!nack ('t40.1');
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
   
     ## Step 7  
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
1408      !!!next-token;      !!!next-token;
1409    }; # $parse_rcdata    }; # $parse_rcdata
1410    
1411    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1412        ## Step 1
1413      my $script_el;      my $script_el;
1414      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1415    
1416        ## Step 2
1417      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1418    
1419        ## Step 3
1420        ## TODO: Mark as "already executed", if ...
1421    
1422        ## Step 4
1423        $insert->($script_el);
1424    
1425        ## ISSUE: $script_el is not put into the stack
1426        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1427    
1428        ## Step 5
1429      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1430      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;  
1431    
1432      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1433          $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  
1434    
1435        $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...  
     }  
       
1436      !!!next-token;      !!!next-token;
1437    }; # $script_start_tag    }; # $script_start_tag
1438    
1439    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1440    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
1441      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1442    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1443    
1444    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4167  sub _tree_construction_main ($) { Line 1523  sub _tree_construction_main ($) {
1523            !!!cp ('t59');            !!!cp ('t59');
1524            $furthest_block = $node;            $furthest_block = $node;
1525            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1526              ## NOTE: The topmost (eldest) node.
1527          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1528            !!!cp ('t60');            !!!cp ('t60');
1529            last OE;            last OE;
# Line 4253  sub _tree_construction_main ($) { Line 1610  sub _tree_construction_main ($) {
1610          my $foster_parent_element;          my $foster_parent_element;
1611          my $next_sibling;          my $next_sibling;
1612          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1613            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1614                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1615                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1616                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 4313  sub _tree_construction_main ($) { Line 1670  sub _tree_construction_main ($) {
1670            $i = $_;            $i = $_;
1671          }          }
1672        } # OE        } # OE
1673        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1674                
1675        ## Step 14        ## Step 14
1676        redo FET;        redo FET;
# Line 4331  sub _tree_construction_main ($) { Line 1688  sub _tree_construction_main ($) {
1688        my $foster_parent_element;        my $foster_parent_element;
1689        my $next_sibling;        my $next_sibling;
1690        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1691          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1692                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1693                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1694                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 4356  sub _tree_construction_main ($) { Line 1713  sub _tree_construction_main ($) {
1713      }      }
1714    }; # $insert_to_foster    }; # $insert_to_foster
1715    
1716      ## NOTE: Insert a character (MUST): When a character is inserted, if
1717      ## the last node that was inserted by the parser is a Text node and
1718      ## the character has to be inserted after that node, then the
1719      ## character is appended to the Text node.  However, if any other
1720      ## node is inserted by the parser, then a new Text node is created
1721      ## and the character is appended as that Text node.  If I'm not
1722      ## wrong, for a parser with scripting disabled, there are only two
1723      ## cases where this occurs.  One is the case where an element node
1724      ## is inserted to the |head| element.  This is covered by using the
1725      ## |$self->{head_element_inserted}| flag.  Another is the case where
1726      ## an element or comment is inserted into the |table| subtree while
1727      ## foster parenting happens.  This is covered by using the [2] flag
1728      ## of the |$open_tables| structure.  All other cases are handled
1729      ## simply by calling |manakai_append_text| method.
1730    
1731      ## TODO: |<body><script>document.write("a<br>");
1732      ## document.body.removeChild (document.body.lastChild);
1733      ## document.write ("b")</script>|
1734    
1735    B: while (1) {    B: while (1) {
1736      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1737        !!!cp ('t73');        !!!cp ('t73');
# Line 4403  sub _tree_construction_main ($) { Line 1779  sub _tree_construction_main ($) {
1779        } else {        } else {
1780          !!!cp ('t87');          !!!cp ('t87');
1781          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1782            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1783        }        }
1784        !!!next-token;        !!!next-token;
1785        next B;        next B;
1786        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1787          if ($token->{type} == CHARACTER_TOKEN) {
1788            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1789            delete $self->{ignore_newline};
1790    
1791            if (length $token->{data}) {
1792              !!!cp ('t43');
1793              $self->{open_elements}->[-1]->[0]->manakai_append_text
1794                  ($token->{data});
1795            } else {
1796              !!!cp ('t43.1');
1797            }
1798            !!!next-token;
1799            next B;
1800          } elsif ($token->{type} == END_TAG_TOKEN) {
1801            delete $self->{ignore_newline};
1802    
1803            if ($token->{tag_name} eq 'script') {
1804              !!!cp ('t50');
1805              
1806              ## Para 1-2
1807              my $script = pop @{$self->{open_elements}};
1808              
1809              ## Para 3
1810              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1811    
1812              ## Para 4
1813              ## TODO: $old_insertion_point = $current_insertion_point;
1814              ## TODO: $current_insertion_point = just before $self->{nc};
1815    
1816              ## Para 5
1817              ## TODO: Run the $script->[0].
1818    
1819              ## Para 6
1820              ## TODO: $current_insertion_point = $old_insertion_point;
1821    
1822              ## Para 7
1823              ## TODO: if ($pending_external_script) {
1824                ## TODO: ...
1825              ## TODO: }
1826    
1827              !!!next-token;
1828              next B;
1829            } else {
1830              !!!cp ('t42');
1831    
1832              pop @{$self->{open_elements}};
1833    
1834              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1835              !!!next-token;
1836              next B;
1837            }
1838          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1839            delete $self->{ignore_newline};
1840    
1841            !!!cp ('t44');
1842            !!!parse-error (type => 'not closed',
1843                            text => $self->{open_elements}->[-1]->[0]
1844                                ->manakai_local_name,
1845                            token => $token);
1846    
1847            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1848            #  ## TODO: Mark as "already executed"
1849            #}
1850    
1851            pop @{$self->{open_elements}};
1852    
1853            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1854            ## Reprocess.
1855            next B;
1856          } else {
1857            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1858          }
1859      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1860        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1861          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4417  sub _tree_construction_main ($) { Line 1867  sub _tree_construction_main ($) {
1867               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1868              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1869              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1870               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1871            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1872            !!!cp ('t87.2');            !!!cp ('t87.2');
1873            #            #
# Line 4506  sub _tree_construction_main ($) { Line 1956  sub _tree_construction_main ($) {
1956          }          }
1957        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
1958          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
1959          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
1960          #            !!!cp ('t87.41');
1961              #
1962              ## XXXscript: Execute script here.
1963            } else {
1964              !!!cp ('t87.5');
1965              #
1966            }
1967        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1968          !!!cp ('t87.6');          !!!cp ('t87.6');
1969          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4518  sub _tree_construction_main ($) { Line 1974  sub _tree_construction_main ($) {
1974          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
1975              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
1976    
1977            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
1978    
1979          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
1980          ## Reprocess.          ## Reprocess.
1981          next B;          next B;
# Line 4530  sub _tree_construction_main ($) { Line 1988  sub _tree_construction_main ($) {
1988        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1989          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
1990            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
1991              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
1992              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
1993              #                $self->{open_elements}->[-1]->[0]->append_child
1994                    ($self->{document}->create_text_node ($1));
1995                  delete $self->{head_element_inserted};
1996                  ## NOTE: |</head> <link> |
1997                  #
1998                } else {
1999                  !!!cp ('t88.2');
2000                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2001                  ## NOTE: |</head> &#x20;|
2002                  #
2003                }
2004            } else {            } else {
2005              !!!cp ('t88.1');              !!!cp ('t88.1');
2006              ## Ignore the token.              ## Ignore the token.
# Line 4628  sub _tree_construction_main ($) { Line 2096  sub _tree_construction_main ($) {
2096            !!!cp ('t97');            !!!cp ('t97');
2097          }          }
2098    
2099              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2100                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2101                  !!!cp ('t98');              !!!cp ('t98');
2102                  ## As if </noscript>              ## As if </noscript>
2103                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2104                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2105                                  token => $token);                              token => $token);
2106                            
2107                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2108                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2109                } else {            } else {
2110                  !!!cp ('t99');              !!!cp ('t99');
2111                }            }
2112    
2113                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2114                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2115                  !!!cp ('t100');              !!!cp ('t100');
2116                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2117                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2118                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2119                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2120                } else {              $self->{head_element_inserted} = 1;
2121                  !!!cp ('t101');            } else {
2122                }              !!!cp ('t101');
2123                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2124                pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2125                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2126                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2127                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2128                !!!next-token;            !!!nack ('t101.1');
2129                next B;            !!!next-token;
2130              next B;
2131          } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
2132            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2133            if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
# Line 4667  sub _tree_construction_main ($) { Line 2136  sub _tree_construction_main ($) {
2136                              text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2137              push @{$self->{open_elements}},              push @{$self->{open_elements}},
2138                  [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2139                $self->{head_element_inserted} = 1;
2140            } else {            } else {
2141              !!!cp ('t103');              !!!cp ('t103');
2142            }            }
# Line 4699  sub _tree_construction_main ($) { Line 2169  sub _tree_construction_main ($) {
2169              !!!cp ('t103.3');              !!!cp ('t103.3');
2170              #              #
2171            }            }
2172              } elsif ($token->{tag_name} eq 'meta') {          } elsif ($token->{tag_name} eq 'meta') {
2173                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2174                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2175                  !!!cp ('t104');              !!!cp ('t104');
2176                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2177                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2178                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2179                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2180                } else {              $self->{head_element_inserted} = 1;
2181                  !!!cp ('t105');            } else {
2182                }              !!!cp ('t105');
2183                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2184                my $meta_el = pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2185              my $meta_el = pop @{$self->{open_elements}};
2186    
2187                unless ($self->{confident}) {                unless ($self->{confident}) {
2188                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4769  sub _tree_construction_main ($) { Line 2240  sub _tree_construction_main ($) {
2240                !!!ack ('t110.1');                !!!ack ('t110.1');
2241                !!!next-token;                !!!next-token;
2242                next B;                next B;
2243              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2244                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2245                  !!!cp ('t111');              !!!cp ('t111');
2246                  ## As if </noscript>              ## As if </noscript>
2247                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2248                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2249                                  token => $token);                              token => $token);
2250                            
2251                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2252                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2253                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2254                  !!!cp ('t112');              !!!cp ('t112');
2255                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2256                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2257                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2258                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2259                } else {              $self->{head_element_inserted} = 1;
2260                  !!!cp ('t113');            } else {
2261                }              !!!cp ('t113');
2262              }
2263    
2264                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2265                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2266                    : $self->{open_elements}->[-1]->[0];            ## ISSUE: A spec bug [Bug 6038]
2267                $parse_rcdata->(RCDATA_CONTENT_MODEL);            splice @{$self->{open_elements}}, -2, 1, () # <head>
2268                pop @{$self->{open_elements}} # <head>                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2269                    if $self->{insertion_mode} == AFTER_HEAD_IM;            next B;
2270                next B;          } elsif ($token->{tag_name} eq 'style' or
2271              } elsif ($token->{tag_name} eq 'style' or                   $token->{tag_name} eq 'noframes') {
2272                       $token->{tag_name} eq 'noframes') {            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2273                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## insertion mode IN_HEAD_IM)
2274                ## insertion mode IN_HEAD_IM)            ## NOTE: There is a "as if in head" code clone.
2275                ## NOTE: There is a "as if in head" code clone.            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2276                if ($self->{insertion_mode} == AFTER_HEAD_IM) {              !!!cp ('t114');
2277                  !!!cp ('t114');              !!!parse-error (type => 'after head',
2278                  !!!parse-error (type => 'after head',                              text => $token->{tag_name}, token => $token);
2279                                  text => $token->{tag_name}, token => $token);              push @{$self->{open_elements}},
2280                  push @{$self->{open_elements}},                  [$self->{head_element}, $el_category->{head}];
2281                      [$self->{head_element}, $el_category->{head}];              $self->{head_element_inserted} = 1;
2282                } else {            } else {
2283                  !!!cp ('t115');              !!!cp ('t115');
2284                }            }
2285                $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
2286                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug [Bug 6038]
2287                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1, () # <head>
2288                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2289              } elsif ($token->{tag_name} eq 'noscript') {            next B;
2290            } elsif ($token->{tag_name} eq 'noscript') {
2291                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2292                  !!!cp ('t116');                  !!!cp ('t116');
2293                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4835  sub _tree_construction_main ($) { Line 2308  sub _tree_construction_main ($) {
2308                  !!!cp ('t118');                  !!!cp ('t118');
2309                  #                  #
2310                }                }
2311              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2312                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2313                  !!!cp ('t119');              !!!cp ('t119');
2314                  ## As if </noscript>              ## As if </noscript>
2315                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2316                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2317                                  token => $token);                              token => $token);
2318                            
2319                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2320                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2321                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2322                  !!!cp ('t120');              !!!cp ('t120');
2323                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2324                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2325                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2326                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2327                } else {              $self->{head_element_inserted} = 1;
2328                  !!!cp ('t121');            } else {
2329                }              !!!cp ('t121');
2330              }
2331    
2332                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2333                $script_start_tag->();            $script_start_tag->();
2334                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2335                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2336                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2337              } elsif ($token->{tag_name} eq 'body' or            next B;
2338                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2339                     $token->{tag_name} eq 'frameset') {
2340                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2341                  !!!cp ('t122');                  !!!cp ('t122');
2342                  ## As if </noscript>                  ## As if </noscript>
# Line 4996  sub _tree_construction_main ($) { Line 2471  sub _tree_construction_main ($) {
2471              } elsif ({              } elsif ({
2472                        body => 1, html => 1,                        body => 1, html => 1,
2473                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2474                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2475                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2476                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2477                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2478                  !!!cp ('t140');                  !!!cp ('t140');
# Line 5040  sub _tree_construction_main ($) { Line 2516  sub _tree_construction_main ($) {
2516                  ## Reprocess in the "after head" insertion mode...                  ## Reprocess in the "after head" insertion mode...
2517                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2518                  !!!cp ('t143.3');                  !!!cp ('t143.3');
2519                  ## ISSUE: Two parse errors for <head><noscript></br>                  ## NOTE: Two parse errors for <head><noscript></br>
2520                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2521                                  text => 'br', token => $token);                                  text => 'br', token => $token);
2522                  ## As if </noscript>                  ## As if </noscript>
# Line 5183  sub _tree_construction_main ($) { Line 2659  sub _tree_construction_main ($) {
2659                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2660                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2661                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2662                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2663                  ## have an element in table scope                  ## have an element in table scope
2664                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2665                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2666                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2667                      !!!cp ('t151');                      !!!cp ('t151');
2668    
2669                      ## Close the cell                      ## Close the cell
# Line 5211  sub _tree_construction_main ($) { Line 2687  sub _tree_construction_main ($) {
2687                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2688                  !!!next-token;                  !!!next-token;
2689                  next B;                  next B;
2690                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2691                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2692                                  token => $token);                                  token => $token);
2693                                    
# Line 5221  sub _tree_construction_main ($) { Line 2697  sub _tree_construction_main ($) {
2697                  INSCOPE: {                  INSCOPE: {
2698                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2699                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2700                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2701                        !!!cp ('t155');                        !!!cp ('t155');
2702                        $i = $_;                        $i = $_;
2703                        last INSCOPE;                        last INSCOPE;
# Line 5247  sub _tree_construction_main ($) { Line 2723  sub _tree_construction_main ($) {
2723                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2724                  }                  }
2725    
2726                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2727                    !!!cp ('t159');                    !!!cp ('t159');
2728                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2729                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5276  sub _tree_construction_main ($) { Line 2752  sub _tree_construction_main ($) {
2752              }              }
2753            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2754              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2755                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2756                  ## have an element in table scope                  ## have an element in table scope
2757                  my $i;                  my $i;
2758                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5326  sub _tree_construction_main ($) { Line 2802  sub _tree_construction_main ($) {
2802                                    
2803                  !!!next-token;                  !!!next-token;
2804                  next B;                  next B;
2805                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2806                  !!!cp ('t169');                  !!!cp ('t169');
2807                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2808                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5338  sub _tree_construction_main ($) { Line 2814  sub _tree_construction_main ($) {
2814                  #                  #
2815                }                }
2816              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2817                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2818                  ## have a table element in table scope                  ## have a table element in table scope
2819                  my $i;                  my $i;
2820                  INSCOPE: {                  INSCOPE: {
2821                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2822                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2823                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2824                        !!!cp ('t171');                        !!!cp ('t171');
2825                        $i = $_;                        $i = $_;
2826                        last INSCOPE;                        last INSCOPE;
# Line 5369  sub _tree_construction_main ($) { Line 2845  sub _tree_construction_main ($) {
2845                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2846                  }                  }
2847                                    
2848                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2849                    !!!cp ('t175');                    !!!cp ('t175');
2850                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2851                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5387  sub _tree_construction_main ($) { Line 2863  sub _tree_construction_main ($) {
2863                                    
2864                  !!!next-token;                  !!!next-token;
2865                  next B;                  next B;
2866                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2867                  !!!cp ('t177');                  !!!cp ('t177');
2868                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2869                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5402  sub _tree_construction_main ($) { Line 2878  sub _tree_construction_main ($) {
2878                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2879                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2880                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2881                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2882                ## have an element in table scope                ## have an element in table scope
2883                my $i;                my $i;
2884                my $tn;                my $tn;
# Line 5419  sub _tree_construction_main ($) { Line 2895  sub _tree_construction_main ($) {
2895                                line => $token->{line},                                line => $token->{line},
2896                                column => $token->{column}};                                column => $token->{column}};
2897                      next B;                      next B;
2898                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2899                      !!!cp ('t180');                      !!!cp ('t180');
2900                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2901                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5439  sub _tree_construction_main ($) { Line 2915  sub _tree_construction_main ($) {
2915                  next B;                  next B;
2916                } # INSCOPE                } # INSCOPE
2917              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2918                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2919                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2920                                token => $token);                                token => $token);
2921    
# Line 5448  sub _tree_construction_main ($) { Line 2924  sub _tree_construction_main ($) {
2924                my $i;                my $i;
2925                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2926                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
2927                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
2928                    !!!cp ('t184');                    !!!cp ('t184');
2929                    $i = $_;                    $i = $_;
2930                    last INSCOPE;                    last INSCOPE;
# Line 5459  sub _tree_construction_main ($) { Line 2935  sub _tree_construction_main ($) {
2935                } # INSCOPE                } # INSCOPE
2936                unless (defined $i) {                unless (defined $i) {
2937                  !!!cp ('t186');                  !!!cp ('t186');
2938            ## TODO: Wrong error type?
2939                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2940                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
2941                  ## Ignore the token                  ## Ignore the token
# Line 5472  sub _tree_construction_main ($) { Line 2949  sub _tree_construction_main ($) {
2949                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2950                }                }
2951    
2952                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2953                  !!!cp ('t188');                  !!!cp ('t188');
2954                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
2955                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5504  sub _tree_construction_main ($) { Line 2981  sub _tree_construction_main ($) {
2981                  !!!cp ('t191');                  !!!cp ('t191');
2982                  #                  #
2983                }                }
2984              } elsif ({          } elsif ({
2985                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
2986                        thead => 1, tr => 1,                    thead => 1, tr => 1,
2987                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
2988                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2989                !!!cp ('t192');            !!!cp ('t192');
2990                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
2991                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
2992                ## Ignore the token            ## Ignore the token
2993                !!!next-token;            !!!next-token;
2994                next B;            next B;
2995              } else {          } else {
2996                !!!cp ('t193');            !!!cp ('t193');
2997                #            #
2998              }          }
2999        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3000          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3001            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5553  sub _tree_construction_main ($) { Line 3030  sub _tree_construction_main ($) {
3030    
3031          !!!parse-error (type => 'in table:#text', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
3032    
3033              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
3034              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
3035                            
3036              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
3037                # MUST            # MUST
3038                my $foster_parent_element;            my $foster_parent_element;
3039                my $next_sibling;            my $next_sibling;
3040                my $prev_sibling;            my $prev_sibling;
3041                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
3042                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
3043                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3044                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
3045                      !!!cp ('t196');                  $foster_parent_element = $parent;
3046                      $foster_parent_element = $parent;                  !!!cp ('t196');
3047                      $next_sibling = $self->{open_elements}->[$_]->[0];                  $next_sibling = $self->{open_elements}->[$_]->[0];
3048                      $prev_sibling = $next_sibling->previous_sibling;                  $prev_sibling = $next_sibling->previous_sibling;
3049                    } 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});  
3050                } else {                } else {
3051                  !!!cp ('t199');                  !!!cp ('t197');
3052                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3053                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
3054                     $next_sibling);                  #
3055                }                }
3056                  last OE;
3057                }
3058              } # OE
3059              $foster_parent_element = $self->{open_elements}->[0]->[0] and
3060              $prev_sibling = $foster_parent_element->last_child
3061                  unless defined $foster_parent_element;
3062              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
3063              if (defined $prev_sibling and
3064                  $prev_sibling->node_type == 3) {
3065                !!!cp ('t198');
3066                $prev_sibling->manakai_append_text ($token->{data});
3067              } else {
3068                !!!cp ('t199');
3069                $foster_parent_element->insert_before
3070                    ($self->{document}->create_text_node ($token->{data}),
3071                     $next_sibling);
3072              }
3073            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
3074              $open_tables->[-1]->[2] = 1; # ~node inserted
3075          } else {          } else {
3076              ## NOTE: Fragment case or in a foster parent'ed element
3077              ## (e.g. |<table><span>a|).  In fragment case, whether the
3078              ## character is appended to existing node or a new node is
3079              ## created is irrelevant, since the foster parent'ed nodes
3080              ## are discarded and fragment parsing does not invoke any
3081              ## script.
3082            !!!cp ('t200');            !!!cp ('t200');
3083            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
3084                  ($token->{data});
3085          }          }
3086                            
3087          !!!next-token;          !!!next-token;
3088          next B;          next B;
3089        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3090          if ({          if ({
3091               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3092               th => 1, td => 1,               th => 1, td => 1,
3093              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3094            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3095              ## Clear back to table context              ## Clear back to table context
3096              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3097                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5619  sub _tree_construction_main ($) { Line 3104  sub _tree_construction_main ($) {
3104              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3105            }            }
3106                        
3107            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3108              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3109                !!!cp ('t202');                !!!cp ('t202');
3110                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5633  sub _tree_construction_main ($) { Line 3118  sub _tree_construction_main ($) {
3118                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3119              }              }
3120                                    
3121                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3122                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3123                    !!!cp ('t204');                !!!cp ('t204');
3124                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3125                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3126                    !!!next-token;                !!!nack ('t204');
3127                    next B;                !!!next-token;
3128                  } else {                next B;
3129                    !!!cp ('t205');              } else {
3130                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3131                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3132                  }                ## reprocess in the "in row" insertion mode
3133                } else {              }
3134                  !!!cp ('t206');            } else {
3135                }              !!!cp ('t206');
3136              }
3137    
3138                ## Clear back to table row context                ## Clear back to table row context
3139                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5656  sub _tree_construction_main ($) { Line 3142  sub _tree_construction_main ($) {
3142                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3143                }                }
3144                                
3145                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3146                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3147              $self->{insertion_mode} = IN_CELL_IM;
3148    
3149                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3150                                
3151                !!!nack ('t207.1');            !!!nack ('t207.1');
3152              !!!next-token;
3153              next B;
3154            } elsif ({
3155                      caption => 1, col => 1, colgroup => 1,
3156                      tbody => 1, tfoot => 1, thead => 1,
3157                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3158                     }->{$token->{tag_name}}) {
3159              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3160                ## As if </tr>
3161                ## have an element in table scope
3162                my $i;
3163                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3164                  my $node = $self->{open_elements}->[$_];
3165                  if ($node->[1] == TABLE_ROW_EL) {
3166                    !!!cp ('t208');
3167                    $i = $_;
3168                    last INSCOPE;
3169                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3170                    !!!cp ('t209');
3171                    last INSCOPE;
3172                  }
3173                } # INSCOPE
3174                unless (defined $i) {
3175                  !!!cp ('t210');
3176                  ## TODO: This type is wrong.
3177                  !!!parse-error (type => 'unmacthed end tag',
3178                                  text => $token->{tag_name}, token => $token);
3179                  ## Ignore the token
3180                  !!!nack ('t210.1');
3181                !!!next-token;                !!!next-token;
3182                next B;                next B;
3183              } 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;  
                 }  
3184                                    
3185                  ## Clear back to table row context                  ## Clear back to table row context
3186                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5716  sub _tree_construction_main ($) { Line 3203  sub _tree_construction_main ($) {
3203                  }                  }
3204                }                }
3205    
3206                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3207                  ## have an element in table scope                  ## have an element in table scope
3208                  my $i;                  my $i;
3209                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3210                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3211                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3212                      !!!cp ('t214');                      !!!cp ('t214');
3213                      $i = $_;                      $i = $_;
3214                      last INSCOPE;                      last INSCOPE;
# Line 5763  sub _tree_construction_main ($) { Line 3250  sub _tree_construction_main ($) {
3250                  !!!cp ('t218');                  !!!cp ('t218');
3251                }                }
3252    
3253                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3254                  ## Clear back to table context              ## Clear back to table context
3255                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3256                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3257                    !!!cp ('t219');                !!!cp ('t219');
3258                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3259                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3260                  }              }
3261                                
3262                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3263                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3264                  ## reprocess              ## reprocess
3265                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3266                  next B;              !!!ack-later;
3267                } elsif ({              next B;
3268                          caption => 1,            } elsif ({
3269                          colgroup => 1,                      caption => 1,
3270                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3271                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3272                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3273                ## Clear back to table context
3274                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3275                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3276                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5790  sub _tree_construction_main ($) { Line 3278  sub _tree_construction_main ($) {
3278                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3279                  }                  }
3280                                    
3281                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3282                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3283                                    
3284                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3285                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3286                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3287                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3288                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3289                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3290                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3291                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3292                  !!!next-token;                                        }->{$token->{tag_name}};
3293                  !!!nack ('t220.1');              !!!next-token;
3294                  next B;              !!!nack ('t220.1');
3295                } else {              next B;
3296                  die "$0: in table: <>: $token->{tag_name}";            } else {
3297                }              die "$0: in table: <>: $token->{tag_name}";
3298              }
3299              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3300                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3301                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5818  sub _tree_construction_main ($) { Line 3307  sub _tree_construction_main ($) {
3307                my $i;                my $i;
3308                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3309                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3310                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3311                    !!!cp ('t221');                    !!!cp ('t221');
3312                    $i = $_;                    $i = $_;
3313                    last INSCOPE;                    last INSCOPE;
# Line 5845  sub _tree_construction_main ($) { Line 3334  sub _tree_construction_main ($) {
3334                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3335                }                }
3336    
3337                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3338                  !!!cp ('t225');                  !!!cp ('t225');
3339                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3340                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5869  sub _tree_construction_main ($) { Line 3358  sub _tree_construction_main ($) {
3358              !!!cp ('t227.8');              !!!cp ('t227.8');
3359              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3360              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
3361                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3362              next B;              next B;
3363            } else {            } else {
3364              !!!cp ('t227.7');              !!!cp ('t227.7');
# Line 5879  sub _tree_construction_main ($) { Line 3369  sub _tree_construction_main ($) {
3369              !!!cp ('t227.6');              !!!cp ('t227.6');
3370              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3371              $script_start_tag->();              $script_start_tag->();
3372                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3373              next B;              next B;
3374            } else {            } else {
3375              !!!cp ('t227.5');              !!!cp ('t227.5');
# Line 5894  sub _tree_construction_main ($) { Line 3385  sub _tree_construction_main ($) {
3385                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
3386    
3387                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3388                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3389    
3390                  ## TODO: form element pointer                  ## TODO: form element pointer
3391    
# Line 5925  sub _tree_construction_main ($) { Line 3417  sub _tree_construction_main ($) {
3417          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3418          #          #
3419        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3420              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3421                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3422                ## have an element in table scope            ## have an element in table scope
3423                my $i;                my $i;
3424                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3425                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3426                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3427                    !!!cp ('t228');                    !!!cp ('t228');
3428                    $i = $_;                    $i = $_;
3429                    last INSCOPE;                    last INSCOPE;
# Line 5966  sub _tree_construction_main ($) { Line 3458  sub _tree_construction_main ($) {
3458                !!!nack ('t231.1');                !!!nack ('t231.1');
3459                next B;                next B;
3460              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3461                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3462                  ## As if </tr>                  ## As if </tr>
3463                  ## have an element in table scope                  ## have an element in table scope
3464                  my $i;                  my $i;
3465                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3466                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3467                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3468                      !!!cp ('t233');                      !!!cp ('t233');
3469                      $i = $_;                      $i = $_;
3470                      last INSCOPE;                      last INSCOPE;
# Line 6005  sub _tree_construction_main ($) { Line 3497  sub _tree_construction_main ($) {
3497                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3498                }                }
3499    
3500                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3501                  ## have an element in table scope                  ## have an element in table scope
3502                  my $i;                  my $i;
3503                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3504                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3505                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3506                      !!!cp ('t237');                      !!!cp ('t237');
3507                      $i = $_;                      $i = $_;
3508                      last INSCOPE;                      last INSCOPE;
# Line 6057  sub _tree_construction_main ($) { Line 3549  sub _tree_construction_main ($) {
3549                my $i;                my $i;
3550                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3551                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3552                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3553                    !!!cp ('t241');                    !!!cp ('t241');
3554                    $i = $_;                    $i = $_;
3555                    last INSCOPE;                    last INSCOPE;
# Line 6087  sub _tree_construction_main ($) { Line 3579  sub _tree_construction_main ($) {
3579                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3580                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3581                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3582                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3583                  ## have an element in table scope                  ## have an element in table scope
3584                  my $i;                  my $i;
3585                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6116  sub _tree_construction_main ($) { Line 3608  sub _tree_construction_main ($) {
3608                  my $i;                  my $i;
3609                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3610                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3611                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3612                      !!!cp ('t250');                      !!!cp ('t250');
3613                      $i = $_;                      $i = $_;
3614                      last INSCOPE;                      last INSCOPE;
# Line 6206  sub _tree_construction_main ($) { Line 3698  sub _tree_construction_main ($) {
3698            #            #
3699          }          }
3700        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3701          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3702                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3703            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3704            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6221  sub _tree_construction_main ($) { Line 3713  sub _tree_construction_main ($) {
3713        } else {        } else {
3714          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3715        }        }
3716      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3717            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3718              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3719                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6248  sub _tree_construction_main ($) { Line 3740  sub _tree_construction_main ($) {
3740              }              }
3741            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3742              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3743                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3744                  !!!cp ('t264');                  !!!cp ('t264');
3745                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3746                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6274  sub _tree_construction_main ($) { Line 3766  sub _tree_construction_main ($) {
3766                #                #
3767              }              }
3768        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3769          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3770              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3771            !!!cp ('t270.2');            !!!cp ('t270.2');
3772            ## Stop parsing.            ## Stop parsing.
# Line 6292  sub _tree_construction_main ($) { Line 3784  sub _tree_construction_main ($) {
3784        }        }
3785    
3786            ## As if </colgroup>            ## As if </colgroup>
3787            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3788              !!!cp ('t269');              !!!cp ('t269');
3789  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3790              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6317  sub _tree_construction_main ($) { Line 3809  sub _tree_construction_main ($) {
3809          next B;          next B;
3810        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3811          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3812            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3813              !!!cp ('t272');              !!!cp ('t272');
3814              ## As if </option>              ## As if </option>
3815              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6330  sub _tree_construction_main ($) { Line 3822  sub _tree_construction_main ($) {
3822            !!!next-token;            !!!next-token;
3823            next B;            next B;
3824          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3825            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3826              !!!cp ('t274');              !!!cp ('t274');
3827              ## As if </option>              ## As if </option>
3828              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6338  sub _tree_construction_main ($) { Line 3830  sub _tree_construction_main ($) {
3830              !!!cp ('t275');              !!!cp ('t275');
3831            }            }
3832    
3833            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3834              !!!cp ('t276');              !!!cp ('t276');
3835              ## As if </optgroup>              ## As if </optgroup>
3836              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6351  sub _tree_construction_main ($) { Line 3843  sub _tree_construction_main ($) {
3843            !!!next-token;            !!!next-token;
3844            next B;            next B;
3845          } elsif ({          } elsif ({
3846                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3847                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3848                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3849                          == IN_SELECT_IN_TABLE_IM and
3850                    {                    {
3851                     caption => 1, table => 1,                     caption => 1, table => 1,
3852                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
# Line 6368  sub _tree_construction_main ($) { Line 3861  sub _tree_construction_main ($) {
3861            my $i;            my $i;
3862            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3863              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3864              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3865                !!!cp ('t278');                !!!cp ('t278');
3866                $i = $_;                $i = $_;
3867                last INSCOPE;                last INSCOPE;
# Line 6413  sub _tree_construction_main ($) { Line 3906  sub _tree_construction_main ($) {
3906          }          }
3907        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3908          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3909            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3910                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3911              !!!cp ('t283');              !!!cp ('t283');
3912              ## As if </option>              ## As if </option>
3913              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3914            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3915              !!!cp ('t284');              !!!cp ('t284');
3916              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3917            } else {            } else {
# Line 6431  sub _tree_construction_main ($) { Line 3924  sub _tree_construction_main ($) {
3924            !!!next-token;            !!!next-token;
3925            next B;            next B;
3926          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3927            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3928              !!!cp ('t286');              !!!cp ('t286');
3929              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3930            } else {            } else {
# Line 6448  sub _tree_construction_main ($) { Line 3941  sub _tree_construction_main ($) {
3941            my $i;            my $i;
3942            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3943              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3944              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3945                !!!cp ('t288');                !!!cp ('t288');
3946                $i = $_;                $i = $_;
3947                last INSCOPE;                last INSCOPE;
# Line 6475  sub _tree_construction_main ($) { Line 3968  sub _tree_construction_main ($) {
3968            !!!nack ('t291.1');            !!!nack ('t291.1');
3969            !!!next-token;            !!!next-token;
3970            next B;            next B;
3971          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3972                         == IN_SELECT_IN_TABLE_IM and
3973                   {                   {
3974                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3975                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6510  sub _tree_construction_main ($) { Line 4004  sub _tree_construction_main ($) {
4004            undef $i;            undef $i;
4005            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4006              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4007              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4008                !!!cp ('t295');                !!!cp ('t295');
4009                $i = $_;                $i = $_;
4010                last INSCOPE;                last INSCOPE;
# Line 6549  sub _tree_construction_main ($) { Line 4043  sub _tree_construction_main ($) {
4043            next B;            next B;
4044          }          }
4045        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4046          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4047                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4048            !!!cp ('t299.1');            !!!cp ('t299.1');
4049            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6736  sub _tree_construction_main ($) { Line 4230  sub _tree_construction_main ($) {
4230        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4231          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4232              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4233            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4234                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4235              !!!cp ('t325');              !!!cp ('t325');
4236              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6750  sub _tree_construction_main ($) { Line 4244  sub _tree_construction_main ($) {
4244            }            }
4245    
4246            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4247                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4248              !!!cp ('t327');              !!!cp ('t327');
4249              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4250            } else {            } else {
# Line 6782  sub _tree_construction_main ($) { Line 4276  sub _tree_construction_main ($) {
4276            next B;            next B;
4277          }          }
4278        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4279          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4280                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4281            !!!cp ('t331.1');            !!!cp ('t331.1');
4282            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6885  sub _tree_construction_main ($) { Line 4379  sub _tree_construction_main ($) {
4379          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4380                                
4381          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4382              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4383            !!!cp ('t342');            !!!cp ('t342');
4384            ## Ignore the token            ## Ignore the token
4385          } else {          } else {
# Line 6929  sub _tree_construction_main ($) { Line 4423  sub _tree_construction_main ($) {
4423            next B;            next B;
4424          }          }
4425    
4426          ## has a p element in scope          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4427          INSCOPE: for (reverse @{$self->{open_elements}}) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4428            if ($_->[1] & P_EL) {            ## has a p element in scope
4429              !!!cp ('t344');            INSCOPE: for (reverse @{$self->{open_elements}}) {
4430              !!!back-token; # <form>              if ($_->[1] == P_EL) {
4431              $token = {type => END_TAG_TOKEN, tag_name => 'p',                !!!cp ('t344');
4432                        line => $token->{line}, column => $token->{column}};                !!!back-token; # <form>
4433              next B;                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4434            } elsif ($_->[1] & SCOPING_EL) {                          line => $token->{line}, column => $token->{column}};
4435              !!!cp ('t345');                next B;
4436              last INSCOPE;              } elsif ($_->[1] & SCOPING_EL) {
4437            }                !!!cp ('t345');
4438          } # INSCOPE                last INSCOPE;
4439                }
4440              } # INSCOPE
4441            }
4442                        
4443          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4444          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
# Line 7004  sub _tree_construction_main ($) { Line 4501  sub _tree_construction_main ($) {
4501    
4502          ## 1.          ## 1.
4503          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4504            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4505              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4506              {              {
4507                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7035  sub _tree_construction_main ($) { Line 4532  sub _tree_construction_main ($) {
4532                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4533                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4534                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4535                       (not $node->[1] & ADDRESS_DIV_P_EL)
4536                     (not $node->[1] & ADDRESS_EL) &                    ) {
                    (not $node->[1] & DIV_EL) &  
                    (not $node->[1] & P_EL)) {  
4537              ## 3.              ## 3.
4538              !!!cp ('t357');              !!!cp ('t357');
4539              last; ## goto 5.              last; ## goto 5.
# Line 7057  sub _tree_construction_main ($) { Line 4552  sub _tree_construction_main ($) {
4552    
4553          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4554          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4555            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4556              !!!cp ('t353');              !!!cp ('t353');
4557    
4558              ## NOTE: |<p><li>|, for example.              ## NOTE: |<p><li>|, for example.
# Line 7086  sub _tree_construction_main ($) { Line 4581  sub _tree_construction_main ($) {
4581    
4582          ## 1.          ## 1.
4583          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4584            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4585              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4586              {              {
4587                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7118  sub _tree_construction_main ($) { Line 4613  sub _tree_construction_main ($) {
4613                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4614                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4615    
4616                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4617                     (not $node->[1] & DIV_EL) &                    ) {
                    (not $node->[1] & P_EL)) {  
4618              ## 3.              ## 3.
4619              !!!cp ('t357.1');              !!!cp ('t357.1');
4620              last; ## goto 5.              last; ## goto 5.
# Line 7139  sub _tree_construction_main ($) { Line 4633  sub _tree_construction_main ($) {
4633    
4634          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4635          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4636            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4637              !!!cp ('t353.1');              !!!cp ('t353.1');
4638              !!!back-token; # <x>              !!!back-token; # <x>
4639              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7161  sub _tree_construction_main ($) { Line 4655  sub _tree_construction_main ($) {
4655    
4656          ## has a p element in scope          ## has a p element in scope
4657          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4658            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4659              !!!cp ('t367');              !!!cp ('t367');
4660              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4661              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7183  sub _tree_construction_main ($) { Line 4677  sub _tree_construction_main ($) {
4677        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4678          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4679            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4680            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4681              !!!cp ('t371');              !!!cp ('t371');
4682              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4683                            
# Line 7227  sub _tree_construction_main ($) { Line 4721  sub _tree_construction_main ($) {
4721          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4722          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4723            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4724            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4725              !!!cp ('t376');              !!!cp ('t376');
4726              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4727              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7250  sub _tree_construction_main ($) { Line 4744  sub _tree_construction_main ($) {
4744          ## has a button element in scope          ## has a button element in scope
4745          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4746            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4747            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4748              !!!cp ('t378');              !!!cp ('t378');
4749              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4750              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7315  sub _tree_construction_main ($) { Line 4809  sub _tree_construction_main ($) {
4809                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4810                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4811                           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}},  
4812                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4813                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4814                         );                         );
# Line 7339  sub _tree_construction_main ($) { Line 4831  sub _tree_construction_main ($) {
4831                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4832                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4833                           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}},  
4834                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4835                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4836                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7350  sub _tree_construction_main ($) { Line 4840  sub _tree_construction_main ($) {
4840            next B;            next B;
4841          }          }
4842        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4843          my $tag_name = $token->{tag_name};          ## Step 1
4844          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4845                    
4846            ## Step 2
4847          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4848    
4849            ## Step 3
4850            $self->{ignore_newline} = 1;
4851    
4852            ## Step 4
4853            ## ISSUE: This step is wrong. (r2302 enbugged)
4854    
4855            ## Step 5
4856          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4857          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4858            
4859          $insert->($el);          ## Step 6-7
4860                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4861          my $text = '';  
4862          !!!nack ('t392.1');          !!!nack ('t392.1');
4863          !!!next-token;          !!!next-token;
4864          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4865            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4866            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4867              !!!cp ('t392');          ## has an |option| element in scope
4868              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4869            } else {            my $node = $self->{open_elements}->[$_];
4870              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4871                !!!cp ('t397.1');
4872                ## NOTE: As if </option>
4873                !!!back-token; # <option> or <optgroup>
4874                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4875                          line => $token->{line}, column => $token->{column}};
4876                next B;
4877              } elsif ($node->[1] & SCOPING_EL) {
4878                !!!cp ('t397.2');
4879                last INSCOPE;
4880            }            }
4881          } else {          } # INSCOPE
4882            !!!cp ('t394');  
4883          }          $reconstruct_active_formatting_elements->($insert_to_current);
4884          while ($token->{type} == CHARACTER_TOKEN) {  
4885            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4886            $text .= $token->{data};  
4887            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
4888          !!!next-token;          !!!next-token;
4889          next B;          redo B;
4890        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4891                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4892          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4893          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4894            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4895            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4896              !!!cp ('t398.1');              !!!cp ('t398.1');
4897              ## generate implied end tags              ## generate implied end tags
4898              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4899                !!!cp ('t398.2');                !!!cp ('t398.2');
4900                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4901              }              }
4902              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4903                !!!cp ('t398.3');                !!!cp ('t398.3');
4904                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4905                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4906                                    ->manakai_local_name,                                    ->manakai_local_name,
4907                                token => $token);                                token => $token);
4908                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4909                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4910              }              }
4911              last INSCOPE;              last INSCOPE;
4912            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7423  sub _tree_construction_main ($) { Line 4914  sub _tree_construction_main ($) {
4914              last INSCOPE;              last INSCOPE;
4915            }            }
4916          } # INSCOPE          } # INSCOPE
4917              
4918            ## TODO: <non-ruby><rt> is not allowed.
4919    
4920          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4921    
# Line 7443  sub _tree_construction_main ($) { Line 4936  sub _tree_construction_main ($) {
4936                    
4937          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4938            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4939            !!!ack ('t398.1');            !!!ack ('t398.6');
4940          } else {          } else {
4941            !!!cp ('t398.2');            !!!cp ('t398.7');
4942            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
4943            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
4944            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7456  sub _tree_construction_main ($) { Line 4949  sub _tree_construction_main ($) {
4949          next B;          next B;
4950        } elsif ({        } elsif ({
4951                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4952                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
4953                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
4954                  thead => 1, tr => 1,                  thead => 1, tr => 1,
4955                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7520  sub _tree_construction_main ($) { Line 5013  sub _tree_construction_main ($) {
5013                    
5014            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5015                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5016                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5017              !!!cp ('t400.1');              !!!cp ('t400.1');
5018              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5019            } else {            } else {
# Line 7541  sub _tree_construction_main ($) { Line 5034  sub _tree_construction_main ($) {
5034          my $i;          my $i;
5035          INSCOPE: {          INSCOPE: {
5036            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5037              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5038                !!!cp ('t405');                !!!cp ('t405');
5039                $i = $_;                $i = $_;
5040                last INSCOPE;                last INSCOPE;
# Line 7551  sub _tree_construction_main ($) { Line 5044  sub _tree_construction_main ($) {
5044              }              }
5045            }            }
5046    
5047            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
5048    
5049              !!!parse-error (type => 'unmatched end tag',
5050                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5051            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
5052            !!!next-token;            !!!next-token;
# Line 7559  sub _tree_construction_main ($) { Line 5054  sub _tree_construction_main ($) {
5054          } # INSCOPE          } # INSCOPE
5055    
5056          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5057            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5058                      $_->[1] == OPTGROUP_EL ||
5059                      $_->[1] == OPTION_EL ||
5060                      $_->[1] == RUBY_COMPONENT_EL) {
5061              !!!cp ('t403');              !!!cp ('t403');
5062              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5063                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7577  sub _tree_construction_main ($) { Line 5075  sub _tree_construction_main ($) {
5075          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5076          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5077          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5078              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5079            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
5080              !!!cp ('t406');              !!!cp ('t406');
5081              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5082                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7685  sub _tree_construction_main ($) { Line 5183  sub _tree_construction_main ($) {
5183          my $i;          my $i;
5184          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5185            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5186            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5187              !!!cp ('t418');              !!!cp ('t418');
5188              $i = $_;              $i = $_;
5189              last INSCOPE;              last INSCOPE;
# Line 7733  sub _tree_construction_main ($) { Line 5231  sub _tree_construction_main ($) {
5231          my $i;          my $i;
5232          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5233            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5234            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5235              !!!cp ('t423');              !!!cp ('t423');
5236              $i = $_;              $i = $_;
5237              last INSCOPE;              last INSCOPE;
# Line 7779  sub _tree_construction_main ($) { Line 5277  sub _tree_construction_main ($) {
5277          my $i;          my $i;
5278          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5279            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5280            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5281              !!!cp ('t410.1');              !!!cp ('t410.1');
5282              $i = $_;              $i = $_;
5283              last INSCOPE;              last INSCOPE;
# Line 7852  sub _tree_construction_main ($) { Line 5350  sub _tree_construction_main ($) {
5350          ## Ignore the token.          ## Ignore the token.
5351          !!!next-token;          !!!next-token;
5352          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
5353        } else {        } else {
5354          if ($token->{tag_name} eq 'sarcasm') {          if ($token->{tag_name} eq 'sarcasm') {
5355            sleep 0.001; # take a deep breath            sleep 0.001; # take a deep breath
# Line 7881  sub _tree_construction_main ($) { Line 5361  sub _tree_construction_main ($) {
5361    
5362          ## Step 2          ## Step 2
5363          S2: {          S2: {
5364            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5365              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5366              if ($node_tag_name eq $token->{tag_name}) {
5367              ## Step 1              ## Step 1
5368              ## generate implied end tags              ## generate implied end tags
5369              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7894  sub _tree_construction_main ($) { Line 5376  sub _tree_construction_main ($) {
5376              }              }
5377                    
5378              ## Step 2              ## Step 2
5379              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5380                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5381                $current_tag_name =~ tr/A-Z/a-z/;
5382                if ($current_tag_name ne $token->{tag_name}) {
5383                !!!cp ('t431');                !!!cp ('t431');
5384                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5385                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7970  sub _tree_construction_main ($) { Line 5454  sub _tree_construction_main ($) {
5454    ## TODO: script stuffs    ## TODO: script stuffs
5455  } # _tree_construct_main  } # _tree_construct_main
5456    
5457    ## XXX: How this method is organized is somewhat out of date, although
5458    ## it still does what the current spec documents.
5459  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5460    my $class = shift;    my $class = shift;
5461    my $node = shift;    my $node = shift; # /context/
5462    #my $s = \$_[0];    #my $s = \$_[0];
5463    my $onerror = $_[1];    my $onerror = $_[1];
5464    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
# Line 7980  sub set_inner_html ($$$$;$) { Line 5466  sub set_inner_html ($$$$;$) {
5466    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
5467    
5468    my $nt = $node->node_type;    my $nt = $node->node_type;
5469    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5470      # MUST      # MUST
5471            
5472      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7995  sub set_inner_html ($$$$;$) { Line 5481  sub set_inner_html ($$$$;$) {
5481    
5482      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5483      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5484    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5485      ## TODO: If non-html element      ## TODO: If non-html element
5486    
5487      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5488    
5489  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5490    
5491      ## Step 1 # MUST      ## F1. Create an HTML document.
5492      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5493      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5494      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5495    
5496        ## F2. Propagate quirkness flag
5497        my $node_doc = $node->owner_document;
5498        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5499    
5500        ## F3. Create an HTML parser
5501      my $p = $class->new;      my $p = $class->new;
5502      $p->{document} = $doc;      $p->{document} = $doc;
5503    
# Line 8133  sub set_inner_html ($$$$;$) { Line 5625  sub set_inner_html ($$$$;$) {
5625      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5626      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5627    
5628      ## Step 2      ## F4. If /context/ is not undef...
5629    
5630        ## F4.1. content model flag
5631      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5632      $p->{content_model} = {      $p->{content_model} = {
5633        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8149  sub set_inner_html ($$$$;$) { Line 5643  sub set_inner_html ($$$$;$) {
5643      }->{$node_ln};      }->{$node_ln};
5644      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5645          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5646    
5647      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5648        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5649    
5650      ## Step 3      ## F4.2. Root |html| element
5651      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5652        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5653    
5654      ## Step 4 # MUST      ## F4.3.
5655      $doc->append_child ($root);      $doc->append_child ($root);
5656    
5657      ## Step 5 # MUST      ## F4.4.
5658      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5659    
5660      undef $p->{head_element};      undef $p->{head_element};
5661        undef $p->{head_element_inserted};
5662    
5663      ## Step 6 # MUST      ## F4.5.
5664      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5665    
5666      ## Step 7 # MUST      ## F4.6.
5667      my $anode = $node;      my $anode = $node;
5668      AN: while (defined $anode) {      AN: while (defined $anode) {
5669        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8184  sub set_inner_html ($$$$;$) { Line 5678  sub set_inner_html ($$$$;$) {
5678        }        }
5679        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5680      } # AN      } # AN
5681        
5682      ## Step 9 # MUST      ## F.6. Start the parser.
5683      {      {
5684        my $self = $p;        my $self = $p;
5685        !!!next-token;        !!!next-token;
5686      }      }
5687      $p->_tree_construction_main;      $p->_tree_construction_main;
5688    
5689      ## Step 10 # MUST      ## F.7.
5690      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5691      for (@cn) {      for (@cn) {
5692        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.198  
changed lines
  Added in v.1.220

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24