/[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.191 by wakaba, Mon Sep 22 06:04:29 2008 UTC revision 1.227 by wakaba, Sun Aug 16 06:31:20 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
 sub ADDRESS_EL () { 0b10 }  
 sub BODY_EL () { 0b100 }  
 sub BUTTON_EL () { 0b1000 }  
 sub CAPTION_EL () { 0b10000 }  
 sub DD_EL () { 0b100000 }  
 sub DIV_EL () { 0b1000000 }  
 sub DT_EL () { 0b10000000 }  
 sub FORM_EL () { 0b100000000 }  
 sub FORMATTING_EL () { 0b1000000000 }  
 sub FRAMESET_EL () { 0b10000000000 }  
 sub HEADING_EL () { 0b100000000000 }  
 sub HTML_EL () { 0b1000000000000 }  
 sub LI_EL () { 0b10000000000000 }  
 sub NOBR_EL () { 0b100000000000000 }  
 sub OPTION_EL () { 0b1000000000000000 }  
 sub OPTGROUP_EL () { 0b10000000000000000 }  
 sub P_EL () { 0b100000000000000000 }  
 sub SELECT_EL () { 0b1000000000000000000 }  
 sub TABLE_EL () { 0b10000000000000000000 }  
 sub TABLE_CELL_EL () { 0b100000000000000000000 }  
 sub TABLE_ROW_EL () { 0b1000000000000000000000 }  
 sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  
 sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
36    
37  ## NOTE: Used in "generate implied end tags" algorithm.  ## Bits 12-15
38  ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL  sub SPECIAL_EL () { 0b1_000000000000000 }
39  ## is used in "generate implied end tags" implementation (search for the  sub SCOPING_EL () { 0b1_00000000000000 }
40  ## function mae).  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub END_TAG_OPTIONAL_EL () {  sub PHRASING_EL () { 0b1_000000000000 }
42    DD_EL |  
43    DT_EL |  ## Bits 10-11
44    LI_EL |  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45    P_EL |  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46    RUBY_COMPONENT_EL  
47  }  ## Bits 6-9
48    sub TABLE_SCOPING_EL () { 0b1_000000000 }
49    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51    sub TABLE_ROWS_EL () { 0b1_000000 }
52    
53    ## Bit 5
54    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55    
56  ## NOTE: Used in </body> and EOF algorithms.  ## NOTE: Used in </body> and EOF algorithms.
57  sub ALL_END_TAG_OPTIONAL_EL () {  ## Bit 4
58    DD_EL |  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  sub SCOPING_EL () {  ## NOTE: Used in "generate implied end tags" algorithm.
61    BUTTON_EL |  ## NOTE: There is a code where a modified version of
62    CAPTION_EL |  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63    HTML_EL |  ## implementation (search for the algorithm name).
64    TABLE_EL |  ## Bit 3
65    TABLE_CELL_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
66    MISC_SCOPING_EL  
67    ## Bits 0-2
68    
69    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    sub FORM_EL () { SPECIAL_EL | 0b001 }
71    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
   FORM_EL |  
   FRAMESET_EL |  
   HEADING_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   SELECT_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
124  }  }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156      article => MISC_SPECIAL_EL,
157      aside => MISC_SPECIAL_EL,
158    b => FORMATTING_EL,    b => FORMATTING_EL,
159    base => MISC_SPECIAL_EL,    base => MISC_SPECIAL_EL,
160    basefont => MISC_SPECIAL_EL,    basefont => MISC_SPECIAL_EL,
# Line 154  my $el_category = { Line 168  my $el_category = {
168    center => MISC_SPECIAL_EL,    center => MISC_SPECIAL_EL,
169    col => MISC_SPECIAL_EL,    col => MISC_SPECIAL_EL,
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    dd => DD_EL,    command => MISC_SPECIAL_EL,
172      datagrid => MISC_SPECIAL_EL,
173      dd => DTDD_EL,
174      details => MISC_SPECIAL_EL,
175      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,
183    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
184      figure => MISC_SPECIAL_EL,
185    font => FORMATTING_EL,    font => FORMATTING_EL,
186      footer => MISC_SPECIAL_EL,
187    form => FORM_EL,    form => FORM_EL,
188    frame => MISC_SPECIAL_EL,    frame => MISC_SPECIAL_EL,
189    frameset => FRAMESET_EL,    frameset => FRAMESET_EL,
# Line 173  my $el_category = { Line 194  my $el_category = {
194    h5 => HEADING_EL,    h5 => HEADING_EL,
195    h6 => HEADING_EL,    h6 => HEADING_EL,
196    head => MISC_SPECIAL_EL,    head => MISC_SPECIAL_EL,
197      header => MISC_SPECIAL_EL,
198    hr => MISC_SPECIAL_EL,    hr => MISC_SPECIAL_EL,
199    html => HTML_EL,    html => HTML_EL,
200    i => FORMATTING_EL,    i => FORMATTING_EL,
201    iframe => MISC_SPECIAL_EL,    iframe => MISC_SPECIAL_EL,
202    img => MISC_SPECIAL_EL,    img => MISC_SPECIAL_EL,
203      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
204    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
205    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
206    li => LI_EL,    li => LI_EL,
# Line 186  my $el_category = { Line 209  my $el_category = {
209    marquee => MISC_SCOPING_EL,    marquee => MISC_SCOPING_EL,
210    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
211    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
212    nobr => NOBR_EL | FORMATTING_EL,    nav => MISC_SPECIAL_EL,
213      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 204  my $el_category = { Line 228  my $el_category = {
228    s => FORMATTING_EL,    s => FORMATTING_EL,
229    script => MISC_SPECIAL_EL,    script => MISC_SPECIAL_EL,
230    select => SELECT_EL,    select => SELECT_EL,
231      section => MISC_SPECIAL_EL,
232    small => FORMATTING_EL,    small => FORMATTING_EL,
233    spacer => MISC_SPECIAL_EL,    spacer => MISC_SPECIAL_EL,
234    strike => FORMATTING_EL,    strike => FORMATTING_EL,
# Line 227  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 323  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    
351  my $charref_map = {  ## TODO: Invoke the reset algorithm when a resettable element is
352    0x0D => 0x000A,  ## created (cf. HTML5 revision 2259).
   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;  
353    
354  sub parse_byte_string ($$$$;$) {  sub parse_byte_string ($$$$;$) {
355    my $self = shift;    my $self = shift;
# Line 470  sub parse_byte_stream ($$$$;$$) { Line 454  sub parse_byte_stream ($$$$;$$) {
454      if (defined $charset_name) {      if (defined $charset_name) {
455        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
456    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
457        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
458        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
459            ($byte_stream);            ($byte_stream);
# Line 542  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 563  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 817  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 897  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 927  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};  
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_NAME_STATE) {  
 ## ISSUE: Redundant "First," in the spec.  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (161);  
         $self->{state} = AFTER_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (162);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (163);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (164);  
         $self->{ct}->{name}  
           .= chr ($self->{nc}); # DOCTYPE  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (165);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (166);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (167);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == 0x0050 or # P  
                $self->{nc} == 0x0070) { # p  
         $self->{state} = PUBLIC_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0053 or # S  
                $self->{nc} == 0x0073) { # s  
         $self->{state} = SYSTEM_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (180);  
         !!!parse-error (type => 'string after DOCTYPE name');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == PUBLIC_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0055, # U  
             0x0042, # B  
             0x004C, # L  
             0x0049, # I  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0075, # u  
             0x0062, # b  
             0x006C, # l  
             0x0069, # i  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (175);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x0043 or # C  
                 $self->{nc} == 0x0063)) { # c  
         !!!cp (168);  
         $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (169);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == SYSTEM_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0059, # Y  
             0x0053, # S  
             0x0054, # T  
             0x0045, # E  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0079, # y  
             0x0073, # s  
             0x0074, # t  
             0x0065, # e  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (170);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x004D or # M  
                 $self->{nc} == 0x006D)) { # m  
         !!!cp (171);  
         $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (172);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (181);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0022) { # "  
         !!!cp (182);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0027) { # '  
         !!!cp (183);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x003E) { # >  
         !!!cp (184);  
         !!!parse-error (type => 'no PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (185);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (186);  
         !!!parse-error (type => 'string after PUBLIC');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (187);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (188);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (189);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (190);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q[">],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (191);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (192);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (193);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (194);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q['>],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (195);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (196);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (197);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (198);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (199);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (200);  
         !!!parse-error (type => 'string after PUBLIC literal');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (201);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (202);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (203);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (204);  
         !!!parse-error (type => 'no SYSTEM literal');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (205);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (206);  
         !!!parse-error (type => 'string after SYSTEM');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (207);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (208);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (209);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (210);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q[">],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (211);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (212);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (213);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (214);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q['>],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (215);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (216);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (217);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (218);  
         !!!parse-error (type => 'string after SYSTEM literal');  
         #$self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (219);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (220);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (221);  
         my $s = '';  
         $self->{read_until}->($s, q[>], 0);  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_STATE) {  
       ## NOTE: "CDATA section state" in the state is jointly implemented  
       ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,  
       ## and |CDATA_SECTION_MSE2_STATE|.  
         
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.1);  
         $self->{state} = CDATA_SECTION_MSE1_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.2);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.3);  
           ## No token to emit. $self->{ct} is discarded.  
         }          
         redo A;  
       } else {  
         !!!cp (221.4);  
         $self->{ct}->{data} .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{data},  
                               q<]>,  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
   
       ## ISSUE: "text tokens" in spec.  
     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {  
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.5);  
         $self->{state} = CDATA_SECTION_MSE2_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.6);  
         $self->{ct}->{data} .= ']';  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.7);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.8);  
           ## No token to emit. $self->{ct} is discarded.  
         }  
         redo A;  
       } elsif ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.9); # character  
         $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.11);  
         $self->{ct}->{data} .= ']]'; # character  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_STATE) {  
       if ($is_space->{$self->{nc}} or  
           {  
             0x003C => 1, 0x0026 => 1, -1 => 1, # <, &  
             $self->{entity_add} => 1,  
           }->{$self->{nc}}) {  
         !!!cp (1001);  
         ## Don't consume  
         ## No error  
         ## Return nothing.  
         #  
       } elsif ($self->{nc} == 0x0023) { # #  
         !!!cp (999);  
         $self->{state} = ENTITY_HASH_STATE;  
         $self->{s_kwd} = '#';  
         !!!next-input-character;  
         redo A;  
       } elsif ((0x0041 <= $self->{nc} and  
                 $self->{nc} <= 0x005A) or # A..Z  
                (0x0061 <= $self->{nc} and  
                 $self->{nc} <= 0x007A)) { # a..z  
         !!!cp (998);  
         require Whatpm::_NamedEntityList;  
         $self->{state} = ENTITY_NAME_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         $self->{entity__value} = $self->{s_kwd};  
         $self->{entity__match} = 0;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (1027);  
         !!!parse-error (type => 'bare ero');  
         ## Return nothing.  
         #  
       }  
   
       ## NOTE: No character is consumed by the "consume a character  
       ## reference" algorithm.  In other word, there is an "&" character  
       ## that does not introduce a character reference, which would be  
       ## appended to the parent element or the attribute value in later  
       ## process of the tokenizer.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (997);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } else {  
         !!!cp (996);  
         $self->{ca}->{value} .= '&';  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_HASH_STATE) {  
       if ($self->{nc} == 0x0078 or # x  
           $self->{nc} == 0x0058) { # X  
         !!!cp (995);  
         $self->{state} = HEXREF_X_STATE;  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0030 <= $self->{nc} and  
                $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (994);  
         $self->{state} = NCR_NUM_STATE;  
         $self->{s_kwd} = $self->{nc} - 0x0030;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare nero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" is appended to the parent element or the attribute  
         ## value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1019);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&#',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1,  
                    });  
           redo A;  
         } else {  
           !!!cp (993);  
           $self->{ca}->{value} .= '&#';  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == NCR_NUM_STATE) {  
       if (0x0030 <= $self->{nc} and  
           $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (1012);  
         $self->{s_kwd} *= 10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
           
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1013);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1014);  
         !!!parse-error (type => 'no refc');  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1015);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1016);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (992);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (991);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == HEXREF_X_STATE) {  
       if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or  
           (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or  
           (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {  
         # 0..9, A..F, a..f  
         !!!cp (990);  
         $self->{state} = HEXREF_HEX_STATE;  
         $self->{s_kwd} = 0;  
         ## Reconsume.  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare hcro',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" followed by "X" or "x" is appended to the parent  
         ## element or the attribute value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1005);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (989);  
           $self->{ca}->{value} .= '&' . $self->{s_kwd};  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == HEXREF_HEX_STATE) {  
       if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {  
         # 0..9  
         !!!cp (1002);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x0066) { # a..f  
         !!!cp (1003);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0060 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x0046) { # A..F  
         !!!cp (1004);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0040 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1006);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1007);  
         !!!parse-error (type => 'no refc',  
                         line => $self->{line},  
                         column => $self->{column});  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1008);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1009);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (988);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (987);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_NAME_STATE) {  
       if (length $self->{s_kwd} < 30 and  
           ## NOTE: Some number greater than the maximum length of entity name  
           ((0x0041 <= $self->{nc} and # a  
             $self->{nc} <= 0x005A) or # x  
            (0x0061 <= $self->{nc} and # a  
             $self->{nc} <= 0x007A) or # z  
            (0x0030 <= $self->{nc} and # 0  
             $self->{nc} <= 0x0039) or # 9  
            $self->{nc} == 0x003B)) { # ;  
         our $EntityChar;  
         $self->{s_kwd} .= chr $self->{nc};  
         if (defined $EntityChar->{$self->{s_kwd}}) {  
           if ($self->{nc} == 0x003B) { # ;  
             !!!cp (1020);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = 1;  
             !!!next-input-character;  
             #  
           } else {  
             !!!cp (1021);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = -1;  
             ## Stay in the state.  
             !!!next-input-character;  
             redo A;  
           }  
         } else {  
           !!!cp (1022);  
           $self->{entity__value} .= chr $self->{nc};  
           $self->{entity__match} *= 2;  
           ## Stay in the state.  
           !!!next-input-character;  
           redo A;  
         }  
       }  
   
       my $data;  
       my $has_ref;  
       if ($self->{entity__match} > 0) {  
         !!!cp (1023);  
         $data = $self->{entity__value};  
         $has_ref = 1;  
         #  
       } elsif ($self->{entity__match} < 0) {  
         !!!parse-error (type => 'no refc');  
         if ($self->{prev_state} != DATA_STATE and # in attribute  
             $self->{entity__match} < -1) {  
           !!!cp (1024);  
           $data = '&' . $self->{s_kwd};  
           #  
         } else {  
           !!!cp (1025);  
           $data = $self->{entity__value};  
           $has_ref = 1;  
           #  
         }  
       } else {  
         !!!cp (1026);  
         !!!parse-error (type => 'bare ero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - length $self->{s_kwd});  
         $data = '&' . $self->{s_kwd};  
         #  
       }  
     
       ## NOTE: In these cases, when a character reference is found,  
       ## it is consumed and a character token is returned, or, otherwise,  
       ## nothing is consumed and returned, according to the spec algorithm.  
       ## In this implementation, anything that has been examined by the  
       ## tokenizer is appended to the parent element or the attribute value  
       ## as string, either literal string when no character reference or  
       ## entity-replaced string otherwise, in this stage, since any characters  
       ## that would not be consumed are appended in the data state or in an  
       ## appropriate attribute value state anyway.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (986);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN,  
                   data => $data,  
                   line => $self->{line_prev},  
                   column => $self->{column_prev} + 1 - length $self->{s_kwd},  
                  });  
         redo A;  
       } else {  
         !!!cp (985);  
         $self->{ca}->{value} .= $data;  
         $self->{ca}->{has_reference} = 1 if $has_ref;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } else {  
       die "$0: $self->{state}: Unknown state";  
     }  
   } # A    
   
   die "$0: _get_next_token: unexpected case";  
 } # _get_next_token  
   
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 3451  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 3481  sub _tree_construction_initial ($) { Line 911  sub _tree_construction_initial ($) {
911    
912    INITIAL: {    INITIAL: {
913      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
914        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
915        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
916        ## language.        ## another language.  (We don't support such mode switchings; it
917          ## is nonsense to do anything different from what browsers do.)
918        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
919        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
920          my $doctype = $self->{document}->create_document_type_definition
921              ($doctype_name);
922    
923        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
924        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
925            defined $token->{sysid}) {            defined $token->{sysid}) {
# Line 3507  sub _tree_construction_initial ($) { Line 941  sub _tree_construction_initial ($) {
941          #          #
942        }        }
943                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
944        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
945        ## are empty strings, so that we don't set any value in missing cases.        ## are empty strings, so that we don't set any value in missing cases.
946        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
947        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
948    
949        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
950        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
951          ## string, while |null| is an allowed value for the attribute
952          ## according to DOM3 Core.
953        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
954                
955        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
# Line 3767  sub _tree_construction_root_element ($) Line 1202  sub _tree_construction_root_element ($)
1202      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
1203      !!!ack-later;      !!!ack-later;
1204      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
1205    } # B    } # B
1206    
1207    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3804  sub _reset_insertion_mode ($) { Line 1237  sub _reset_insertion_mode ($) {
1237          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1238          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1239          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1240        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1241          if ($last) {          if ($last) {
1242            !!!cp ('t28.2');            !!!cp ('t28.2');
1243            #            #
# Line 3833  sub _reset_insertion_mode ($) { Line 1266  sub _reset_insertion_mode ($) {
1266        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1267                
1268        ## Step 15        ## Step 15
1269        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1270          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1271            !!!cp ('t29');            !!!cp ('t29');
1272            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3965  sub _tree_construction_main ($) { Line 1398  sub _tree_construction_main ($) {
1398    
1399      ## Step 1      ## Step 1
1400      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1401      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1402    
1403      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1404      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1405      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1406    
1407      ## Step 4      ## Step 3, 4
1408      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);  
     }  
1409    
1410      ## 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";  
       }  
     }  
1411      !!!next-token;      !!!next-token;
1412    }; # $parse_rcdata    }; # $parse_rcdata
1413    
1414    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1415        ## Step 1
1416      my $script_el;      my $script_el;
1417      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1418    
1419        ## Step 2
1420      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1421    
1422        ## Step 3
1423        ## TODO: Mark as "already executed", if ...
1424    
1425        ## Step 4 (HTML5 revision 2702)
1426        $insert->($script_el);
1427        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1428    
1429        ## Step 5
1430      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1431      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;  
1432    
1433      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1434          $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  
1435    
1436        $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...  
     }  
       
1437      !!!next-token;      !!!next-token;
1438    }; # $script_start_tag    }; # $script_start_tag
1439    
1440    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1441    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
1442      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1443    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1444    
1445    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4153  sub _tree_construction_main ($) { Line 1524  sub _tree_construction_main ($) {
1524            !!!cp ('t59');            !!!cp ('t59');
1525            $furthest_block = $node;            $furthest_block = $node;
1526            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1527              ## NOTE: The topmost (eldest) node.
1528          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1529            !!!cp ('t60');            !!!cp ('t60');
1530            last OE;            last OE;
# Line 4239  sub _tree_construction_main ($) { Line 1611  sub _tree_construction_main ($) {
1611          my $foster_parent_element;          my $foster_parent_element;
1612          my $next_sibling;          my $next_sibling;
1613          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1614            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1615                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1616                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1617                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 4299  sub _tree_construction_main ($) { Line 1671  sub _tree_construction_main ($) {
1671            $i = $_;            $i = $_;
1672          }          }
1673        } # OE        } # OE
1674        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1675                
1676        ## Step 14        ## Step 14
1677        redo FET;        redo FET;
# Line 4317  sub _tree_construction_main ($) { Line 1689  sub _tree_construction_main ($) {
1689        my $foster_parent_element;        my $foster_parent_element;
1690        my $next_sibling;        my $next_sibling;
1691        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1692          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1693                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1694                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1695                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 4342  sub _tree_construction_main ($) { Line 1714  sub _tree_construction_main ($) {
1714      }      }
1715    }; # $insert_to_foster    }; # $insert_to_foster
1716    
1717      ## NOTE: Insert a character (MUST): When a character is inserted, if
1718      ## the last node that was inserted by the parser is a Text node and
1719      ## the character has to be inserted after that node, then the
1720      ## character is appended to the Text node.  However, if any other
1721      ## node is inserted by the parser, then a new Text node is created
1722      ## and the character is appended as that Text node.  If I'm not
1723      ## wrong, for a parser with scripting disabled, there are only two
1724      ## cases where this occurs.  One is the case where an element node
1725      ## is inserted to the |head| element.  This is covered by using the
1726      ## |$self->{head_element_inserted}| flag.  Another is the case where
1727      ## an element or comment is inserted into the |table| subtree while
1728      ## foster parenting happens.  This is covered by using the [2] flag
1729      ## of the |$open_tables| structure.  All other cases are handled
1730      ## simply by calling |manakai_append_text| method.
1731    
1732      ## TODO: |<body><script>document.write("a<br>");
1733      ## document.body.removeChild (document.body.lastChild);
1734      ## document.write ("b")</script>|
1735    
1736    B: while (1) {    B: while (1) {
1737      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1738        !!!cp ('t73');        !!!cp ('t73');
# Line 4389  sub _tree_construction_main ($) { Line 1780  sub _tree_construction_main ($) {
1780        } else {        } else {
1781          !!!cp ('t87');          !!!cp ('t87');
1782          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1783            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1784        }        }
1785        !!!next-token;        !!!next-token;
1786        next B;        next B;
1787        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1788          if ($token->{type} == CHARACTER_TOKEN) {
1789            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1790            delete $self->{ignore_newline};
1791    
1792            if (length $token->{data}) {
1793              !!!cp ('t43');
1794              $self->{open_elements}->[-1]->[0]->manakai_append_text
1795                  ($token->{data});
1796            } else {
1797              !!!cp ('t43.1');
1798            }
1799            !!!next-token;
1800            next B;
1801          } elsif ($token->{type} == END_TAG_TOKEN) {
1802            delete $self->{ignore_newline};
1803    
1804            if ($token->{tag_name} eq 'script') {
1805              !!!cp ('t50');
1806              
1807              ## Para 1-2
1808              my $script = pop @{$self->{open_elements}};
1809              
1810              ## Para 3
1811              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1812    
1813              ## Para 4
1814              ## TODO: $old_insertion_point = $current_insertion_point;
1815              ## TODO: $current_insertion_point = just before $self->{nc};
1816    
1817              ## Para 5
1818              ## TODO: Run the $script->[0].
1819    
1820              ## Para 6
1821              ## TODO: $current_insertion_point = $old_insertion_point;
1822    
1823              ## Para 7
1824              ## TODO: if ($pending_external_script) {
1825                ## TODO: ...
1826              ## TODO: }
1827    
1828              !!!next-token;
1829              next B;
1830            } else {
1831              !!!cp ('t42');
1832    
1833              pop @{$self->{open_elements}};
1834    
1835              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1836              !!!next-token;
1837              next B;
1838            }
1839          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1840            delete $self->{ignore_newline};
1841    
1842            !!!cp ('t44');
1843            !!!parse-error (type => 'not closed',
1844                            text => $self->{open_elements}->[-1]->[0]
1845                                ->manakai_local_name,
1846                            token => $token);
1847    
1848            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1849            #  ## TODO: Mark as "already executed"
1850            #}
1851    
1852            pop @{$self->{open_elements}};
1853    
1854            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1855            ## Reprocess.
1856            next B;
1857          } else {
1858            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1859          }
1860      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1861        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1862          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4403  sub _tree_construction_main ($) { Line 1868  sub _tree_construction_main ($) {
1868               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1869              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1870              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1871               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1872            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1873            !!!cp ('t87.2');            !!!cp ('t87.2');
1874            #            #
1875          } elsif ({          } elsif ({
1876                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1877                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1878                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1879                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1880                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1881                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1882                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1883                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1884                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1885                     ($token->{tag_name} eq 'font' and
1886                      ($token->{attributes}->{color} or
1887                       $token->{attributes}->{face} or
1888                       $token->{attributes}->{size}))) {
1889            !!!cp ('t87.2');            !!!cp ('t87.2');
1890            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1891                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4492  sub _tree_construction_main ($) { Line 1961  sub _tree_construction_main ($) {
1961          }          }
1962        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
1963          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
1964          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
1965          #            !!!cp ('t87.41');
1966              #
1967              ## XXXscript: Execute script here.
1968            } else {
1969              !!!cp ('t87.5');
1970              #
1971            }
1972        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1973          !!!cp ('t87.6');          !!!cp ('t87.6');
1974          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4504  sub _tree_construction_main ($) { Line 1979  sub _tree_construction_main ($) {
1979          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
1980              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
1981    
1982            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
1983    
1984          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
1985          ## Reprocess.          ## Reprocess.
1986          next B;          next B;
# Line 4516  sub _tree_construction_main ($) { Line 1993  sub _tree_construction_main ($) {
1993        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1994          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
1995            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
1996              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
1997              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
1998              #                $self->{open_elements}->[-1]->[0]->append_child
1999                    ($self->{document}->create_text_node ($1));
2000                  delete $self->{head_element_inserted};
2001                  ## NOTE: |</head> <link> |
2002                  #
2003                } else {
2004                  !!!cp ('t88.2');
2005                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2006                  ## NOTE: |</head> &#x20;|
2007                  #
2008                }
2009            } else {            } else {
2010              !!!cp ('t88.1');              !!!cp ('t88.1');
2011              ## Ignore the token.              ## Ignore the token.
# Line 4614  sub _tree_construction_main ($) { Line 2101  sub _tree_construction_main ($) {
2101            !!!cp ('t97');            !!!cp ('t97');
2102          }          }
2103    
2104              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2105                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2106                  !!!cp ('t98');              !!!cp ('t98');
2107                  ## As if </noscript>              ## As if </noscript>
2108                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2109                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2110                                  token => $token);                              token => $token);
2111                            
2112                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2113                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2114                } else {            } else {
2115                  !!!cp ('t99');              !!!cp ('t99');
2116                }            }
2117    
2118                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2119                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2120                  !!!cp ('t100');              !!!cp ('t100');
2121                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2122                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2123                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2124                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2125                } else {              $self->{head_element_inserted} = 1;
2126                  !!!cp ('t101');            } else {
2127                }              !!!cp ('t101');
2128                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2129                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2130                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2131                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2132                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2133                !!!next-token;            !!!nack ('t101.1');
2134                next B;            !!!next-token;
2135              } elsif ($token->{tag_name} eq 'link') {            next B;
2136                ## NOTE: There is a "as if in head" code clone.          } elsif ($token->{tag_name} eq 'link') {
2137                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            ## NOTE: There is a "as if in head" code clone.
2138                  !!!cp ('t102');            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2139                  !!!parse-error (type => 'after head',              !!!cp ('t102');
2140                                  text => $token->{tag_name}, token => $token);              !!!parse-error (type => 'after head',
2141                  push @{$self->{open_elements}},                              text => $token->{tag_name}, token => $token);
2142                      [$self->{head_element}, $el_category->{head}];              push @{$self->{open_elements}},
2143                } else {                  [$self->{head_element}, $el_category->{head}];
2144                  !!!cp ('t103');              $self->{head_element_inserted} = 1;
2145                }            } else {
2146                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!cp ('t103');
2147                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            }
2148                pop @{$self->{open_elements}} # <head>            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2149                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}};
2150                !!!ack ('t103.1');            pop @{$self->{open_elements}} # <head>
2151                !!!next-token;                if $self->{insertion_mode} == AFTER_HEAD_IM;
2152                next B;            !!!ack ('t103.1');
2153              } elsif ($token->{tag_name} eq 'meta') {            !!!next-token;
2154                ## NOTE: There is a "as if in head" code clone.            next B;
2155                if ($self->{insertion_mode} == AFTER_HEAD_IM) {          } elsif ($token->{tag_name} eq 'command' or
2156                  !!!cp ('t104');                   $token->{tag_name} eq 'eventsource') {
2157                  !!!parse-error (type => 'after head',            if ($self->{insertion_mode} == IN_HEAD_IM) {
2158                                  text => $token->{tag_name}, token => $token);              ## NOTE: If the insertion mode at the time of the emission
2159                  push @{$self->{open_elements}},              ## of the token was "before head", $self->{insertion_mode}
2160                      [$self->{head_element}, $el_category->{head}];              ## is already changed to |IN_HEAD_IM|.
2161                } else {  
2162                  !!!cp ('t105');              ## NOTE: There is a "as if in head" code clone.
2163                }              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2164                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              pop @{$self->{open_elements}};
2165                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              pop @{$self->{open_elements}} # <head>
2166                    if $self->{insertion_mode} == AFTER_HEAD_IM;
2167                !!!ack ('t103.2');
2168                !!!next-token;
2169                next B;
2170              } else {
2171                ## NOTE: "in head noscript" or "after head" insertion mode
2172                ## - in these cases, these tags are treated as same as
2173                ## normal in-body tags.
2174                !!!cp ('t103.3');
2175                #
2176              }
2177            } elsif ($token->{tag_name} eq 'meta') {
2178              ## NOTE: There is a "as if in head" code clone.
2179              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2180                !!!cp ('t104');
2181                !!!parse-error (type => 'after head',
2182                                text => $token->{tag_name}, token => $token);
2183                push @{$self->{open_elements}},
2184                    [$self->{head_element}, $el_category->{head}];
2185                $self->{head_element_inserted} = 1;
2186              } else {
2187                !!!cp ('t105');
2188              }
2189              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2190              my $meta_el = pop @{$self->{open_elements}};
2191    
2192                unless ($self->{confident}) {                unless ($self->{confident}) {
2193                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4733  sub _tree_construction_main ($) { Line 2245  sub _tree_construction_main ($) {
2245                !!!ack ('t110.1');                !!!ack ('t110.1');
2246                !!!next-token;                !!!next-token;
2247                next B;                next B;
2248              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2249                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2250                  !!!cp ('t111');              !!!cp ('t111');
2251                  ## As if </noscript>              ## As if </noscript>
2252                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2253                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2254                                  token => $token);                              token => $token);
2255                            
2256                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2257                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2258                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2259                  !!!cp ('t112');              !!!cp ('t112');
2260                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2261                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2262                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2263                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2264                } else {              $self->{head_element_inserted} = 1;
2265                  !!!cp ('t113');            } else {
2266                }              !!!cp ('t113');
2267              }
2268    
2269                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2270                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2271                    : $self->{open_elements}->[-1]->[0];  
2272                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2273                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2274                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2275                next B;            ## |head| element is inserted only for the purpose of
2276              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2277                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2278                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2279                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2280                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2281                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2282                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2283                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2284                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2285                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2286                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2287                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2288                  !!!cp ('t115');              !!!cp ('t114');
2289                }              !!!parse-error (type => 'after head',
2290                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2291                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2292                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2293                next B;              $self->{head_element_inserted} = 1;
2294              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2295                !!!cp ('t115');
2296              }
2297              $parse_rcdata->(CDATA_CONTENT_MODEL);
2298              ## ISSUE: A spec bug [Bug 6038]
2299              splice @{$self->{open_elements}}, -2, 1, () # <head>
2300                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2301              next B;
2302            } elsif ($token->{tag_name} eq 'noscript') {
2303                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2304                  !!!cp ('t116');                  !!!cp ('t116');
2305                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4799  sub _tree_construction_main ($) { Line 2320  sub _tree_construction_main ($) {
2320                  !!!cp ('t118');                  !!!cp ('t118');
2321                  #                  #
2322                }                }
2323              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2324                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2325                  !!!cp ('t119');              !!!cp ('t119');
2326                  ## As if </noscript>              ## As if </noscript>
2327                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2328                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2329                                  token => $token);                              token => $token);
2330                            
2331                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2332                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2333                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2334                  !!!cp ('t120');              !!!cp ('t120');
2335                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2336                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2337                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2338                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2339                } else {              $self->{head_element_inserted} = 1;
2340                  !!!cp ('t121');            } else {
2341                }              !!!cp ('t121');
2342              }
2343    
2344                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2345                $script_start_tag->();            $script_start_tag->();
2346                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2347                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2348                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2349              } elsif ($token->{tag_name} eq 'body' or            next B;
2350                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2351                     $token->{tag_name} eq 'frameset') {
2352                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2353                  !!!cp ('t122');                  !!!cp ('t122');
2354                  ## As if </noscript>                  ## As if </noscript>
# Line 4960  sub _tree_construction_main ($) { Line 2483  sub _tree_construction_main ($) {
2483              } elsif ({              } elsif ({
2484                        body => 1, html => 1,                        body => 1, html => 1,
2485                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2486                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2487                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2488                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2489                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2490                  !!!cp ('t140');                  !!!cp ('t140');
# Line 4986  sub _tree_construction_main ($) { Line 2510  sub _tree_construction_main ($) {
2510                ## Ignore the token                ## Ignore the token
2511                !!!next-token;                !!!next-token;
2512                next B;                next B;
2513              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2514                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2515                  !!!cp ('t142.2');              !!!cp ('t142.2');
2516                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2517                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2518                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2519                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2520        
2521                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2522                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2523                  !!!cp ('t143.2');              !!!cp ('t143.2');
2524                  ## As if </head>              ## As if </head>
2525                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2526                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2527        
2528                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2529                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2530                  !!!cp ('t143.3');              !!!cp ('t143.3');
2531                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2532                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2533                                  text => 'br', token => $token);                              text => 'br', token => $token);
2534                  ## As if </noscript>              ## As if </noscript>
2535                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2536                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2537    
2538                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2539                  ## As if </head>              ## As if </head>
2540                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2541                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2542    
2543                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2544                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2545                  !!!cp ('t143.4');              !!!cp ('t143.4');
2546                  #              #
2547                } else {            } else {
2548                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2549                }            }
2550    
2551                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2552                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2553                !!!cp ('t145');                !!!cp ('t145');
2554                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2555                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5074  sub _tree_construction_main ($) { Line 2593  sub _tree_construction_main ($) {
2593              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2594              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2595              ## reprocess              ## reprocess
2596              next B;          next B;
2597        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2598          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2599            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5132  sub _tree_construction_main ($) { Line 2651  sub _tree_construction_main ($) {
2651        } else {        } else {
2652          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2653        }        }
   
           ## ISSUE: An issue in the spec.  
2654      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2655            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
2656              !!!cp ('t150');              !!!cp ('t150');
# Line 5149  sub _tree_construction_main ($) { Line 2666  sub _tree_construction_main ($) {
2666                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2667                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2668                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2669                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2670                  ## have an element in table scope                  ## have an element in table scope
2671                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2672                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2673                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2674                      !!!cp ('t151');                      !!!cp ('t151');
2675    
2676                      ## Close the cell                      ## Close the cell
# Line 5177  sub _tree_construction_main ($) { Line 2694  sub _tree_construction_main ($) {
2694                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2695                  !!!next-token;                  !!!next-token;
2696                  next B;                  next B;
2697                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2698                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2699                                  token => $token);                                  token => $token);
2700                                    
# Line 5187  sub _tree_construction_main ($) { Line 2704  sub _tree_construction_main ($) {
2704                  INSCOPE: {                  INSCOPE: {
2705                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2706                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2707                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2708                        !!!cp ('t155');                        !!!cp ('t155');
2709                        $i = $_;                        $i = $_;
2710                        last INSCOPE;                        last INSCOPE;
# Line 5213  sub _tree_construction_main ($) { Line 2730  sub _tree_construction_main ($) {
2730                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2731                  }                  }
2732    
2733                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2734                    !!!cp ('t159');                    !!!cp ('t159');
2735                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2736                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5242  sub _tree_construction_main ($) { Line 2759  sub _tree_construction_main ($) {
2759              }              }
2760            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2761              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2762                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2763                  ## have an element in table scope                  ## have an element in table scope
2764                  my $i;                  my $i;
2765                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5292  sub _tree_construction_main ($) { Line 2809  sub _tree_construction_main ($) {
2809                                    
2810                  !!!next-token;                  !!!next-token;
2811                  next B;                  next B;
2812                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2813                  !!!cp ('t169');                  !!!cp ('t169');
2814                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2815                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5304  sub _tree_construction_main ($) { Line 2821  sub _tree_construction_main ($) {
2821                  #                  #
2822                }                }
2823              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2824                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2825                  ## have a table element in table scope                  ## have a table element in table scope
2826                  my $i;                  my $i;
2827                  INSCOPE: {                  INSCOPE: {
2828                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2829                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2830                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2831                        !!!cp ('t171');                        !!!cp ('t171');
2832                        $i = $_;                        $i = $_;
2833                        last INSCOPE;                        last INSCOPE;
# Line 5335  sub _tree_construction_main ($) { Line 2852  sub _tree_construction_main ($) {
2852                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2853                  }                  }
2854                                    
2855                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2856                    !!!cp ('t175');                    !!!cp ('t175');
2857                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2858                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5353  sub _tree_construction_main ($) { Line 2870  sub _tree_construction_main ($) {
2870                                    
2871                  !!!next-token;                  !!!next-token;
2872                  next B;                  next B;
2873                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2874                  !!!cp ('t177');                  !!!cp ('t177');
2875                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2876                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5368  sub _tree_construction_main ($) { Line 2885  sub _tree_construction_main ($) {
2885                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2886                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2887                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2888                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2889                ## have an element in table scope                ## have an element in table scope
2890                my $i;                my $i;
2891                my $tn;                my $tn;
# Line 5385  sub _tree_construction_main ($) { Line 2902  sub _tree_construction_main ($) {
2902                                line => $token->{line},                                line => $token->{line},
2903                                column => $token->{column}};                                column => $token->{column}};
2904                      next B;                      next B;
2905                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2906                      !!!cp ('t180');                      !!!cp ('t180');
2907                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2908                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5405  sub _tree_construction_main ($) { Line 2922  sub _tree_construction_main ($) {
2922                  next B;                  next B;
2923                } # INSCOPE                } # INSCOPE
2924              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2925                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2926                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2927                                token => $token);                                token => $token);
2928    
# Line 5414  sub _tree_construction_main ($) { Line 2931  sub _tree_construction_main ($) {
2931                my $i;                my $i;
2932                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2933                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
2934                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
2935                    !!!cp ('t184');                    !!!cp ('t184');
2936                    $i = $_;                    $i = $_;
2937                    last INSCOPE;                    last INSCOPE;
# Line 5425  sub _tree_construction_main ($) { Line 2942  sub _tree_construction_main ($) {
2942                } # INSCOPE                } # INSCOPE
2943                unless (defined $i) {                unless (defined $i) {
2944                  !!!cp ('t186');                  !!!cp ('t186');
2945            ## TODO: Wrong error type?
2946                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2947                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
2948                  ## Ignore the token                  ## Ignore the token
# Line 5438  sub _tree_construction_main ($) { Line 2956  sub _tree_construction_main ($) {
2956                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2957                }                }
2958    
2959                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2960                  !!!cp ('t188');                  !!!cp ('t188');
2961                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
2962                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5470  sub _tree_construction_main ($) { Line 2988  sub _tree_construction_main ($) {
2988                  !!!cp ('t191');                  !!!cp ('t191');
2989                  #                  #
2990                }                }
2991              } elsif ({          } elsif ({
2992                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
2993                        thead => 1, tr => 1,                    thead => 1, tr => 1,
2994                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
2995                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2996                !!!cp ('t192');            !!!cp ('t192');
2997                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
2998                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
2999                ## Ignore the token            ## Ignore the token
3000                !!!next-token;            !!!next-token;
3001                next B;            next B;
3002              } else {          } else {
3003                !!!cp ('t193');            !!!cp ('t193');
3004                #            #
3005              }          }
3006        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3007          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3008            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5519  sub _tree_construction_main ($) { Line 3037  sub _tree_construction_main ($) {
3037    
3038          !!!parse-error (type => 'in table:#text', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
3039    
3040              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
3041              ## 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);  
3042                            
3043              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
3044                # MUST            # MUST
3045                my $foster_parent_element;            my $foster_parent_element;
3046                my $next_sibling;            my $next_sibling;
3047                my $prev_sibling;            my $prev_sibling;
3048                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
3049                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
3050                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3051                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
3052                      !!!cp ('t196');                  $foster_parent_element = $parent;
3053                      $foster_parent_element = $parent;                  !!!cp ('t196');
3054                      $next_sibling = $self->{open_elements}->[$_]->[0];                  $next_sibling = $self->{open_elements}->[$_]->[0];
3055                      $prev_sibling = $next_sibling->previous_sibling;                  $prev_sibling = $next_sibling->previous_sibling;
3056                    } 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});  
3057                } else {                } else {
3058                  !!!cp ('t199');                  !!!cp ('t197');
3059                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3060                    ($self->{document}->create_text_node ($token->{data}),                  $prev_sibling = $foster_parent_element->last_child;
3061                     $next_sibling);                  #
3062                }                }
3063                  last OE;
3064                }
3065              } # OE
3066              $foster_parent_element = $self->{open_elements}->[0]->[0] and
3067              $prev_sibling = $foster_parent_element->last_child
3068                  unless defined $foster_parent_element;
3069              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
3070              if (defined $prev_sibling and
3071                  $prev_sibling->node_type == 3) {
3072                !!!cp ('t198');
3073                $prev_sibling->manakai_append_text ($token->{data});
3074              } else {
3075                !!!cp ('t199');
3076                $foster_parent_element->insert_before
3077                    ($self->{document}->create_text_node ($token->{data}),
3078                     $next_sibling);
3079              }
3080            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
3081              $open_tables->[-1]->[2] = 1; # ~node inserted
3082          } else {          } else {
3083              ## NOTE: Fragment case or in a foster parent'ed element
3084              ## (e.g. |<table><span>a|).  In fragment case, whether the
3085              ## character is appended to existing node or a new node is
3086              ## created is irrelevant, since the foster parent'ed nodes
3087              ## are discarded and fragment parsing does not invoke any
3088              ## script.
3089            !!!cp ('t200');            !!!cp ('t200');
3090            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text
3091                  ($token->{data});
3092          }          }
3093                            
3094          !!!next-token;          !!!next-token;
3095          next B;          next B;
3096        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3097          if ({          if ({
3098               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3099               th => 1, td => 1,               th => 1, td => 1,
3100              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3101            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3102              ## Clear back to table context              ## Clear back to table context
3103              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3104                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5585  sub _tree_construction_main ($) { Line 3111  sub _tree_construction_main ($) {
3111              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3112            }            }
3113                        
3114            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3115              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3116                !!!cp ('t202');                !!!cp ('t202');
3117                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5599  sub _tree_construction_main ($) { Line 3125  sub _tree_construction_main ($) {
3125                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3126              }              }
3127                                    
3128                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3129                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3130                    !!!cp ('t204');                !!!cp ('t204');
3131                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3132                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3133                    !!!next-token;                !!!nack ('t204');
3134                    next B;                !!!next-token;
3135                  } else {                next B;
3136                    !!!cp ('t205');              } else {
3137                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3138                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3139                  }                ## reprocess in the "in row" insertion mode
3140                } else {              }
3141                  !!!cp ('t206');            } else {
3142                }              !!!cp ('t206');
3143              }
3144    
3145                ## Clear back to table row context                ## Clear back to table row context
3146                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5622  sub _tree_construction_main ($) { Line 3149  sub _tree_construction_main ($) {
3149                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3150                }                }
3151                                
3152                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3153                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3154              $self->{insertion_mode} = IN_CELL_IM;
3155    
3156                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3157                                
3158                !!!nack ('t207.1');            !!!nack ('t207.1');
3159              !!!next-token;
3160              next B;
3161            } elsif ({
3162                      caption => 1, col => 1, colgroup => 1,
3163                      tbody => 1, tfoot => 1, thead => 1,
3164                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3165                     }->{$token->{tag_name}}) {
3166              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3167                ## As if </tr>
3168                ## have an element in table scope
3169                my $i;
3170                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3171                  my $node = $self->{open_elements}->[$_];
3172                  if ($node->[1] == TABLE_ROW_EL) {
3173                    !!!cp ('t208');
3174                    $i = $_;
3175                    last INSCOPE;
3176                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3177                    !!!cp ('t209');
3178                    last INSCOPE;
3179                  }
3180                } # INSCOPE
3181                unless (defined $i) {
3182                  !!!cp ('t210');
3183                  ## TODO: This type is wrong.
3184                  !!!parse-error (type => 'unmacthed end tag',
3185                                  text => $token->{tag_name}, token => $token);
3186                  ## Ignore the token
3187                  !!!nack ('t210.1');
3188                !!!next-token;                !!!next-token;
3189                next B;                next B;
3190              } 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;  
                 }  
3191                                    
3192                  ## Clear back to table row context                  ## Clear back to table row context
3193                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5682  sub _tree_construction_main ($) { Line 3210  sub _tree_construction_main ($) {
3210                  }                  }
3211                }                }
3212    
3213                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3214                  ## have an element in table scope                  ## have an element in table scope
3215                  my $i;                  my $i;
3216                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3217                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3218                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3219                      !!!cp ('t214');                      !!!cp ('t214');
3220                      $i = $_;                      $i = $_;
3221                      last INSCOPE;                      last INSCOPE;
# Line 5729  sub _tree_construction_main ($) { Line 3257  sub _tree_construction_main ($) {
3257                  !!!cp ('t218');                  !!!cp ('t218');
3258                }                }
3259    
3260                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3261                  ## Clear back to table context              ## Clear back to table context
3262                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3263                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3264                    !!!cp ('t219');                !!!cp ('t219');
3265                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3266                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3267                  }              }
3268                                
3269                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3270                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3271                  ## reprocess              ## reprocess
3272                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3273                  next B;              !!!ack-later;
3274                } elsif ({              next B;
3275                          caption => 1,            } elsif ({
3276                          colgroup => 1,                      caption => 1,
3277                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3278                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3279                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3280                ## Clear back to table context
3281                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3282                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3283                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5756  sub _tree_construction_main ($) { Line 3285  sub _tree_construction_main ($) {
3285                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3286                  }                  }
3287                                    
3288                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3289                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3290                                    
3291                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3292                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3293                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3294                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3295                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3296                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3297                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3298                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3299                  !!!next-token;                                        }->{$token->{tag_name}};
3300                  !!!nack ('t220.1');              !!!next-token;
3301                  next B;              !!!nack ('t220.1');
3302                } else {              next B;
3303                  die "$0: in table: <>: $token->{tag_name}";            } else {
3304                }              die "$0: in table: <>: $token->{tag_name}";
3305              }
3306              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3307                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3308                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5784  sub _tree_construction_main ($) { Line 3314  sub _tree_construction_main ($) {
3314                my $i;                my $i;
3315                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3316                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3317                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3318                    !!!cp ('t221');                    !!!cp ('t221');
3319                    $i = $_;                    $i = $_;
3320                    last INSCOPE;                    last INSCOPE;
# Line 5811  sub _tree_construction_main ($) { Line 3341  sub _tree_construction_main ($) {
3341                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3342                }                }
3343    
3344                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3345                  !!!cp ('t225');                  !!!cp ('t225');
3346                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3347                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5835  sub _tree_construction_main ($) { Line 3365  sub _tree_construction_main ($) {
3365              !!!cp ('t227.8');              !!!cp ('t227.8');
3366              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3367              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
3368                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3369              next B;              next B;
3370            } else {            } else {
3371              !!!cp ('t227.7');              !!!cp ('t227.7');
# Line 5845  sub _tree_construction_main ($) { Line 3376  sub _tree_construction_main ($) {
3376              !!!cp ('t227.6');              !!!cp ('t227.6');
3377              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3378              $script_start_tag->();              $script_start_tag->();
3379                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3380              next B;              next B;
3381            } else {            } else {
3382              !!!cp ('t227.5');              !!!cp ('t227.5');
# Line 5860  sub _tree_construction_main ($) { Line 3392  sub _tree_construction_main ($) {
3392                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
3393    
3394                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3395                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3396    
3397                  ## TODO: form element pointer                  ## TODO: form element pointer
3398    
# Line 5891  sub _tree_construction_main ($) { Line 3424  sub _tree_construction_main ($) {
3424          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3425          #          #
3426        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3427              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3428                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3429                ## have an element in table scope            ## have an element in table scope
3430                my $i;                my $i;
3431                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3432                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3433                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3434                    !!!cp ('t228');                    !!!cp ('t228');
3435                    $i = $_;                    $i = $_;
3436                    last INSCOPE;                    last INSCOPE;
# Line 5932  sub _tree_construction_main ($) { Line 3465  sub _tree_construction_main ($) {
3465                !!!nack ('t231.1');                !!!nack ('t231.1');
3466                next B;                next B;
3467              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3468                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3469                  ## As if </tr>                  ## As if </tr>
3470                  ## have an element in table scope                  ## have an element in table scope
3471                  my $i;                  my $i;
3472                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3473                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3474                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3475                      !!!cp ('t233');                      !!!cp ('t233');
3476                      $i = $_;                      $i = $_;
3477                      last INSCOPE;                      last INSCOPE;
# Line 5971  sub _tree_construction_main ($) { Line 3504  sub _tree_construction_main ($) {
3504                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3505                }                }
3506    
3507                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3508                  ## have an element in table scope                  ## have an element in table scope
3509                  my $i;                  my $i;
3510                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3511                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3512                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3513                      !!!cp ('t237');                      !!!cp ('t237');
3514                      $i = $_;                      $i = $_;
3515                      last INSCOPE;                      last INSCOPE;
# Line 6023  sub _tree_construction_main ($) { Line 3556  sub _tree_construction_main ($) {
3556                my $i;                my $i;
3557                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3558                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3559                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3560                    !!!cp ('t241');                    !!!cp ('t241');
3561                    $i = $_;                    $i = $_;
3562                    last INSCOPE;                    last INSCOPE;
# Line 6053  sub _tree_construction_main ($) { Line 3586  sub _tree_construction_main ($) {
3586                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3587                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3588                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3589                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3590                  ## have an element in table scope                  ## have an element in table scope
3591                  my $i;                  my $i;
3592                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6082  sub _tree_construction_main ($) { Line 3615  sub _tree_construction_main ($) {
3615                  my $i;                  my $i;
3616                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3617                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3618                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3619                      !!!cp ('t250');                      !!!cp ('t250');
3620                      $i = $_;                      $i = $_;
3621                      last INSCOPE;                      last INSCOPE;
# Line 6172  sub _tree_construction_main ($) { Line 3705  sub _tree_construction_main ($) {
3705            #            #
3706          }          }
3707        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3708          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3709                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3710            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3711            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6187  sub _tree_construction_main ($) { Line 3720  sub _tree_construction_main ($) {
3720        } else {        } else {
3721          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3722        }        }
3723      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3724            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3725              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3726                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6214  sub _tree_construction_main ($) { Line 3747  sub _tree_construction_main ($) {
3747              }              }
3748            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3749              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3750                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3751                  !!!cp ('t264');                  !!!cp ('t264');
3752                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3753                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6240  sub _tree_construction_main ($) { Line 3773  sub _tree_construction_main ($) {
3773                #                #
3774              }              }
3775        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3776          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3777              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3778            !!!cp ('t270.2');            !!!cp ('t270.2');
3779            ## Stop parsing.            ## Stop parsing.
# Line 6258  sub _tree_construction_main ($) { Line 3791  sub _tree_construction_main ($) {
3791        }        }
3792    
3793            ## As if </colgroup>            ## As if </colgroup>
3794            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3795              !!!cp ('t269');              !!!cp ('t269');
3796  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3797              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6283  sub _tree_construction_main ($) { Line 3816  sub _tree_construction_main ($) {
3816          next B;          next B;
3817        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3818          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3819            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3820              !!!cp ('t272');              !!!cp ('t272');
3821              ## As if </option>              ## As if </option>
3822              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6296  sub _tree_construction_main ($) { Line 3829  sub _tree_construction_main ($) {
3829            !!!next-token;            !!!next-token;
3830            next B;            next B;
3831          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3832            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3833              !!!cp ('t274');              !!!cp ('t274');
3834              ## As if </option>              ## As if </option>
3835              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6304  sub _tree_construction_main ($) { Line 3837  sub _tree_construction_main ($) {
3837              !!!cp ('t275');              !!!cp ('t275');
3838            }            }
3839    
3840            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3841              !!!cp ('t276');              !!!cp ('t276');
3842              ## As if </optgroup>              ## As if </optgroup>
3843              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6317  sub _tree_construction_main ($) { Line 3850  sub _tree_construction_main ($) {
3850            !!!next-token;            !!!next-token;
3851            next B;            next B;
3852          } elsif ({          } elsif ({
3853                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3854                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3855                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3856                          == IN_SELECT_IN_TABLE_IM and
3857                    {                    {
3858                     caption => 1, table => 1,                     caption => 1, table => 1,
3859                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3860                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3861                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3862            ## TODO: The type below is not good - <select> is replaced by </select>  
3863            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3864                            token => $token);            if ($token->{tag_name} eq 'select') {
3865            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3866            ## as if there were </select> (otherwise).                                token => $token);
3867            ## have an element in table scope            } else {
3868                !!!parse-error (type => 'not closed', text => 'select',
3869                                token => $token);
3870              }
3871    
3872              ## 2./<select>-1. Unless "have an element in table scope" (select):
3873            my $i;            my $i;
3874            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3875              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3876              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3877                !!!cp ('t278');                !!!cp ('t278');
3878                $i = $_;                $i = $_;
3879                last INSCOPE;                last INSCOPE;
# Line 6345  sub _tree_construction_main ($) { Line 3884  sub _tree_construction_main ($) {
3884            } # INSCOPE            } # INSCOPE
3885            unless (defined $i) {            unless (defined $i) {
3886              !!!cp ('t280');              !!!cp ('t280');
3887              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3888                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3889              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3890                  ## case two errors, "select in select" and "unmatched
3891                  ## end tags" are reported to the user, the latter might
3892                  ## be confusing but this is what the spec requires.
3893                  !!!parse-error (type => 'unmatched end tag',
3894                                  text => 'select',
3895                                  token => $token);
3896                }
3897                ## Ignore the token.
3898              !!!nack ('t280.1');              !!!nack ('t280.1');
3899              !!!next-token;              !!!next-token;
3900              next B;              next B;
3901            }            }
3902    
3903              ## 3. Otherwise, as if there were <select>:
3904                                
3905            !!!cp ('t281');            !!!cp ('t281');
3906            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6368  sub _tree_construction_main ($) { Line 3917  sub _tree_construction_main ($) {
3917              ## Reprocess the token.              ## Reprocess the token.
3918              next B;              next B;
3919            }            }
3920            } elsif ($token->{tag_name} eq 'script') {
3921              !!!cp ('t281.3');
3922              ## NOTE: This is an "as if in head" code clone
3923              $script_start_tag->();
3924              next B;
3925          } else {          } else {
3926            !!!cp ('t282');            !!!cp ('t282');
3927            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6379  sub _tree_construction_main ($) { Line 3933  sub _tree_construction_main ($) {
3933          }          }
3934        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3935          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3936            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3937                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3938              !!!cp ('t283');              !!!cp ('t283');
3939              ## As if </option>              ## As if </option>
3940              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3941            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3942              !!!cp ('t284');              !!!cp ('t284');
3943              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3944            } else {            } else {
# Line 6397  sub _tree_construction_main ($) { Line 3951  sub _tree_construction_main ($) {
3951            !!!next-token;            !!!next-token;
3952            next B;            next B;
3953          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3954            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3955              !!!cp ('t286');              !!!cp ('t286');
3956              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3957            } else {            } else {
# Line 6414  sub _tree_construction_main ($) { Line 3968  sub _tree_construction_main ($) {
3968            my $i;            my $i;
3969            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3970              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3971              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3972                !!!cp ('t288');                !!!cp ('t288');
3973                $i = $_;                $i = $_;
3974                last INSCOPE;                last INSCOPE;
# Line 6441  sub _tree_construction_main ($) { Line 3995  sub _tree_construction_main ($) {
3995            !!!nack ('t291.1');            !!!nack ('t291.1');
3996            !!!next-token;            !!!next-token;
3997            next B;            next B;
3998          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3999                         == IN_SELECT_IN_TABLE_IM and
4000                   {                   {
4001                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
4002                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6476  sub _tree_construction_main ($) { Line 4031  sub _tree_construction_main ($) {
4031            undef $i;            undef $i;
4032            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4033              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4034              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4035                !!!cp ('t295');                !!!cp ('t295');
4036                $i = $_;                $i = $_;
4037                last INSCOPE;                last INSCOPE;
# Line 6515  sub _tree_construction_main ($) { Line 4070  sub _tree_construction_main ($) {
4070            next B;            next B;
4071          }          }
4072        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4073          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4074                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4075            !!!cp ('t299.1');            !!!cp ('t299.1');
4076            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6702  sub _tree_construction_main ($) { Line 4257  sub _tree_construction_main ($) {
4257        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4258          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4259              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4260            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4261                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4262              !!!cp ('t325');              !!!cp ('t325');
4263              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6716  sub _tree_construction_main ($) { Line 4271  sub _tree_construction_main ($) {
4271            }            }
4272    
4273            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4274                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4275              !!!cp ('t327');              !!!cp ('t327');
4276              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4277            } else {            } else {
# Line 6748  sub _tree_construction_main ($) { Line 4303  sub _tree_construction_main ($) {
4303            next B;            next B;
4304          }          }
4305        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4306          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4307                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4308            !!!cp ('t331.1');            !!!cp ('t331.1');
4309            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6761  sub _tree_construction_main ($) { Line 4316  sub _tree_construction_main ($) {
4316        } else {        } else {
4317          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4318        }        }
   
       ## ISSUE: An issue in spec here  
4319      } else {      } else {
4320        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4321      }      }
# Line 6780  sub _tree_construction_main ($) { Line 4333  sub _tree_construction_main ($) {
4333          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4334          next B;          next B;
4335        } elsif ({        } elsif ({
4336                  base => 1, link => 1,                  base => 1, command => 1, eventsource => 1, link => 1,
4337                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4338          !!!cp ('t334');          !!!cp ('t334');
4339          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
4340          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4341          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          pop @{$self->{open_elements}};
4342          !!!ack ('t334.1');          !!!ack ('t334.1');
4343          !!!next-token;          !!!next-token;
4344          next B;          next B;
4345        } elsif ($token->{tag_name} eq 'meta') {        } elsif ($token->{tag_name} eq 'meta') {
4346          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
4347          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4348          my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          my $meta_el = pop @{$self->{open_elements}};
4349    
4350          unless ($self->{confident}) {          unless ($self->{confident}) {
4351            if ($token->{attributes}->{charset}) {            if ($token->{attributes}->{charset}) {
# Line 6853  sub _tree_construction_main ($) { Line 4406  sub _tree_construction_main ($) {
4406          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4407                                
4408          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4409              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4410            !!!cp ('t342');            !!!cp ('t342');
4411            ## Ignore the token            ## Ignore the token
4412          } else {          } else {
# Line 6871  sub _tree_construction_main ($) { Line 4424  sub _tree_construction_main ($) {
4424          !!!next-token;          !!!next-token;
4425          next B;          next B;
4426        } elsif ({        } elsif ({
4427                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
4428                  div => 1, dl => 1, fieldset => 1,  
4429                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
4430                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
4431                    center => 1, datagrid => 1, details => 1, dialog => 1,
4432                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
4433                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
4434                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
4435                    section => 1, ul => 1,
4436                    ## NOTE: As normal, but drops leading newline
4437                  pre => 1, listing => 1,                  pre => 1, listing => 1,
4438                    ## NOTE: As normal, but interacts with the form element pointer
4439                  form => 1,                  form => 1,
4440                    
4441                  table => 1,                  table => 1,
4442                  hr => 1,                  hr => 1,
4443                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4444    
4445            ## 1. When there is an opening |form| element:
4446          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4447            !!!cp ('t350');            !!!cp ('t350');
4448            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6889  sub _tree_construction_main ($) { Line 4452  sub _tree_construction_main ($) {
4452            next B;            next B;
4453          }          }
4454    
4455          ## has a p element in scope          ## 2. Close the |p| element, if any.
4456          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4457            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4458              !!!cp ('t344');            ## has a p element in scope
4459              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4460              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4461                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4462              next B;                !!!back-token; # <form>
4463            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4464              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4465              last INSCOPE;                next B;
4466                } elsif ($_->[1] & SCOPING_EL) {
4467                  !!!cp ('t345');
4468                  last INSCOPE;
4469                }
4470              } # INSCOPE
4471            }
4472    
4473            ## 3. Close the opening <hn> element, if any.
4474            if ({h1 => 1, h2 => 1, h3 => 1,
4475                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4476              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4477                !!!parse-error (type => 'not closed',
4478                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4479                                token => $token);
4480                pop @{$self->{open_elements}};
4481            }            }
4482          } # INSCOPE          }
4483              
4484            ## 4. Insertion.
4485          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4486          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4487            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6943  sub _tree_construction_main ($) { Line 4522  sub _tree_construction_main ($) {
4522            !!!next-token;            !!!next-token;
4523          }          }
4524          next B;          next B;
4525        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
4526          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
4527    
4528            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4529              ## Interpreted as <li><foo/></li><li/> (non-conforming):
4530              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4531              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
4532              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4533              ## object (Fx)
4534              ## Generate non-tree (non-conforming):
4535              ## basefont (IE7 (where basefont is non-void)), center (IE),
4536              ## form (IE), hn (IE)
4537            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4538              ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4539              ## div (Fx, S)
4540    
4541            my $non_optional;
4542            my $i = -1;
4543    
4544            ## 1.
4545            for my $node (reverse @{$self->{open_elements}}) {
4546              if ($node->[1] == LI_EL) {
4547                ## 2. (a) As if </li>
4548                {
4549                  ## If no </li> - not applied
4550                  #
4551    
4552                  ## Otherwise
4553    
4554                  ## 1. generate implied end tags, except for </li>
4555                  #
4556    
4557                  ## 2. If current node != "li", parse error
4558                  if ($non_optional) {
4559                    !!!parse-error (type => 'not closed',
4560                                    text => $non_optional->[0]->manakai_local_name,
4561                                    token => $token);
4562                    !!!cp ('t355');
4563                  } else {
4564                    !!!cp ('t356');
4565                  }
4566    
4567                  ## 3. Pop
4568                  splice @{$self->{open_elements}}, $i;
4569                }
4570    
4571                last; ## 2. (b) goto 5.
4572              } elsif (
4573                       ## NOTE: not "formatting" and not "phrasing"
4574                       ($node->[1] & SPECIAL_EL or
4575                        $node->[1] & SCOPING_EL) and
4576                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4577                       (not $node->[1] & ADDRESS_DIV_P_EL)
4578                      ) {
4579                ## 3.
4580                !!!cp ('t357');
4581                last; ## goto 5.
4582              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4583                !!!cp ('t358');
4584                #
4585              } else {
4586                !!!cp ('t359');
4587                $non_optional ||= $node;
4588                #
4589              }
4590              ## 4.
4591              ## goto 2.
4592              $i--;
4593            }
4594    
4595            ## 5. (a) has a |p| element in scope
4596          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4597            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4598              !!!cp ('t353');              !!!cp ('t353');
4599    
4600                ## NOTE: |<p><li>|, for example.
4601    
4602              !!!back-token; # <x>              !!!back-token; # <x>
4603              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
4604                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6957  sub _tree_construction_main ($) { Line 4608  sub _tree_construction_main ($) {
4608              last INSCOPE;              last INSCOPE;
4609            }            }
4610          } # INSCOPE          } # INSCOPE
4611              
4612          ## Step 1          ## 5. (b) insert
4613            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4614            !!!nack ('t359.1');
4615            !!!next-token;
4616            next B;
4617          } elsif ($token->{tag_name} eq 'dt' or
4618                   $token->{tag_name} eq 'dd') {
4619            ## NOTE: As normal, but imply </dt> or </dd> when ...
4620    
4621            my $non_optional;
4622          my $i = -1;          my $i = -1;
4623          my $node = $self->{open_elements}->[$i];  
4624          my $li_or_dtdd = {li => {li => 1},          ## 1.
4625                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
4626                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] == DTDD_EL) {
4627          LI: {              ## 2. (a) As if </li>
4628            ## Step 2              {
4629            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
4630              if ($i != -1) {                #
4631                !!!cp ('t355');  
4632                !!!parse-error (type => 'not closed',                ## Otherwise
4633                                text => $self->{open_elements}->[-1]->[0]  
4634                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
4635                                token => $token);                #
4636              } else {  
4637                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
4638                  if ($non_optional) {
4639                    !!!parse-error (type => 'not closed',
4640                                    text => $non_optional->[0]->manakai_local_name,
4641                                    token => $token);
4642                    !!!cp ('t355.1');
4643                  } else {
4644                    !!!cp ('t356.1');
4645                  }
4646    
4647                  ## 3. Pop
4648                  splice @{$self->{open_elements}}, $i;
4649              }              }
4650              splice @{$self->{open_elements}}, $i;  
4651              last LI;              last; ## 2. (b) goto 5.
4652              } elsif (
4653                       ## NOTE: not "formatting" and not "phrasing"
4654                       ($node->[1] & SPECIAL_EL or
4655                        $node->[1] & SCOPING_EL) and
4656                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4657    
4658                       (not $node->[1] & ADDRESS_DIV_P_EL)
4659                      ) {
4660                ## 3.
4661                !!!cp ('t357.1');
4662                last; ## goto 5.
4663              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4664                !!!cp ('t358.1');
4665                #
4666            } else {            } else {
4667              !!!cp ('t357');              !!!cp ('t359.1');
4668            }              $non_optional ||= $node;
4669                          #
           ## Step 3  
           if (not ($node->[1] & FORMATTING_EL) and  
               #not $phrasing_category->{$node->[1]} and  
               ($node->[1] & SPECIAL_EL or  
                $node->[1] & SCOPING_EL) and  
               not ($node->[1] & ADDRESS_EL) and  
               not ($node->[1] & DIV_EL)) {  
             !!!cp ('t358');  
             last LI;  
4670            }            }
4671                        ## 4.
4672            !!!cp ('t359');            ## goto 2.
           ## Step 4  
4673            $i--;            $i--;
4674            $node = $self->{open_elements}->[$i];          }
4675            redo LI;  
4676          } # LI          ## 5. (a) has a |p| element in scope
4677                      INSCOPE: for (reverse @{$self->{open_elements}}) {
4678              if ($_->[1] == P_EL) {
4679                !!!cp ('t353.1');
4680                !!!back-token; # <x>
4681                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4682                          line => $token->{line}, column => $token->{column}};
4683                next B;
4684              } elsif ($_->[1] & SCOPING_EL) {
4685                !!!cp ('t354.1');
4686                last INSCOPE;
4687              }
4688            } # INSCOPE
4689    
4690            ## 5. (b) insert
4691          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4692          !!!nack ('t359.1');          !!!nack ('t359.2');
4693          !!!next-token;          !!!next-token;
4694          next B;          next B;
4695        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
4696            ## NOTE: As normal, but effectively ends parsing
4697    
4698          ## has a p element in scope          ## has a p element in scope
4699          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4700            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4701              !!!cp ('t367');              !!!cp ('t367');
4702              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4703              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7029  sub _tree_construction_main ($) { Line 4719  sub _tree_construction_main ($) {
4719        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4720          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4721            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4722            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4723              !!!cp ('t371');              !!!cp ('t371');
4724              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4725                            
# Line 7073  sub _tree_construction_main ($) { Line 4763  sub _tree_construction_main ($) {
4763          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4764          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4765            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4766            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4767              !!!cp ('t376');              !!!cp ('t376');
4768              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4769              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7096  sub _tree_construction_main ($) { Line 4786  sub _tree_construction_main ($) {
4786          ## has a button element in scope          ## has a button element in scope
4787          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4788            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4789            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4790              !!!cp ('t378');              !!!cp ('t378');
4791              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4792              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7161  sub _tree_construction_main ($) { Line 4851  sub _tree_construction_main ($) {
4851                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4852                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4853                           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}},  
4854                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4855                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4856                         );                         );
# Line 7185  sub _tree_construction_main ($) { Line 4873  sub _tree_construction_main ($) {
4873                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4874                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4875                           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}},  
4876                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4877                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4878                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7196  sub _tree_construction_main ($) { Line 4882  sub _tree_construction_main ($) {
4882            next B;            next B;
4883          }          }
4884        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4885          my $tag_name = $token->{tag_name};          ## 1. Insert
4886          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4887                    
4888            ## Step 2 # XXX
4889          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4890    
4891            ## 2. Drop U+000A LINE FEED
4892            $self->{ignore_newline} = 1;
4893    
4894            ## 3. RCDATA
4895          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4896          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4897            
4898          $insert->($el);          ## 4., 6. Insertion mode
4899                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4900          my $text = '';  
4901            ## XXX: 5. frameset-ok flag
4902    
4903          !!!nack ('t392.1');          !!!nack ('t392.1');
4904          !!!next-token;          !!!next-token;
4905          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4906            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4907            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4908              !!!cp ('t392');          ## has an |option| element in scope
4909              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4910            } else {            my $node = $self->{open_elements}->[$_];
4911              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4912                !!!cp ('t397.1');
4913                ## NOTE: As if </option>
4914                !!!back-token; # <option> or <optgroup>
4915                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4916                          line => $token->{line}, column => $token->{column}};
4917                next B;
4918              } elsif ($node->[1] & SCOPING_EL) {
4919                !!!cp ('t397.2');
4920                last INSCOPE;
4921            }            }
4922          } else {          } # INSCOPE
4923            !!!cp ('t394');  
4924          }          $reconstruct_active_formatting_elements->($insert_to_current);
4925          while ($token->{type} == CHARACTER_TOKEN) {  
4926            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4927            $text .= $token->{data};  
4928            !!!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);  
         }  
4929          !!!next-token;          !!!next-token;
4930          next B;          redo B;
4931        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4932                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4933          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4934          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4935            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4936            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4937              !!!cp ('t398.1');              !!!cp ('t398.1');
4938              ## generate implied end tags              ## generate implied end tags
4939              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4940                !!!cp ('t398.2');                !!!cp ('t398.2');
4941                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4942              }              }
4943              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4944                !!!cp ('t398.3');                !!!cp ('t398.3');
4945                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4946                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4947                                    ->manakai_local_name,                                    ->manakai_local_name,
4948                                token => $token);                                token => $token);
4949                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4950                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4951              }              }
4952              last INSCOPE;              last INSCOPE;
4953            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7269  sub _tree_construction_main ($) { Line 4955  sub _tree_construction_main ($) {
4955              last INSCOPE;              last INSCOPE;
4956            }            }
4957          } # INSCOPE          } # INSCOPE
4958              
4959            ## TODO: <non-ruby><rt> is not allowed.
4960    
4961          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4962    
# Line 7289  sub _tree_construction_main ($) { Line 4977  sub _tree_construction_main ($) {
4977                    
4978          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4979            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4980            !!!ack ('t398.1');            !!!ack ('t398.6');
4981          } else {          } else {
4982            !!!cp ('t398.2');            !!!cp ('t398.7');
4983            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
4984            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
4985            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7302  sub _tree_construction_main ($) { Line 4990  sub _tree_construction_main ($) {
4990          next B;          next B;
4991        } elsif ({        } elsif ({
4992                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4993                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
4994                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
4995                  thead => 1, tr => 1,                  thead => 1, tr => 1,
4996                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7313  sub _tree_construction_main ($) { Line 5001  sub _tree_construction_main ($) {
5001          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
5002          !!!next-token;          !!!next-token;
5003          next B;          next B;
5004                  } elsif ($token->{tag_name} eq 'param' or
5005          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
5006            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5007            pop @{$self->{open_elements}};
5008    
5009            !!!ack ('t398.5');
5010            !!!next-token;
5011            redo B;
5012        } else {        } else {
5013          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
5014            !!!cp ('t384');            !!!cp ('t384');
# Line 7337  sub _tree_construction_main ($) { Line 5031  sub _tree_construction_main ($) {
5031            !!!nack ('t380.1');            !!!nack ('t380.1');
5032          } elsif ({          } elsif ({
5033                    b => 1, big => 1, em => 1, font => 1, i => 1,                    b => 1, big => 1, em => 1, font => 1, i => 1,
5034                    s => 1, small => 1, strile => 1,                    s => 1, small => 1, strike => 1,
5035                    strong => 1, tt => 1, u => 1,                    strong => 1, tt => 1, u => 1,
5036                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5037            !!!cp ('t375');            !!!cp ('t375');
# Line 7350  sub _tree_construction_main ($) { Line 5044  sub _tree_construction_main ($) {
5044            !!!ack ('t388.2');            !!!ack ('t388.2');
5045          } elsif ({          } elsif ({
5046                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5047                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
5048                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5049            !!!cp ('t388.1');            !!!cp ('t388.1');
5050            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7361  sub _tree_construction_main ($) { Line 5054  sub _tree_construction_main ($) {
5054                    
5055            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5056                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5057                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5058              !!!cp ('t400.1');              !!!cp ('t400.1');
5059              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5060            } else {            } else {
# Line 7378  sub _tree_construction_main ($) { Line 5071  sub _tree_construction_main ($) {
5071        }        }
5072      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5073        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5074          ## has a |body| element in scope  
5075            ## 1. If not "have an element in scope":
5076            ## "has a |body| element in scope"
5077          my $i;          my $i;
5078          INSCOPE: {          INSCOPE: {
5079            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5080              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5081                !!!cp ('t405');                !!!cp ('t405');
5082                $i = $_;                $i = $_;
5083                last INSCOPE;                last INSCOPE;
# Line 7392  sub _tree_construction_main ($) { Line 5087  sub _tree_construction_main ($) {
5087              }              }
5088            }            }
5089    
5090            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
5091    
5092              !!!parse-error (type => 'unmatched end tag',
5093                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5094            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
5095            !!!next-token;            !!!next-token;
5096            next B;            next B;
5097          } # INSCOPE          } # INSCOPE
5098    
5099            ## 2. If unclosed elements:
5100          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5101            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5102                      $_->[1] == OPTGROUP_EL ||
5103                      $_->[1] == OPTION_EL ||
5104                      $_->[1] == RUBY_COMPONENT_EL) {
5105              !!!cp ('t403');              !!!cp ('t403');
5106              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5107                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7411  sub _tree_construction_main ($) { Line 5112  sub _tree_construction_main ($) {
5112            }            }
5113          }          }
5114    
5115            ## 3. Switch the insertion mode.
5116          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5117          !!!next-token;          !!!next-token;
5118          next B;          next B;
# Line 7418  sub _tree_construction_main ($) { Line 5120  sub _tree_construction_main ($) {
5120          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5121          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5122          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5123              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5124            ## ISSUE: There is an issue in the spec.            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
5125              !!!cp ('t406');              !!!cp ('t406');
5126              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5127                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7441  sub _tree_construction_main ($) { Line 5142  sub _tree_construction_main ($) {
5142            next B;            next B;
5143          }          }
5144        } elsif ({        } elsif ({
5145                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
5146                  div => 1, dl => 1, fieldset => 1, listing => 1,  
5147                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
5148                    address => 1, article => 1, aside => 1, blockquote => 1,
5149                    center => 1, datagrid => 1, details => 1, dialog => 1,
5150                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
5151                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
5152                    ol => 1, pre => 1, section => 1, ul => 1,
5153    
5154                    ## NOTE: As normal, but ... optional tags
5155                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
5156    
5157                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
5158                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5159            ## NOTE: Code for <li> start tags includes "as if </li>" code.
5160            ## Code for <dt> or <dd> start tags includes "as if </dt> or
5161            ## </dd>" code.
5162    
5163          ## has an element in scope          ## has an element in scope
5164          my $i;          my $i;
5165          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 7473  sub _tree_construction_main ($) { Line 5186  sub _tree_construction_main ($) {
5186                    dd => ($token->{tag_name} ne 'dd'),                    dd => ($token->{tag_name} ne 'dd'),
5187                    dt => ($token->{tag_name} ne 'dt'),                    dt => ($token->{tag_name} ne 'dt'),
5188                    li => ($token->{tag_name} ne 'li'),                    li => ($token->{tag_name} ne 'li'),
5189                      option => 1,
5190                      optgroup => 1,
5191                    p => 1,                    p => 1,
5192                    rt => 1,                    rt => 1,
5193                    rp => 1,                    rp => 1,
# Line 7505  sub _tree_construction_main ($) { Line 5220  sub _tree_construction_main ($) {
5220          !!!next-token;          !!!next-token;
5221          next B;          next B;
5222        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
5223            ## NOTE: As normal, but interacts with the form element pointer
5224    
5225          undef $self->{form_element};          undef $self->{form_element};
5226    
5227          ## has an element in scope          ## has an element in scope
5228          my $i;          my $i;
5229          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5230            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5231            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5232              !!!cp ('t418');              !!!cp ('t418');
5233              $i = $_;              $i = $_;
5234              last INSCOPE;              last INSCOPE;
# Line 7552  sub _tree_construction_main ($) { Line 5269  sub _tree_construction_main ($) {
5269          !!!next-token;          !!!next-token;
5270          next B;          next B;
5271        } elsif ({        } elsif ({
5272                    ## NOTE: As normal, except acts as a closer for any ...
5273                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5274                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5275          ## has an element in scope          ## has an element in scope
5276          my $i;          my $i;
5277          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5278            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5279            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5280              !!!cp ('t423');              !!!cp ('t423');
5281              $i = $_;              $i = $_;
5282              last INSCOPE;              last INSCOPE;
# Line 7597  sub _tree_construction_main ($) { Line 5315  sub _tree_construction_main ($) {
5315          !!!next-token;          !!!next-token;
5316          next B;          next B;
5317        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
5318            ## NOTE: As normal, except </p> implies <p> and ...
5319    
5320          ## has an element in scope          ## has an element in scope
5321            my $non_optional;
5322          my $i;          my $i;
5323          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5324            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5325            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5326              !!!cp ('t410.1');              !!!cp ('t410.1');
5327              $i = $_;              $i = $_;
5328              last INSCOPE;              last INSCOPE;
5329            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
5330              !!!cp ('t411.1');              !!!cp ('t411.1');
5331              last INSCOPE;              last INSCOPE;
5332              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
5333                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
5334                !!!cp ('t411.2');
5335                #
5336              } else {
5337                !!!cp ('t411.3');
5338                $non_optional ||= $node;
5339                #
5340            }            }
5341          } # INSCOPE          } # INSCOPE
5342    
5343          if (defined $i) {          if (defined $i) {
5344            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
5345                    ne $token->{tag_name}) {            #
5346    
5347              ## 2. If current node != "p", parse error
5348              if ($non_optional) {
5349              !!!cp ('t412.1');              !!!cp ('t412.1');
5350              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5351                              text => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
5352                              token => $token);                              token => $token);
5353            } else {            } else {
5354              !!!cp ('t414.1');              !!!cp ('t414.1');
5355            }            }
5356    
5357              ## 3. Pop
5358            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5359          } else {          } else {
5360            !!!cp ('t413.1');            !!!cp ('t413.1');
# Line 7642  sub _tree_construction_main ($) { Line 5374  sub _tree_construction_main ($) {
5374        } elsif ({        } elsif ({
5375                  a => 1,                  a => 1,
5376                  b => 1, big => 1, em => 1, font => 1, i => 1,                  b => 1, big => 1, em => 1, font => 1, i => 1,
5377                  nobr => 1, s => 1, small => 1, strile => 1,                  nobr => 1, s => 1, small => 1, strike => 1,
5378                  strong => 1, tt => 1, u => 1,                  strong => 1, tt => 1, u => 1,
5379                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5380          !!!cp ('t427');          !!!cp ('t427');
# Line 7663  sub _tree_construction_main ($) { Line 5395  sub _tree_construction_main ($) {
5395          ## Ignore the token.          ## Ignore the token.
5396          !!!next-token;          !!!next-token;
5397          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
5398        } else {        } else {
5399            if ($token->{tag_name} eq 'sarcasm') {
5400              sleep 0.001; # take a deep breath
5401            }
5402    
5403          ## Step 1          ## Step 1
5404          my $node_i = -1;          my $node_i = -1;
5405          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
5406    
5407          ## Step 2          ## Step 2
5408          S2: {          S2: {
5409            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5410              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5411              if ($node_tag_name eq $token->{tag_name}) {
5412              ## Step 1              ## Step 1
5413              ## generate implied end tags              ## generate implied end tags
5414              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7704  sub _tree_construction_main ($) { Line 5421  sub _tree_construction_main ($) {
5421              }              }
5422                    
5423              ## Step 2              ## Step 2
5424              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5425                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5426                $current_tag_name =~ tr/A-Z/a-z/;
5427                if ($current_tag_name ne $token->{tag_name}) {
5428                !!!cp ('t431');                !!!cp ('t431');
5429                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5430                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7733  sub _tree_construction_main ($) { Line 5452  sub _tree_construction_main ($) {
5452                ## Ignore the token                ## Ignore the token
5453                !!!next-token;                !!!next-token;
5454                last S2;                last S2;
             }  
5455    
5456                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
5457                  ## 9.27, "a" is a child of <dd> (conforming).  In
5458                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
5459                  ## "a" is a child of both <body> and <dd>.
5460                }
5461                
5462              !!!cp ('t434');              !!!cp ('t434');
5463            }            }
5464                        
# Line 7775  sub _tree_construction_main ($) { Line 5499  sub _tree_construction_main ($) {
5499    ## TODO: script stuffs    ## TODO: script stuffs
5500  } # _tree_construct_main  } # _tree_construct_main
5501    
5502    ## XXX: How this method is organized is somewhat out of date, although
5503    ## it still does what the current spec documents.
5504  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5505    my $class = shift;    my $class = shift;
5506    my $node = shift;    my $node = shift; # /context/
5507    #my $s = \$_[0];    #my $s = \$_[0];
5508    my $onerror = $_[1];    my $onerror = $_[1];
5509    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
# Line 7785  sub set_inner_html ($$$$;$) { Line 5511  sub set_inner_html ($$$$;$) {
5511    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
5512    
5513    my $nt = $node->node_type;    my $nt = $node->node_type;
5514    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5515      # MUST      # MUST
5516            
5517      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7800  sub set_inner_html ($$$$;$) { Line 5526  sub set_inner_html ($$$$;$) {
5526    
5527      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5528      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5529    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5530      ## TODO: If non-html element      ## TODO: If non-html element
5531    
5532      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5533    
5534  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5535    
5536      ## Step 1 # MUST      ## F1. Create an HTML document.
5537      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5538      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5539      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5540    
5541        ## F2. Propagate quirkness flag
5542        my $node_doc = $node->owner_document;
5543        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5544    
5545        ## F3. Create an HTML parser
5546      my $p = $class->new;      my $p = $class->new;
5547      $p->{document} = $doc;      $p->{document} = $doc;
5548    
# Line 7938  sub set_inner_html ($$$$;$) { Line 5670  sub set_inner_html ($$$$;$) {
5670      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5671      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5672    
5673      ## Step 2      ## F4. If /context/ is not undef...
5674    
5675        ## F4.1. content model flag
5676      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5677      $p->{content_model} = {      $p->{content_model} = {
5678        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 7954  sub set_inner_html ($$$$;$) { Line 5688  sub set_inner_html ($$$$;$) {
5688      }->{$node_ln};      }->{$node_ln};
5689      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5690          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5691    
5692      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5693        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5694    
5695      ## Step 3      ## F4.2. Root |html| element
5696      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5697        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5698    
5699      ## Step 4 # MUST      ## F4.3.
5700      $doc->append_child ($root);      $doc->append_child ($root);
5701    
5702      ## Step 5 # MUST      ## F4.4.
5703      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5704    
5705      undef $p->{head_element};      undef $p->{head_element};
5706        undef $p->{head_element_inserted};
5707    
5708      ## Step 6 # MUST      ## F4.5.
5709      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5710    
5711      ## Step 7 # MUST      ## F4.6.
5712      my $anode = $node;      my $anode = $node;
5713      AN: while (defined $anode) {      AN: while (defined $anode) {
5714        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 7989  sub set_inner_html ($$$$;$) { Line 5723  sub set_inner_html ($$$$;$) {
5723        }        }
5724        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5725      } # AN      } # AN
5726        
5727      ## Step 9 # MUST      ## F.6. Start the parser.
5728      {      {
5729        my $self = $p;        my $self = $p;
5730        !!!next-token;        !!!next-token;
5731      }      }
5732      $p->_tree_construction_main;      $p->_tree_construction_main;
5733    
5734      ## Step 10 # MUST      ## F.7.
5735      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5736      for (@cn) {      for (@cn) {
5737        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.191  
changed lines
  Added in v.1.227

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24