/[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.194 by wakaba, Sat Oct 4 05:53:45 2008 UTC revision 1.236 by wakaba, Sun Sep 6 08:15:37 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
36  sub ADDRESS_EL () { 0b10 }  
37  sub BODY_EL () { 0b100 }  ## Bits 12-15
38  sub BUTTON_EL () { 0b1000 }  sub SPECIAL_EL () { 0b1_000000000000000 }
39  sub CAPTION_EL () { 0b10000 }  sub SCOPING_EL () { 0b1_00000000000000 }
40  sub DD_EL () { 0b100000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub DIV_EL () { 0b1000000 }  sub PHRASING_EL () { 0b1_000000000000 }
42  sub DT_EL () { 0b10000000 }  
43  sub FORM_EL () { 0b100000000 }  ## Bits 10-11
44  sub FORMATTING_EL () { 0b1000000000 }  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45  sub FRAMESET_EL () { 0b10000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46  sub HEADING_EL () { 0b100000000000 }  
47  sub HTML_EL () { 0b1000000000000 }  ## Bits 6-9
48  sub LI_EL () { 0b10000000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
49  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50  sub OPTION_EL () { 0b1000000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51  sub OPTGROUP_EL () { 0b10000000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
52  sub P_EL () { 0b100000000000000000 }  
53  sub SELECT_EL () { 0b1000000000000000000 }  ## Bit 5
54  sub TABLE_EL () { 0b10000000000000000000 }  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55  sub TABLE_CELL_EL () { 0b100000000000000000000 }  
56  sub TABLE_ROW_EL () { 0b1000000000000000000000 }  ## NOTE: Used in </body> and EOF algorithms.
57  sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  ## Bit 4
58  sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
61  ## NOTE: There is a code where a modified version of  ## NOTE: There is a code where a modified version of
62  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63  ## implementation (search for the algorithm name).  ## implementation (search for the algorithm name).
64  sub END_TAG_OPTIONAL_EL () {  ## Bit 3
65    DD_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
   DT_EL |  
   LI_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
66    
67  ## NOTE: Used in </body> and EOF algorithms.  ## Bits 0-2
 sub ALL_END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
68    
69  sub SCOPING_EL () {  sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    BUTTON_EL |  sub FORM_EL () { SPECIAL_EL | 0b001 }
71    CAPTION_EL |  sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    HTML_EL |  sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    TABLE_EL |  sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    TABLE_CELL_EL |  sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    MISC_SCOPING_EL  
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    FORM_EL |  }
125    FRAMESET_EL |  sub TABLE_EL () {
126    HEADING_EL |    SCOPING_EL |
127    OPTION_EL |    TABLE_ROWS_EL |
128    OPTGROUP_EL |    TABLE_SCOPING_EL |
129    SELECT_EL |    0b001
130    TABLE_ROW_EL |  }
131    TABLE_ROW_GROUP_EL |  sub TABLE_CELL_EL () {
132    MISC_SPECIAL_EL    SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136  }  }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 160  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
   eventsource => MISC_SPECIAL_EL,  
182    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
183    figure => MISC_SPECIAL_EL,    figure => MISC_SPECIAL_EL,
184    font => FORMATTING_EL,    font => FORMATTING_EL,
# Line 193  my $el_category = { Line 202  my $el_category = {
202    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
203    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
204    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
205      ## XXX keygen? (Whether a void element is in Special or not does not
206      ## affect to the processing, however.)
207    li => LI_EL,    li => LI_EL,
208    link => MISC_SPECIAL_EL,    link => MISC_SPECIAL_EL,
209    listing => MISC_SPECIAL_EL,    listing => MISC_SPECIAL_EL,
# Line 200  my $el_category = { Line 211  my $el_category = {
211    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
212    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
213    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
214    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
215    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
216    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
217    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 237  my $el_category = { Line 248  my $el_category = {
248    u => FORMATTING_EL,    u => FORMATTING_EL,
249    ul => MISC_SPECIAL_EL,    ul => MISC_SPECIAL_EL,
250    wbr => MISC_SPECIAL_EL,    wbr => MISC_SPECIAL_EL,
251      xmp => MISC_SPECIAL_EL,
252  };  };
253    
254  my $el_category_f = {  my $el_category_f = {
255    $MML_NS => {    $MML_NS => {
256      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
257      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
261      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
262    },    },
263    $SVG_NS => {    $SVG_NS => {
264      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
266      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
267    },    },
268    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
269  };  };
# Line 338  my $foreign_attr_xname = { Line 350  my $foreign_attr_xname = {
350    
351  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
352    
 my $charref_map = {  
   0x0D => 0x000A,  
   0x80 => 0x20AC,  
   0x81 => 0xFFFD,  
   0x82 => 0x201A,  
   0x83 => 0x0192,  
   0x84 => 0x201E,  
   0x85 => 0x2026,  
   0x86 => 0x2020,  
   0x87 => 0x2021,  
   0x88 => 0x02C6,  
   0x89 => 0x2030,  
   0x8A => 0x0160,  
   0x8B => 0x2039,  
   0x8C => 0x0152,  
   0x8D => 0xFFFD,  
   0x8E => 0x017D,  
   0x8F => 0xFFFD,  
   0x90 => 0xFFFD,  
   0x91 => 0x2018,  
   0x92 => 0x2019,  
   0x93 => 0x201C,  
   0x94 => 0x201D,  
   0x95 => 0x2022,  
   0x96 => 0x2013,  
   0x97 => 0x2014,  
   0x98 => 0x02DC,  
   0x99 => 0x2122,  
   0x9A => 0x0161,  
   0x9B => 0x203A,  
   0x9C => 0x0153,  
   0x9D => 0xFFFD,  
   0x9E => 0x017E,  
   0x9F => 0x0178,  
 }; # $charref_map  
 $charref_map->{$_} = 0xFFFD  
     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,  
         0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF  
         0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,  
         0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,  
         0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,  
         0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,  
         0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;  
   
353  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
354  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
355    
# Line 488  sub parse_byte_stream ($$$$;$$) { Line 456  sub parse_byte_stream ($$$$;$$) {
456      if (defined $charset_name) {      if (defined $charset_name) {
457        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
458    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
459        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
460        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
461            ($byte_stream);            ($byte_stream);
# Line 560  sub parse_byte_stream ($$$$;$$) { Line 527  sub parse_byte_stream ($$$$;$$) {
527            
528      if ($char_stream) { # if supported      if ($char_stream) { # if supported
529        ## "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;  
530                
531        ## Step 2        ## Step 1
532        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
533            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
534          !!!parse-error (type => 'charset label:matching',          !!!parse-error (type => 'charset label:matching',
# Line 581  sub parse_byte_stream ($$$$;$$) { Line 538  sub parse_byte_stream ($$$$;$$) {
538          return;          return;
539        }        }
540    
541          ## Step 2 (HTML5 revision 3205)
542          if (defined $self->{input_encoding} and
543              Message::Charset::Info->get_by_html_name ($self->{input_encoding})
544              ->{category} & Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
545            $self->{confident} = 1;
546            return;
547          }
548    
549          ## Step 3
550          if ($charset->{category} &
551              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
552            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
553            ($char_stream, $e_status) = $charset->get_decode_handle
554                ($byte_stream,
555                 byte_buffer => \ $buffer->{buffer});
556          }
557          $charset_name = $charset->get_iana_name;
558    
559        !!!parse-error (type => 'charset label detected',        !!!parse-error (type => 'charset label detected',
560                        text => $self->{input_encoding},                        text => $self->{input_encoding},
561                        value => $charset_name,                        value => $charset_name,
562                        level => $self->{level}->{warn},                        level => $self->{level}->{warn},
563                        token => $token);                        token => $token);
564                
565        ## Step 3        ## Step 4
566        # if (can) {        # if (can) {
567          ## change the encoding on the fly.          ## change the encoding on the fly.
568          #$self->{confident} = 1;          #$self->{confident} = 1;
569          #return;          #return;
570        # }        # }
571                
572        ## Step 4        ## Step 5
573        throw Whatpm::HTML::RestartParser ();        throw Whatpm::HTML::RestartParser ();
574      }      }
575    }; # $self->{change_encoding}    }; # $self->{change_encoding}
# Line 674  sub parse_char_stream ($$$;$$) { Line 649  sub parse_char_stream ($$$;$$) {
649    
650    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
651    
652      ## Confidence: irrelevant.
653    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
654    
655    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
656        if defined $self->{input_encoding};        if defined $self->{input_encoding};
657  ## TODO: |{input_encoding}| is needless?  ## TODO: |{input_encoding}| is needless?
# Line 835  sub new ($) { Line 812  sub new ($) {
812    return $self;    return $self;
813  } # new  } # new
814    
815  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 }  
816    
817  sub AFTER_HTML_IMS () { 0b100 }  sub AFTER_HTML_IMS () { 0b100 }
818  sub HEAD_IMS ()       { 0b1000 }  sub HEAD_IMS ()       { 0b1000 }
# Line 915  sub ROW_IMS ()        { 0b10000000 } Line 823  sub ROW_IMS ()        { 0b10000000 }
823  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
824  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
825  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
826  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
827      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
828      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
829      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
830    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
831        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
832        ## combined with the original insertion mode.  In thie parser,
833        ## they are stored together in the bit-or'ed form.
834    
835    sub IM_MASK () { 0b11111111111 }
836    
837  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
838    
# Line 945  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 859  sub IN_SELECT_IM () { SELECT_IMS | 0b01
859  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
860  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
861    
 ## 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  
   
862  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
863    my $self = shift;    my $self = shift;
864    ## NOTE: $self->{document} MUST be specified before this method is called    ## NOTE: $self->{document} MUST be specified before this method is called
# Line 3469  sub _construct_tree ($) { Line 887  sub _construct_tree ($) {
887    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
888    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
889    ## 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  
890        
891    !!!next-token;    !!!next-token;
892    
893    undef $self->{form_element};    undef $self->{form_element};
894    undef $self->{head_element};    undef $self->{head_element};
895      undef $self->{head_element_inserted};
896    $self->{open_elements} = [];    $self->{open_elements} = [];
897    undef $self->{inner_html_node};    undef $self->{inner_html_node};
898      undef $self->{ignore_newline};
899    
900    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
901    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3499  sub _tree_construction_initial ($) { Line 915  sub _tree_construction_initial ($) {
915    
916    INITIAL: {    INITIAL: {
917      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
918        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
919        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
920        ## language.        ## another language.  (We don't support such mode switchings; it
921          ## is nonsense to do anything different from what browsers do.)
922        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
923        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
924        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        my $doctype = $self->{document}->create_document_type_definition
925        if (not defined $token->{name} or # <!DOCTYPE>            ($doctype_name);
926            defined $token->{sysid}) {  
927          $doctype_name =~ tr/A-Z/a-z/; # ASCII case-insensitive
928          if ($doctype_name ne 'html') {
929          !!!cp ('t1');          !!!cp ('t1');
930          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
931        } elsif ($doctype_name ne 'HTML') {        } elsif (defined $token->{pubid}) {
932          !!!cp ('t2');          !!!cp ('t2');
933            ## XXX Obsolete permitted DOCTYPEs
934          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
935        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{sysid}) {
936          if ($token->{pubid} eq 'XSLT-compat') {          if ($token->{sysid} eq 'about:legacy-compat') {
937            !!!cp ('t1.2');            !!!cp ('t1.2'); ## <!DOCTYPE HTML SYSTEM "about:legacy-compat">
938            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
939                            level => $self->{level}->{should});                            level => $self->{level}->{should});
940          } else {          } else {
941            !!!parse-error (type => 'not HTML5', token => $token);            !!!parse-error (type => 'not HTML5', token => $token);
942          }          }
943        } else {        } else { ## <!DOCTYPE HTML>
944          !!!cp ('t3');          !!!cp ('t3');
945          #          #
946        }        }
947                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
948        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
949        ## 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.
950        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
951        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
952    
953        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
954        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
955          ## string, while |null| is an allowed value for the attribute
956          ## according to DOM3 Core.
957        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
958                
959        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'html') {
960          !!!cp ('t4');          !!!cp ('t4');
961          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
962        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{pubid}) {
# Line 3785  sub _tree_construction_root_element ($) Line 1206  sub _tree_construction_root_element ($)
1206      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
1207      !!!ack-later;      !!!ack-later;
1208      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
1209    } # B    } # B
1210    
1211    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3822  sub _reset_insertion_mode ($) { Line 1241  sub _reset_insertion_mode ($) {
1241          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1242          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1243          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1244        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1245          if ($last) {          if ($last) {
1246            !!!cp ('t28.2');            !!!cp ('t28.2');
1247            #            #
# Line 3851  sub _reset_insertion_mode ($) { Line 1270  sub _reset_insertion_mode ($) {
1270        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1271                
1272        ## Step 15        ## Step 15
1273        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1274          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1275            !!!cp ('t29');            !!!cp ('t29');
1276            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3983  sub _tree_construction_main ($) { Line 1402  sub _tree_construction_main ($) {
1402    
1403      ## Step 1      ## Step 1
1404      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1405      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1406    
1407      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1408      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1409      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1410    
1411      ## Step 4      ## Step 3, 4
1412      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;  
     }  
1413    
1414      ## Step 5      !!!nack ('t40.1');
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
   
     ## Step 6  
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
   
     ## Step 7  
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
1415      !!!next-token;      !!!next-token;
1416    }; # $parse_rcdata    }; # $parse_rcdata
1417    
1418    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1419        ## Step 1
1420      my $script_el;      my $script_el;
1421      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1422    
1423        ## Step 2
1424      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1425    
1426        ## Step 3
1427        ## TODO: Mark as "already executed", if ...
1428    
1429        ## Step 4 (HTML5 revision 2702)
1430        $insert->($script_el);
1431        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1432    
1433        ## Step 5
1434      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1435      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;  
1436    
1437      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1438          $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  
1439    
1440        $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...  
     }  
       
1441      !!!next-token;      !!!next-token;
1442    }; # $script_start_tag    }; # $script_start_tag
1443    
1444    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1445    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag (OBSOLETE; unused).
1446      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1447    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1448    
1449    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4171  sub _tree_construction_main ($) { Line 1528  sub _tree_construction_main ($) {
1528            !!!cp ('t59');            !!!cp ('t59');
1529            $furthest_block = $node;            $furthest_block = $node;
1530            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1531              ## NOTE: The topmost (eldest) node.
1532          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1533            !!!cp ('t60');            !!!cp ('t60');
1534            last OE;            last OE;
# Line 4254  sub _tree_construction_main ($) { Line 1612  sub _tree_construction_main ($) {
1612                
1613        ## Step 8        ## Step 8
1614        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
1615            ## Foster parenting.
1616          my $foster_parent_element;          my $foster_parent_element;
1617          my $next_sibling;          my $next_sibling;
1618          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1619            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1620                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;              !!!cp ('t65.2');
1621                               if (defined $parent and $parent->node_type == 1) {              $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1622                                 !!!cp ('t65.1');              $next_sibling = $self->{open_elements}->[$_]->[0];
1623                                 $foster_parent_element = $parent;              undef $next_sibling
1624                                 $next_sibling = $self->{open_elements}->[$_]->[0];                  unless $next_sibling->parent_node eq $foster_parent_element;
1625                               } else {              last OE;
1626                                 !!!cp ('t65.2');            }
1627                                 $foster_parent_element          } # OE
1628                                   = $self->{open_elements}->[$_ - 1]->[0];          $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1629                               }  
                              last OE;  
                            }  
                          } # OE  
                          $foster_parent_element = $self->{open_elements}->[0]->[0]  
                            unless defined $foster_parent_element;  
1630          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
1631          $open_tables->[-1]->[1] = 1; # tainted          $open_tables->[-1]->[1] = 1; # tainted
1632        } else {        } else {
# Line 4317  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 4328  sub _tree_construction_main ($) { Line 1682  sub _tree_construction_main ($) {
1682      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
1683    }; # $insert_to_current    }; # $insert_to_current
1684    
1685      ## Foster parenting.  Note that there are three "foster parenting"
1686      ## code in the parser: for elements (this one), for texts, and for
1687      ## elements in the AAA code.
1688    my $insert_to_foster = sub {    my $insert_to_foster = sub {
1689      my $child = shift;      my $child = shift;
1690      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
# Line 4335  sub _tree_construction_main ($) { Line 1692  sub _tree_construction_main ($) {
1692        my $foster_parent_element;        my $foster_parent_element;
1693        my $next_sibling;        my $next_sibling;
1694        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1695          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1696                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;            !!!cp ('t71');
1697                               if (defined $parent and $parent->node_type == 1) {            $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1698                                 !!!cp ('t70');            $next_sibling = $self->{open_elements}->[$_]->[0];
1699                                 $foster_parent_element = $parent;            undef $next_sibling
1700                                 $next_sibling = $self->{open_elements}->[$_]->[0];                unless $next_sibling->parent_node eq $foster_parent_element;
1701                               } else {            last OE;
1702                                 !!!cp ('t71');          }
1703                                 $foster_parent_element        } # OE
1704                                   = $self->{open_elements}->[$_ - 1]->[0];        $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1705                               }  
1706                               last OE;        $foster_parent_element->insert_before ($child, $next_sibling);
                            }  
                          } # OE  
                          $foster_parent_element = $self->{open_elements}->[0]->[0]  
                            unless defined $foster_parent_element;  
                          $foster_parent_element->insert_before  
                            ($child, $next_sibling);  
1707        $open_tables->[-1]->[1] = 1; # tainted        $open_tables->[-1]->[1] = 1; # tainted
1708      } else {      } else {
1709        !!!cp ('t72');        !!!cp ('t72');
# Line 4360  sub _tree_construction_main ($) { Line 1711  sub _tree_construction_main ($) {
1711      }      }
1712    }; # $insert_to_foster    }; # $insert_to_foster
1713    
1714      ## NOTE: Insert a character (MUST): When a character is inserted, if
1715      ## the last node that was inserted by the parser is a Text node and
1716      ## the character has to be inserted after that node, then the
1717      ## character is appended to the Text node.  However, if any other
1718      ## node is inserted by the parser, then a new Text node is created
1719      ## and the character is appended as that Text node.  If I'm not
1720      ## wrong, for a parser with scripting disabled, there are only two
1721      ## cases where this occurs.  One is the case where an element node
1722      ## is inserted to the |head| element.  This is covered by using the
1723      ## |$self->{head_element_inserted}| flag.  Another is the case where
1724      ## an element or comment is inserted into the |table| subtree while
1725      ## foster parenting happens.  This is covered by using the [2] flag
1726      ## of the |$open_tables| structure.  All other cases are handled
1727      ## simply by calling |manakai_append_text| method.
1728    
1729      ## TODO: |<body><script>document.write("a<br>");
1730      ## document.body.removeChild (document.body.lastChild);
1731      ## document.write ("b")</script>|
1732    
1733    B: while (1) {    B: while (1) {
1734    
1735        ## The "in table text" insertion mode.
1736        if ($self->{insertion_mode} & TABLE_IMS and
1737            not $self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
1738            not $self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1739          C: {
1740            my $s;
1741            if ($token->{type} == CHARACTER_TOKEN) {
1742              !!!cp ('t194');
1743              $self->{pending_chars} ||= [];
1744              push @{$self->{pending_chars}}, $token;
1745              !!!next-token;
1746              next B;
1747            } else {
1748              if ($self->{pending_chars}) {
1749                $s = join '', map { $_->{data} } @{$self->{pending_chars}};
1750                delete $self->{pending_chars};
1751                if ($s =~ /[^\x09\x0A\x0C\x0D\x20]/) {
1752                  !!!cp ('t195');
1753                  #
1754                } else {
1755                  !!!cp ('t195.1');
1756                  #$self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1757                  $self->{open_elements}->[-1]->[0]->append_child
1758                      ($self->{document}->create_text_node ($s));
1759                  last C;
1760                }
1761              } else {
1762                !!!cp ('t195.2');
1763                last C;
1764              }
1765            }
1766    
1767            ## Foster parenting.
1768            !!!parse-error (type => 'in table:#text', token => $token);
1769    
1770            ## NOTE: As if in body, but insert into the foster parent element.
1771            $reconstruct_active_formatting_elements->($insert_to_foster);
1772                
1773            if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
1774              # MUST
1775              my $foster_parent_element;
1776              my $next_sibling;
1777              OE: for (reverse 0..$#{$self->{open_elements}}) {
1778                if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1779                  !!!cp ('t197');
1780                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1781                  $next_sibling = $self->{open_elements}->[$_]->[0];
1782                  undef $next_sibling
1783                    unless $next_sibling->parent_node eq $foster_parent_element;
1784                  last OE;
1785                }
1786              } # OE
1787              $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1788    
1789              !!!cp ('t199');
1790              $foster_parent_element->insert_before
1791                  ($self->{document}->create_text_node ($s), $next_sibling);
1792    
1793              $open_tables->[-1]->[1] = 1; # tainted
1794              $open_tables->[-1]->[2] = 1; # ~node inserted
1795            } else {
1796              ## NOTE: Fragment case or in a foster parent'ed element
1797              ## (e.g. |<table><span>a|).  In fragment case, whether the
1798              ## character is appended to existing node or a new node is
1799              ## created is irrelevant, since the foster parent'ed nodes
1800              ## are discarded and fragment parsing does not invoke any
1801              ## script.
1802              !!!cp ('t200');
1803              $self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1804            }
1805          } # C
1806        } # TABLE_IMS
1807    
1808      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1809        !!!cp ('t73');        !!!cp ('t73');
1810        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
# Line 4407  sub _tree_construction_main ($) { Line 1851  sub _tree_construction_main ($) {
1851        } else {        } else {
1852          !!!cp ('t87');          !!!cp ('t87');
1853          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1854            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1855        }        }
1856        !!!next-token;        !!!next-token;
1857        next B;        next B;
1858        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1859          if ($token->{type} == CHARACTER_TOKEN) {
1860            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1861            delete $self->{ignore_newline};
1862    
1863            if (length $token->{data}) {
1864              !!!cp ('t43');
1865              $self->{open_elements}->[-1]->[0]->manakai_append_text
1866                  ($token->{data});
1867            } else {
1868              !!!cp ('t43.1');
1869            }
1870            !!!next-token;
1871            next B;
1872          } elsif ($token->{type} == END_TAG_TOKEN) {
1873            delete $self->{ignore_newline};
1874    
1875            if ($token->{tag_name} eq 'script') {
1876              !!!cp ('t50');
1877              
1878              ## Para 1-2
1879              my $script = pop @{$self->{open_elements}};
1880              
1881              ## Para 3
1882              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1883    
1884              ## Para 4
1885              ## TODO: $old_insertion_point = $current_insertion_point;
1886              ## TODO: $current_insertion_point = just before $self->{nc};
1887    
1888              ## Para 5
1889              ## TODO: Run the $script->[0].
1890    
1891              ## Para 6
1892              ## TODO: $current_insertion_point = $old_insertion_point;
1893    
1894              ## Para 7
1895              ## TODO: if ($pending_external_script) {
1896                ## TODO: ...
1897              ## TODO: }
1898    
1899              !!!next-token;
1900              next B;
1901            } else {
1902              !!!cp ('t42');
1903    
1904              pop @{$self->{open_elements}};
1905    
1906              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1907              !!!next-token;
1908              next B;
1909            }
1910          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1911            delete $self->{ignore_newline};
1912    
1913            !!!cp ('t44');
1914            !!!parse-error (type => 'not closed',
1915                            text => $self->{open_elements}->[-1]->[0]
1916                                ->manakai_local_name,
1917                            token => $token);
1918    
1919            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1920            #  ## TODO: Mark as "already executed"
1921            #}
1922    
1923            pop @{$self->{open_elements}};
1924    
1925            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1926            ## Reprocess.
1927            next B;
1928          } else {
1929            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1930          }
1931      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1932        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1933          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4421  sub _tree_construction_main ($) { Line 1939  sub _tree_construction_main ($) {
1939               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1940              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1941              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1942               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1943            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1944            !!!cp ('t87.2');            !!!cp ('t87.2');
1945            #            #
1946          } elsif ({          } elsif ({
1947                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1948                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1949                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1950                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1951                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1952                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1953                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1954                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1955                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1956                     ($token->{tag_name} eq 'font' and
1957                      ($token->{attributes}->{color} or
1958                       $token->{attributes}->{face} or
1959                       $token->{attributes}->{size}))) {
1960            !!!cp ('t87.2');            !!!cp ('t87.2');
1961            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1962                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4510  sub _tree_construction_main ($) { Line 2032  sub _tree_construction_main ($) {
2032          }          }
2033        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2034          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
2035          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
2036          #            !!!cp ('t87.41');
2037              #
2038              ## XXXscript: Execute script here.
2039            } else {
2040              !!!cp ('t87.5');
2041              #
2042            }
2043        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2044          !!!cp ('t87.6');          !!!cp ('t87.6');
2045          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4522  sub _tree_construction_main ($) { Line 2050  sub _tree_construction_main ($) {
2050          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
2051              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
2052    
2053            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
2054    
2055          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
2056          ## Reprocess.          ## Reprocess.
2057          next B;          next B;
# Line 4534  sub _tree_construction_main ($) { Line 2064  sub _tree_construction_main ($) {
2064        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
2065          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
2066            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2067              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
2068              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
2069              #                $self->{open_elements}->[-1]->[0]->append_child
2070                    ($self->{document}->create_text_node ($1));
2071                  delete $self->{head_element_inserted};
2072                  ## NOTE: |</head> <link> |
2073                  #
2074                } else {
2075                  !!!cp ('t88.2');
2076                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2077                  ## NOTE: |</head> &#x20;|
2078                  #
2079                }
2080            } else {            } else {
2081              !!!cp ('t88.1');              !!!cp ('t88.1');
2082              ## Ignore the token.              ## Ignore the token.
# Line 4632  sub _tree_construction_main ($) { Line 2172  sub _tree_construction_main ($) {
2172            !!!cp ('t97');            !!!cp ('t97');
2173          }          }
2174    
2175              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2176                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2177                  !!!cp ('t98');              !!!cp ('t98');
2178                  ## As if </noscript>              ## As if </noscript>
2179                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2180                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2181                                  token => $token);                              token => $token);
2182                            
2183                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2184                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2185                } else {            } else {
2186                  !!!cp ('t99');              !!!cp ('t99');
2187                }            }
2188    
2189                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2190                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2191                  !!!cp ('t100');              !!!cp ('t100');
2192                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2193                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2194                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2195                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2196                } else {              $self->{head_element_inserted} = 1;
2197                  !!!cp ('t101');            } else {
2198                }              !!!cp ('t101');
2199                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2200                pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2201                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2202                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2203                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2204                !!!next-token;            !!!nack ('t101.1');
2205                next B;            !!!next-token;
2206              next B;
2207          } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
2208            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2209            if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
# Line 4671  sub _tree_construction_main ($) { Line 2212  sub _tree_construction_main ($) {
2212                              text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2213              push @{$self->{open_elements}},              push @{$self->{open_elements}},
2214                  [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2215                $self->{head_element_inserted} = 1;
2216            } else {            } else {
2217              !!!cp ('t103');              !!!cp ('t103');
2218            }            }
# Line 4681  sub _tree_construction_main ($) { Line 2223  sub _tree_construction_main ($) {
2223            !!!ack ('t103.1');            !!!ack ('t103.1');
2224            !!!next-token;            !!!next-token;
2225            next B;            next B;
2226          } elsif ($token->{tag_name} eq 'command' or          } elsif ($token->{tag_name} eq 'command') {
                  $token->{tag_name} eq 'eventsource') {  
2227            if ($self->{insertion_mode} == IN_HEAD_IM) {            if ($self->{insertion_mode} == IN_HEAD_IM) {
2228              ## NOTE: If the insertion mode at the time of the emission              ## NOTE: If the insertion mode at the time of the emission
2229              ## of the token was "before head", $self->{insertion_mode}              ## of the token was "before head", $self->{insertion_mode}
# Line 4703  sub _tree_construction_main ($) { Line 2244  sub _tree_construction_main ($) {
2244              !!!cp ('t103.3');              !!!cp ('t103.3');
2245              #              #
2246            }            }
2247              } elsif ($token->{tag_name} eq 'meta') {          } elsif ($token->{tag_name} eq 'meta') {
2248                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2249                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2250                  !!!cp ('t104');              !!!cp ('t104');
2251                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2252                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2253                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2254                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2255                } else {              $self->{head_element_inserted} = 1;
2256                  !!!cp ('t105');            } else {
2257                }              !!!cp ('t105');
2258                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2259                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2260              my $meta_el = pop @{$self->{open_elements}};
2261    
2262                unless ($self->{confident}) {                unless ($self->{confident}) {
2263                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4773  sub _tree_construction_main ($) { Line 2315  sub _tree_construction_main ($) {
2315                !!!ack ('t110.1');                !!!ack ('t110.1');
2316                !!!next-token;                !!!next-token;
2317                next B;                next B;
2318              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2319                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2320                  !!!cp ('t111');              !!!cp ('t111');
2321                  ## As if </noscript>              ## As if </noscript>
2322                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2323                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2324                                  token => $token);                              token => $token);
2325                            
2326                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2327                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2328                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2329                  !!!cp ('t112');              !!!cp ('t112');
2330                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2331                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2332                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2333                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2334                } else {              $self->{head_element_inserted} = 1;
2335                  !!!cp ('t113');            } else {
2336                }              !!!cp ('t113');
2337              }
2338    
2339                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2340                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2341                    : $self->{open_elements}->[-1]->[0];  
2342                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2343                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2344                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2345                next B;            ## |head| element is inserted only for the purpose of
2346              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2347                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2348                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2349                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2350                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2351                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2352                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2353                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2354                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2355                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2356                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2357                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2358                  !!!cp ('t115');              !!!cp ('t114');
2359                }              !!!parse-error (type => 'after head',
2360                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2361                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2362                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2363                next B;              $self->{head_element_inserted} = 1;
2364              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2365                !!!cp ('t115');
2366              }
2367              $parse_rcdata->(CDATA_CONTENT_MODEL);
2368              ## ISSUE: A spec bug [Bug 6038]
2369              splice @{$self->{open_elements}}, -2, 1, () # <head>
2370                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2371              next B;
2372            } elsif ($token->{tag_name} eq 'noscript') {
2373                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2374                  !!!cp ('t116');                  !!!cp ('t116');
2375                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4839  sub _tree_construction_main ($) { Line 2390  sub _tree_construction_main ($) {
2390                  !!!cp ('t118');                  !!!cp ('t118');
2391                  #                  #
2392                }                }
2393              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2394                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2395                  !!!cp ('t119');              !!!cp ('t119');
2396                  ## As if </noscript>              ## As if </noscript>
2397                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2398                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2399                                  token => $token);                              token => $token);
2400                            
2401                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2402                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2403                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2404                  !!!cp ('t120');              !!!cp ('t120');
2405                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2406                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2407                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2408                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2409                } else {              $self->{head_element_inserted} = 1;
2410                  !!!cp ('t121');            } else {
2411                }              !!!cp ('t121');
2412              }
2413    
2414                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2415                $script_start_tag->();            $script_start_tag->();
2416                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2417                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2418                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2419              } elsif ($token->{tag_name} eq 'body' or            next B;
2420                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2421                     $token->{tag_name} eq 'frameset') {
2422                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2423                  !!!cp ('t122');                  !!!cp ('t122');
2424                  ## As if </noscript>                  ## As if </noscript>
# Line 5000  sub _tree_construction_main ($) { Line 2553  sub _tree_construction_main ($) {
2553              } elsif ({              } elsif ({
2554                        body => 1, html => 1,                        body => 1, html => 1,
2555                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2556                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2557                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2558                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2559                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2560                  !!!cp ('t140');                  !!!cp ('t140');
# Line 5026  sub _tree_construction_main ($) { Line 2580  sub _tree_construction_main ($) {
2580                ## Ignore the token                ## Ignore the token
2581                !!!next-token;                !!!next-token;
2582                next B;                next B;
2583              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2584                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2585                  !!!cp ('t142.2');              !!!cp ('t142.2');
2586                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2587                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2588                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2589                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2590        
2591                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2592                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2593                  !!!cp ('t143.2');              !!!cp ('t143.2');
2594                  ## As if </head>              ## As if </head>
2595                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2596                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2597        
2598                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2599                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2600                  !!!cp ('t143.3');              !!!cp ('t143.3');
2601                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2602                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2603                                  text => 'br', token => $token);                              text => 'br', token => $token);
2604                  ## As if </noscript>              ## As if </noscript>
2605                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2606                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2607    
2608                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2609                  ## As if </head>              ## As if </head>
2610                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2611                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2612    
2613                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2614                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2615                  !!!cp ('t143.4');              !!!cp ('t143.4');
2616                  #              #
2617                } else {            } else {
2618                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2619                }            }
2620    
2621                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2622                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2623                !!!cp ('t145');                !!!cp ('t145');
2624                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2625                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5114  sub _tree_construction_main ($) { Line 2663  sub _tree_construction_main ($) {
2663              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2664              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2665              ## reprocess              ## reprocess
2666              next B;          next B;
2667        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2668          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2669            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5172  sub _tree_construction_main ($) { Line 2721  sub _tree_construction_main ($) {
2721        } else {        } else {
2722          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2723        }        }
   
           ## ISSUE: An issue in the spec.  
2724      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2725            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
2726              !!!cp ('t150');              !!!cp ('t150');
# Line 5189  sub _tree_construction_main ($) { Line 2736  sub _tree_construction_main ($) {
2736                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2737                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2738                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2739                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2740                  ## have an element in table scope                  ## have an element in table scope
2741                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2742                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2743                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2744                      !!!cp ('t151');                      !!!cp ('t151');
2745    
2746                      ## Close the cell                      ## Close the cell
# Line 5217  sub _tree_construction_main ($) { Line 2764  sub _tree_construction_main ($) {
2764                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2765                  !!!next-token;                  !!!next-token;
2766                  next B;                  next B;
2767                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2768                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2769                                  token => $token);                                  token => $token);
2770                                    
# Line 5227  sub _tree_construction_main ($) { Line 2774  sub _tree_construction_main ($) {
2774                  INSCOPE: {                  INSCOPE: {
2775                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2776                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2777                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2778                        !!!cp ('t155');                        !!!cp ('t155');
2779                        $i = $_;                        $i = $_;
2780                        last INSCOPE;                        last INSCOPE;
# Line 5253  sub _tree_construction_main ($) { Line 2800  sub _tree_construction_main ($) {
2800                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2801                  }                  }
2802    
2803                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2804                    !!!cp ('t159');                    !!!cp ('t159');
2805                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2806                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5282  sub _tree_construction_main ($) { Line 2829  sub _tree_construction_main ($) {
2829              }              }
2830            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2831              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2832                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2833                  ## have an element in table scope                  ## have an element in table scope
2834                  my $i;                  my $i;
2835                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5332  sub _tree_construction_main ($) { Line 2879  sub _tree_construction_main ($) {
2879                                    
2880                  !!!next-token;                  !!!next-token;
2881                  next B;                  next B;
2882                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2883                  !!!cp ('t169');                  !!!cp ('t169');
2884                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2885                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5344  sub _tree_construction_main ($) { Line 2891  sub _tree_construction_main ($) {
2891                  #                  #
2892                }                }
2893              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2894                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2895                  ## have a table element in table scope                  ## have a table element in table scope
2896                  my $i;                  my $i;
2897                  INSCOPE: {                  INSCOPE: {
2898                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2899                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2900                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2901                        !!!cp ('t171');                        !!!cp ('t171');
2902                        $i = $_;                        $i = $_;
2903                        last INSCOPE;                        last INSCOPE;
# Line 5375  sub _tree_construction_main ($) { Line 2922  sub _tree_construction_main ($) {
2922                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2923                  }                  }
2924                                    
2925                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2926                    !!!cp ('t175');                    !!!cp ('t175');
2927                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2928                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5393  sub _tree_construction_main ($) { Line 2940  sub _tree_construction_main ($) {
2940                                    
2941                  !!!next-token;                  !!!next-token;
2942                  next B;                  next B;
2943                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2944                  !!!cp ('t177');                  !!!cp ('t177');
2945                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2946                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5408  sub _tree_construction_main ($) { Line 2955  sub _tree_construction_main ($) {
2955                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2956                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2957                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2958                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2959                ## have an element in table scope                ## have an element in table scope
2960                my $i;                my $i;
2961                my $tn;                my $tn;
# Line 5425  sub _tree_construction_main ($) { Line 2972  sub _tree_construction_main ($) {
2972                                line => $token->{line},                                line => $token->{line},
2973                                column => $token->{column}};                                column => $token->{column}};
2974                      next B;                      next B;
2975                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2976                      !!!cp ('t180');                      !!!cp ('t180');
2977                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2978                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5445  sub _tree_construction_main ($) { Line 2992  sub _tree_construction_main ($) {
2992                  next B;                  next B;
2993                } # INSCOPE                } # INSCOPE
2994              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2995                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2996                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2997                                token => $token);                                token => $token);
2998    
# Line 5454  sub _tree_construction_main ($) { Line 3001  sub _tree_construction_main ($) {
3001                my $i;                my $i;
3002                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3003                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3004                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
3005                    !!!cp ('t184');                    !!!cp ('t184');
3006                    $i = $_;                    $i = $_;
3007                    last INSCOPE;                    last INSCOPE;
# Line 5465  sub _tree_construction_main ($) { Line 3012  sub _tree_construction_main ($) {
3012                } # INSCOPE                } # INSCOPE
3013                unless (defined $i) {                unless (defined $i) {
3014                  !!!cp ('t186');                  !!!cp ('t186');
3015            ## TODO: Wrong error type?
3016                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3017                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
3018                  ## Ignore the token                  ## Ignore the token
# Line 5478  sub _tree_construction_main ($) { Line 3026  sub _tree_construction_main ($) {
3026                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3027                }                }
3028    
3029                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
3030                  !!!cp ('t188');                  !!!cp ('t188');
3031                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
3032                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5510  sub _tree_construction_main ($) { Line 3058  sub _tree_construction_main ($) {
3058                  !!!cp ('t191');                  !!!cp ('t191');
3059                  #                  #
3060                }                }
3061              } elsif ({          } elsif ({
3062                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
3063                        thead => 1, tr => 1,                    thead => 1, tr => 1,
3064                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
3065                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
3066                !!!cp ('t192');            !!!cp ('t192');
3067                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
3068                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
3069                ## Ignore the token            ## Ignore the token
3070                !!!next-token;            !!!next-token;
3071                next B;            next B;
3072              } else {          } else {
3073                !!!cp ('t193');            !!!cp ('t193');
3074                #            #
3075              }          }
3076        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3077          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3078            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5543  sub _tree_construction_main ($) { Line 3091  sub _tree_construction_main ($) {
3091        $insert = $insert_to_current;        $insert = $insert_to_current;
3092        #        #
3093      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3094        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == START_TAG_TOKEN) {
         if (not $open_tables->[-1]->[1] and # tainted  
             $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {  
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
           unless (length $token->{data}) {  
             !!!cp ('t194');  
             !!!next-token;  
             next B;  
           } else {  
             !!!cp ('t195');  
           }  
         }  
   
         !!!parse-error (type => 'in table:#text', token => $token);  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     !!!cp ('t196');  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 !!!cp ('t199');  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
           $open_tables->[-1]->[1] = 1; # tainted  
         } else {  
           !!!cp ('t200');  
           $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
         }  
               
         !!!next-token;  
         next B;  
       } elsif ($token->{type} == START_TAG_TOKEN) {  
3095          if ({          if ({
3096               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3097               th => 1, td => 1,               th => 1, td => 1,
3098              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3099            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3100              ## Clear back to table context              ## Clear back to table context
3101              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3102                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5625  sub _tree_construction_main ($) { Line 3109  sub _tree_construction_main ($) {
3109              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3110            }            }
3111                        
3112            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3113              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3114                !!!cp ('t202');                !!!cp ('t202');
3115                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5639  sub _tree_construction_main ($) { Line 3123  sub _tree_construction_main ($) {
3123                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3124              }              }
3125                                    
3126                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3127                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3128                    !!!cp ('t204');                !!!cp ('t204');
3129                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3130                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3131                    !!!next-token;                !!!nack ('t204');
3132                    next B;                !!!next-token;
3133                  } else {                next B;
3134                    !!!cp ('t205');              } else {
3135                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3136                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3137                  }                ## reprocess in the "in row" insertion mode
3138                } else {              }
3139                  !!!cp ('t206');            } else {
3140                }              !!!cp ('t206');
3141              }
3142    
3143                ## Clear back to table row context                ## Clear back to table row context
3144                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5662  sub _tree_construction_main ($) { Line 3147  sub _tree_construction_main ($) {
3147                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3148                }                }
3149                                
3150                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3151                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3152              $self->{insertion_mode} = IN_CELL_IM;
3153    
3154                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3155                                
3156                !!!nack ('t207.1');            !!!nack ('t207.1');
3157              !!!next-token;
3158              next B;
3159            } elsif ({
3160                      caption => 1, col => 1, colgroup => 1,
3161                      tbody => 1, tfoot => 1, thead => 1,
3162                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3163                     }->{$token->{tag_name}}) {
3164              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3165                ## As if </tr>
3166                ## have an element in table scope
3167                my $i;
3168                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3169                  my $node = $self->{open_elements}->[$_];
3170                  if ($node->[1] == TABLE_ROW_EL) {
3171                    !!!cp ('t208');
3172                    $i = $_;
3173                    last INSCOPE;
3174                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3175                    !!!cp ('t209');
3176                    last INSCOPE;
3177                  }
3178                } # INSCOPE
3179                unless (defined $i) {
3180                  !!!cp ('t210');
3181                  ## TODO: This type is wrong.
3182                  !!!parse-error (type => 'unmacthed end tag',
3183                                  text => $token->{tag_name}, token => $token);
3184                  ## Ignore the token
3185                  !!!nack ('t210.1');
3186                !!!next-token;                !!!next-token;
3187                next B;                next B;
3188              } 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;  
                 }  
3189                                    
3190                  ## Clear back to table row context                  ## Clear back to table row context
3191                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5722  sub _tree_construction_main ($) { Line 3208  sub _tree_construction_main ($) {
3208                  }                  }
3209                }                }
3210    
3211                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3212                  ## have an element in table scope                  ## have an element in table scope
3213                  my $i;                  my $i;
3214                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3215                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3216                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3217                      !!!cp ('t214');                      !!!cp ('t214');
3218                      $i = $_;                      $i = $_;
3219                      last INSCOPE;                      last INSCOPE;
# Line 5769  sub _tree_construction_main ($) { Line 3255  sub _tree_construction_main ($) {
3255                  !!!cp ('t218');                  !!!cp ('t218');
3256                }                }
3257    
3258                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3259                  ## Clear back to table context              ## Clear back to table context
3260                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3261                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3262                    !!!cp ('t219');                !!!cp ('t219');
3263                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3264                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3265                  }              }
3266                                
3267                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3268                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3269                  ## reprocess              ## reprocess
3270                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3271                  next B;              !!!ack-later;
3272                } elsif ({              next B;
3273                          caption => 1,            } elsif ({
3274                          colgroup => 1,                      caption => 1,
3275                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3276                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3277                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3278                ## Clear back to table context
3279                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3280                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3281                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5796  sub _tree_construction_main ($) { Line 3283  sub _tree_construction_main ($) {
3283                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3284                  }                  }
3285                                    
3286                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3287                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3288                                    
3289                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3290                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3291                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3292                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3293                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3294                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3295                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3296                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3297                  !!!next-token;                                        }->{$token->{tag_name}};
3298                  !!!nack ('t220.1');              !!!next-token;
3299                  next B;              !!!nack ('t220.1');
3300                } else {              next B;
3301                  die "$0: in table: <>: $token->{tag_name}";            } else {
3302                }              die "$0: in table: <>: $token->{tag_name}";
3303              }
3304              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3305                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3306                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5824  sub _tree_construction_main ($) { Line 3312  sub _tree_construction_main ($) {
3312                my $i;                my $i;
3313                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3314                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3315                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3316                    !!!cp ('t221');                    !!!cp ('t221');
3317                    $i = $_;                    $i = $_;
3318                    last INSCOPE;                    last INSCOPE;
# Line 5851  sub _tree_construction_main ($) { Line 3339  sub _tree_construction_main ($) {
3339                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3340                }                }
3341    
3342                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3343                  !!!cp ('t225');                  !!!cp ('t225');
3344                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3345                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5871  sub _tree_construction_main ($) { Line 3359  sub _tree_construction_main ($) {
3359            !!!ack-later;            !!!ack-later;
3360            next B;            next B;
3361          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
3362            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.8');
3363              !!!cp ('t227.8');            ## NOTE: This is a "as if in head" code clone.
3364              ## NOTE: This is a "as if in head" code clone.            $parse_rcdata->(CDATA_CONTENT_MODEL);
3365              $parse_rcdata->(CDATA_CONTENT_MODEL);            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3366              next B;            next B;
           } else {  
             !!!cp ('t227.7');  
             #  
           }  
3367          } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
3368            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.6');
3369              !!!cp ('t227.6');            ## NOTE: This is a "as if in head" code clone.
3370              ## NOTE: This is a "as if in head" code clone.            $script_start_tag->();
3371              $script_start_tag->();            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3372              next B;            next B;
           } else {  
             !!!cp ('t227.5');  
             #  
           }  
3373          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
3374            if (not $open_tables->[-1]->[1]) { # tainted            if ($token->{attributes}->{type}) {
3375              if ($token->{attributes}->{type}) { ## TODO: case              my $type = $token->{attributes}->{type}->{value};
3376                my $type = lc $token->{attributes}->{type}->{value};              $type =~ tr/A-Z/a-z/; ## ASCII case-insensitive.
3377                if ($type eq 'hidden') {              if ($type eq 'hidden') {
3378                  !!!cp ('t227.3');                !!!cp ('t227.3');
3379                  !!!parse-error (type => 'in table',                !!!parse-error (type => 'in table',
3380                                  text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
3381    
3382                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3383                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3384    
3385                  ## TODO: form element pointer                ## TODO: form element pointer
3386    
3387                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3388    
3389                  !!!next-token;                !!!next-token;
3390                  !!!ack ('t227.2.1');                !!!ack ('t227.2.1');
3391                  next B;                next B;
               } else {  
                 !!!cp ('t227.2');  
                 #  
               }  
3392              } else {              } else {
3393                !!!cp ('t227.1');                !!!cp ('t227.1');
3394                #                #
# Line 5931  sub _tree_construction_main ($) { Line 3408  sub _tree_construction_main ($) {
3408          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3409          #          #
3410        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3411              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3412                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3413                ## have an element in table scope            ## have an element in table scope
3414                my $i;                my $i;
3415                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3416                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3417                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3418                    !!!cp ('t228');                    !!!cp ('t228');
3419                    $i = $_;                    $i = $_;
3420                    last INSCOPE;                    last INSCOPE;
# Line 5972  sub _tree_construction_main ($) { Line 3449  sub _tree_construction_main ($) {
3449                !!!nack ('t231.1');                !!!nack ('t231.1');
3450                next B;                next B;
3451              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3452                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3453                  ## As if </tr>                  ## As if </tr>
3454                  ## have an element in table scope                  ## have an element in table scope
3455                  my $i;                  my $i;
3456                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3457                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3458                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3459                      !!!cp ('t233');                      !!!cp ('t233');
3460                      $i = $_;                      $i = $_;
3461                      last INSCOPE;                      last INSCOPE;
# Line 6011  sub _tree_construction_main ($) { Line 3488  sub _tree_construction_main ($) {
3488                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3489                }                }
3490    
3491                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3492                  ## have an element in table scope                  ## have an element in table scope
3493                  my $i;                  my $i;
3494                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3495                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3496                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3497                      !!!cp ('t237');                      !!!cp ('t237');
3498                      $i = $_;                      $i = $_;
3499                      last INSCOPE;                      last INSCOPE;
# Line 6063  sub _tree_construction_main ($) { Line 3540  sub _tree_construction_main ($) {
3540                my $i;                my $i;
3541                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3542                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3543                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3544                    !!!cp ('t241');                    !!!cp ('t241');
3545                    $i = $_;                    $i = $_;
3546                    last INSCOPE;                    last INSCOPE;
# Line 6093  sub _tree_construction_main ($) { Line 3570  sub _tree_construction_main ($) {
3570                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3571                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3572                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3573                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3574                  ## have an element in table scope                  ## have an element in table scope
3575                  my $i;                  my $i;
3576                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6122  sub _tree_construction_main ($) { Line 3599  sub _tree_construction_main ($) {
3599                  my $i;                  my $i;
3600                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3601                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3602                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3603                      !!!cp ('t250');                      !!!cp ('t250');
3604                      $i = $_;                      $i = $_;
3605                      last INSCOPE;                      last INSCOPE;
# Line 6212  sub _tree_construction_main ($) { Line 3689  sub _tree_construction_main ($) {
3689            #            #
3690          }          }
3691        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3692          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3693                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3694            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3695            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6227  sub _tree_construction_main ($) { Line 3704  sub _tree_construction_main ($) {
3704        } else {        } else {
3705          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3706        }        }
3707      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3708            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3709              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3710                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6254  sub _tree_construction_main ($) { Line 3731  sub _tree_construction_main ($) {
3731              }              }
3732            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3733              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3734                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3735                  !!!cp ('t264');                  !!!cp ('t264');
3736                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3737                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6280  sub _tree_construction_main ($) { Line 3757  sub _tree_construction_main ($) {
3757                #                #
3758              }              }
3759        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3760          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3761              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3762            !!!cp ('t270.2');            !!!cp ('t270.2');
3763            ## Stop parsing.            ## Stop parsing.
# Line 6298  sub _tree_construction_main ($) { Line 3775  sub _tree_construction_main ($) {
3775        }        }
3776    
3777            ## As if </colgroup>            ## As if </colgroup>
3778            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3779              !!!cp ('t269');              !!!cp ('t269');
3780  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3781              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6323  sub _tree_construction_main ($) { Line 3800  sub _tree_construction_main ($) {
3800          next B;          next B;
3801        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3802          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3803            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3804              !!!cp ('t272');              !!!cp ('t272');
3805              ## As if </option>              ## As if </option>
3806              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6336  sub _tree_construction_main ($) { Line 3813  sub _tree_construction_main ($) {
3813            !!!next-token;            !!!next-token;
3814            next B;            next B;
3815          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3816            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3817              !!!cp ('t274');              !!!cp ('t274');
3818              ## As if </option>              ## As if </option>
3819              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6344  sub _tree_construction_main ($) { Line 3821  sub _tree_construction_main ($) {
3821              !!!cp ('t275');              !!!cp ('t275');
3822            }            }
3823    
3824            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3825              !!!cp ('t276');              !!!cp ('t276');
3826              ## As if </optgroup>              ## As if </optgroup>
3827              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6357  sub _tree_construction_main ($) { Line 3834  sub _tree_construction_main ($) {
3834            !!!next-token;            !!!next-token;
3835            next B;            next B;
3836          } elsif ({          } elsif ({
3837                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3838                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3839                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3840                          == IN_SELECT_IN_TABLE_IM and
3841                    {                    {
3842                     caption => 1, table => 1,                     caption => 1, table => 1,
3843                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3844                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3845                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3846            ## TODO: The type below is not good - <select> is replaced by </select>  
3847            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3848                            token => $token);            if ($token->{tag_name} eq 'select') {
3849            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3850            ## as if there were </select> (otherwise).                                token => $token);
3851            ## have an element in table scope            } else {
3852                !!!parse-error (type => 'not closed', text => 'select',
3853                                token => $token);
3854              }
3855    
3856              ## 2./<select>-1. Unless "have an element in table scope" (select):
3857            my $i;            my $i;
3858            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3859              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3860              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3861                !!!cp ('t278');                !!!cp ('t278');
3862                $i = $_;                $i = $_;
3863                last INSCOPE;                last INSCOPE;
# Line 6385  sub _tree_construction_main ($) { Line 3868  sub _tree_construction_main ($) {
3868            } # INSCOPE            } # INSCOPE
3869            unless (defined $i) {            unless (defined $i) {
3870              !!!cp ('t280');              !!!cp ('t280');
3871              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3872                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3873              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3874                  ## case two errors, "select in select" and "unmatched
3875                  ## end tags" are reported to the user, the latter might
3876                  ## be confusing but this is what the spec requires.
3877                  !!!parse-error (type => 'unmatched end tag',
3878                                  text => 'select',
3879                                  token => $token);
3880                }
3881                ## Ignore the token.
3882              !!!nack ('t280.1');              !!!nack ('t280.1');
3883              !!!next-token;              !!!next-token;
3884              next B;              next B;
3885            }            }
3886    
3887              ## 3. Otherwise, as if there were <select>:
3888                                
3889            !!!cp ('t281');            !!!cp ('t281');
3890            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6408  sub _tree_construction_main ($) { Line 3901  sub _tree_construction_main ($) {
3901              ## Reprocess the token.              ## Reprocess the token.
3902              next B;              next B;
3903            }            }
3904            } elsif ($token->{tag_name} eq 'script') {
3905              !!!cp ('t281.3');
3906              ## NOTE: This is an "as if in head" code clone
3907              $script_start_tag->();
3908              next B;
3909          } else {          } else {
3910            !!!cp ('t282');            !!!cp ('t282');
3911            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6419  sub _tree_construction_main ($) { Line 3917  sub _tree_construction_main ($) {
3917          }          }
3918        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3919          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3920            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3921                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3922              !!!cp ('t283');              !!!cp ('t283');
3923              ## As if </option>              ## As if </option>
3924              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3925            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3926              !!!cp ('t284');              !!!cp ('t284');
3927              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3928            } else {            } else {
# Line 6437  sub _tree_construction_main ($) { Line 3935  sub _tree_construction_main ($) {
3935            !!!next-token;            !!!next-token;
3936            next B;            next B;
3937          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3938            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3939              !!!cp ('t286');              !!!cp ('t286');
3940              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3941            } else {            } else {
# Line 6454  sub _tree_construction_main ($) { Line 3952  sub _tree_construction_main ($) {
3952            my $i;            my $i;
3953            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3954              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3955              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3956                !!!cp ('t288');                !!!cp ('t288');
3957                $i = $_;                $i = $_;
3958                last INSCOPE;                last INSCOPE;
# Line 6481  sub _tree_construction_main ($) { Line 3979  sub _tree_construction_main ($) {
3979            !!!nack ('t291.1');            !!!nack ('t291.1');
3980            !!!next-token;            !!!next-token;
3981            next B;            next B;
3982          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3983                         == IN_SELECT_IN_TABLE_IM and
3984                   {                   {
3985                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3986                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6516  sub _tree_construction_main ($) { Line 4015  sub _tree_construction_main ($) {
4015            undef $i;            undef $i;
4016            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4017              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4018              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4019                !!!cp ('t295');                !!!cp ('t295');
4020                $i = $_;                $i = $_;
4021                last INSCOPE;                last INSCOPE;
# Line 6555  sub _tree_construction_main ($) { Line 4054  sub _tree_construction_main ($) {
4054            next B;            next B;
4055          }          }
4056        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4057          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4058                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4059            !!!cp ('t299.1');            !!!cp ('t299.1');
4060            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6742  sub _tree_construction_main ($) { Line 4241  sub _tree_construction_main ($) {
4241        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4242          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4243              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4244            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4245                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4246              !!!cp ('t325');              !!!cp ('t325');
4247              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6756  sub _tree_construction_main ($) { Line 4255  sub _tree_construction_main ($) {
4255            }            }
4256    
4257            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4258                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4259              !!!cp ('t327');              !!!cp ('t327');
4260              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4261            } else {            } else {
# Line 6788  sub _tree_construction_main ($) { Line 4287  sub _tree_construction_main ($) {
4287            next B;            next B;
4288          }          }
4289        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4290          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4291                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4292            !!!cp ('t331.1');            !!!cp ('t331.1');
4293            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6801  sub _tree_construction_main ($) { Line 4300  sub _tree_construction_main ($) {
4300        } else {        } else {
4301          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4302        }        }
   
       ## ISSUE: An issue in spec here  
4303      } else {      } else {
4304        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4305      }      }
# Line 6820  sub _tree_construction_main ($) { Line 4317  sub _tree_construction_main ($) {
4317          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4318          next B;          next B;
4319        } elsif ({        } elsif ({
4320                  base => 1, command => 1, eventsource => 1, link => 1,                  base => 1, command => 1, link => 1,
4321                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4322          !!!cp ('t334');          !!!cp ('t334');
4323          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
# Line 6893  sub _tree_construction_main ($) { Line 4390  sub _tree_construction_main ($) {
4390          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4391                                
4392          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4393              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4394            !!!cp ('t342');            !!!cp ('t342');
4395            ## Ignore the token            ## Ignore the token
4396          } else {          } else {
# Line 6911  sub _tree_construction_main ($) { Line 4408  sub _tree_construction_main ($) {
4408          !!!next-token;          !!!next-token;
4409          next B;          next B;
4410        } elsif ({        } elsif ({
4411                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: Start tags for non-phrasing flow content elements
4412                  div => 1, dl => 1, fieldset => 1,  
4413                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  ## NOTE: The normal one
4414                  menu => 1, ol => 1, p => 1, ul => 1,                  address => 1, article => 1, aside => 1, blockquote => 1,
4415                    center => 1, datagrid => 1, details => 1, dialog => 1,
4416                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
4417                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
4418                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
4419                    section => 1, ul => 1,
4420                    ## NOTE: As normal, but drops leading newline
4421                  pre => 1, listing => 1,                  pre => 1, listing => 1,
4422                    ## NOTE: As normal, but interacts with the form element pointer
4423                  form => 1,                  form => 1,
4424                    
4425                  table => 1,                  table => 1,
4426                  hr => 1,                  hr => 1,
4427                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4428    
4429            ## 1. When there is an opening |form| element:
4430          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4431            !!!cp ('t350');            !!!cp ('t350');
4432            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6929  sub _tree_construction_main ($) { Line 4436  sub _tree_construction_main ($) {
4436            next B;            next B;
4437          }          }
4438    
4439          ## has a p element in scope          ## 2. Close the |p| element, if any.
4440          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4441            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4442              !!!cp ('t344');            ## has a p element in scope
4443              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4444              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4445                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4446              next B;                !!!back-token; # <form>
4447            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4448              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4449              last INSCOPE;                next B;
4450                } elsif ($_->[1] & SCOPING_EL) {
4451                  !!!cp ('t345');
4452                  last INSCOPE;
4453                }
4454              } # INSCOPE
4455            }
4456    
4457            ## 3. Close the opening <hn> element, if any.
4458            if ({h1 => 1, h2 => 1, h3 => 1,
4459                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4460              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4461                !!!parse-error (type => 'not closed',
4462                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4463                                token => $token);
4464                pop @{$self->{open_elements}};
4465            }            }
4466          } # INSCOPE          }
4467              
4468            ## 4. Insertion.
4469          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4470          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4471            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6983  sub _tree_construction_main ($) { Line 4506  sub _tree_construction_main ($) {
4506            !!!next-token;            !!!next-token;
4507          }          }
4508          next B;          next B;
4509        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {        } elsif ($token->{tag_name} eq 'li') {
4510          ## has a p element in scope          ## NOTE: As normal, but imply </li> when there's another <li> ...
4511    
4512            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4513              ## Interpreted as <li><foo/></li><li/> (non-conforming):
4514              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4515              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
4516              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4517              ## object (Fx)
4518              ## Generate non-tree (non-conforming):
4519              ## basefont (IE7 (where basefont is non-void)), center (IE),
4520              ## form (IE), hn (IE)
4521            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4522              ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4523              ## div (Fx, S)
4524    
4525            my $non_optional;
4526            my $i = -1;
4527    
4528            ## 1.
4529            for my $node (reverse @{$self->{open_elements}}) {
4530              if ($node->[1] == LI_EL) {
4531                ## 2. (a) As if </li>
4532                {
4533                  ## If no </li> - not applied
4534                  #
4535    
4536                  ## Otherwise
4537    
4538                  ## 1. generate implied end tags, except for </li>
4539                  #
4540    
4541                  ## 2. If current node != "li", parse error
4542                  if ($non_optional) {
4543                    !!!parse-error (type => 'not closed',
4544                                    text => $non_optional->[0]->manakai_local_name,
4545                                    token => $token);
4546                    !!!cp ('t355');
4547                  } else {
4548                    !!!cp ('t356');
4549                  }
4550    
4551                  ## 3. Pop
4552                  splice @{$self->{open_elements}}, $i;
4553                }
4554    
4555                last; ## 2. (b) goto 5.
4556              } elsif (
4557                       ## NOTE: not "formatting" and not "phrasing"
4558                       ($node->[1] & SPECIAL_EL or
4559                        $node->[1] & SCOPING_EL) and
4560                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4561                       (not $node->[1] & ADDRESS_DIV_P_EL)
4562                      ) {
4563                ## 3.
4564                !!!cp ('t357');
4565                last; ## goto 5.
4566              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4567                !!!cp ('t358');
4568                #
4569              } else {
4570                !!!cp ('t359');
4571                $non_optional ||= $node;
4572                #
4573              }
4574              ## 4.
4575              ## goto 2.
4576              $i--;
4577            }
4578    
4579            ## 5. (a) has a |p| element in scope
4580          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4581            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4582              !!!cp ('t353');              !!!cp ('t353');
4583    
4584                ## NOTE: |<p><li>|, for example.
4585    
4586              !!!back-token; # <x>              !!!back-token; # <x>
4587              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
4588                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 6998  sub _tree_construction_main ($) { Line 4593  sub _tree_construction_main ($) {
4593            }            }
4594          } # INSCOPE          } # INSCOPE
4595    
4596          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## 5. (b) insert
4597            ## Interpreted as <li><foo/></li><li/> (non-conforming)          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4598            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),          !!!nack ('t359.1');
4599            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),          !!!next-token;
4600            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),          next B;
4601            ## object (Fx)        } elsif ($token->{tag_name} eq 'dt' or
4602            ## Generate non-tree (non-conforming)                 $token->{tag_name} eq 'dd') {
4603            ## basefont (IE7 (where basefont is non-void)), center (IE),          ## NOTE: As normal, but imply </dt> or </dd> when ...
4604            ## form (IE), hn (IE)  
4605          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          my $non_optional;
           ## Interpreted as <li><foo><li/></foo></li> (non-conforming)  
           ## div (Fx, S)  
             
         ## Step 1  
4606          my $i = -1;          my $i = -1;
4607          my $node = $self->{open_elements}->[$i];  
4608          my $li_or_dtdd = {li => {li => 1},          ## 1.
4609                            dt => {dt => 1, dd => 1},          for my $node (reverse @{$self->{open_elements}}) {
4610                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};            if ($node->[1] == DTDD_EL) {
4611          LI: {              ## 2. (a) As if </li>
4612            ## Step 2              {
4613            if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {                ## If no </li> - not applied
4614              if ($i != -1) {                #
4615                !!!cp ('t355');  
4616                !!!parse-error (type => 'not closed',                ## Otherwise
4617                                text => $self->{open_elements}->[-1]->[0]  
4618                                    ->manakai_local_name,                ## 1. generate implied end tags, except for </dt> or </dd>
4619                                token => $token);                #
4620              } else {  
4621                !!!cp ('t356');                ## 2. If current node != "dt"|"dd", parse error
4622                  if ($non_optional) {
4623                    !!!parse-error (type => 'not closed',
4624                                    text => $non_optional->[0]->manakai_local_name,
4625                                    token => $token);
4626                    !!!cp ('t355.1');
4627                  } else {
4628                    !!!cp ('t356.1');
4629                  }
4630    
4631                  ## 3. Pop
4632                  splice @{$self->{open_elements}}, $i;
4633              }              }
4634              splice @{$self->{open_elements}}, $i;  
4635              last LI;              last; ## 2. (b) goto 5.
4636              } elsif (
4637                       ## NOTE: not "formatting" and not "phrasing"
4638                       ($node->[1] & SPECIAL_EL or
4639                        $node->[1] & SCOPING_EL) and
4640                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4641    
4642                       (not $node->[1] & ADDRESS_DIV_P_EL)
4643                      ) {
4644                ## 3.
4645                !!!cp ('t357.1');
4646                last; ## goto 5.
4647              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
4648                !!!cp ('t358.1');
4649                #
4650            } else {            } else {
4651              !!!cp ('t357');              !!!cp ('t359.1');
4652            }              $non_optional ||= $node;
4653                          #
           ## 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;  
4654            }            }
4655                        ## 4.
4656            !!!cp ('t359');            ## goto 2.
           ## Step 4  
4657            $i--;            $i--;
4658            $node = $self->{open_elements}->[$i];          }
4659            redo LI;  
4660          } # LI          ## 5. (a) has a |p| element in scope
4661                      INSCOPE: for (reverse @{$self->{open_elements}}) {
4662              if ($_->[1] == P_EL) {
4663                !!!cp ('t353.1');
4664                !!!back-token; # <x>
4665                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4666                          line => $token->{line}, column => $token->{column}};
4667                next B;
4668              } elsif ($_->[1] & SCOPING_EL) {
4669                !!!cp ('t354.1');
4670                last INSCOPE;
4671              }
4672            } # INSCOPE
4673    
4674            ## 5. (b) insert
4675          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4676          !!!nack ('t359.1');          !!!nack ('t359.2');
4677          !!!next-token;          !!!next-token;
4678          next B;          next B;
4679        } elsif ($token->{tag_name} eq 'plaintext') {        } elsif ($token->{tag_name} eq 'plaintext') {
4680            ## NOTE: As normal, but effectively ends parsing
4681    
4682          ## has a p element in scope          ## has a p element in scope
4683          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4684            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4685              !!!cp ('t367');              !!!cp ('t367');
4686              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4687              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7082  sub _tree_construction_main ($) { Line 4703  sub _tree_construction_main ($) {
4703        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4704          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4705            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4706            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4707              !!!cp ('t371');              !!!cp ('t371');
4708              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4709                            
# Line 7126  sub _tree_construction_main ($) { Line 4747  sub _tree_construction_main ($) {
4747          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4748          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4749            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4750            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4751              !!!cp ('t376');              !!!cp ('t376');
4752              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4753              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7149  sub _tree_construction_main ($) { Line 4770  sub _tree_construction_main ($) {
4770          ## has a button element in scope          ## has a button element in scope
4771          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4772            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4773            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4774              !!!cp ('t378');              !!!cp ('t378');
4775              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4776              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7214  sub _tree_construction_main ($) { Line 4835  sub _tree_construction_main ($) {
4835                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4836                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4837                           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}},  
4838                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4839                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4840                         );                         );
# Line 7238  sub _tree_construction_main ($) { Line 4857  sub _tree_construction_main ($) {
4857                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4858                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4859                           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}},  
4860                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4861                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4862                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7249  sub _tree_construction_main ($) { Line 4866  sub _tree_construction_main ($) {
4866            next B;            next B;
4867          }          }
4868        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4869          my $tag_name = $token->{tag_name};          ## 1. Insert
4870          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4871                    
4872            ## Step 2 # XXX
4873          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4874    
4875            ## 2. Drop U+000A LINE FEED
4876            $self->{ignore_newline} = 1;
4877    
4878            ## 3. RCDATA
4879          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4880          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4881            
4882          $insert->($el);          ## 4., 6. Insertion mode
4883                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4884          my $text = '';  
4885            ## XXX: 5. frameset-ok flag
4886    
4887          !!!nack ('t392.1');          !!!nack ('t392.1');
4888          !!!next-token;          !!!next-token;
4889          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4890            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4891            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4892              !!!cp ('t392');          ## has an |option| element in scope
4893              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4894            } else {            my $node = $self->{open_elements}->[$_];
4895              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4896                !!!cp ('t397.1');
4897                ## NOTE: As if </option>
4898                !!!back-token; # <option> or <optgroup>
4899                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4900                          line => $token->{line}, column => $token->{column}};
4901                next B;
4902              } elsif ($node->[1] & SCOPING_EL) {
4903                !!!cp ('t397.2');
4904                last INSCOPE;
4905            }            }
4906          } else {          } # INSCOPE
4907            !!!cp ('t394');  
4908          }          $reconstruct_active_formatting_elements->($insert_to_current);
4909          while ($token->{type} == CHARACTER_TOKEN) {  
4910            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4911            $text .= $token->{data};  
4912            !!!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);  
         }  
4913          !!!next-token;          !!!next-token;
4914          next B;          redo B;
4915        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4916                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4917          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4918          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4919            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4920            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4921              !!!cp ('t398.1');              !!!cp ('t398.1');
4922              ## generate implied end tags              ## generate implied end tags
4923              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4924                !!!cp ('t398.2');                !!!cp ('t398.2');
4925                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4926              }              }
4927              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4928                !!!cp ('t398.3');                !!!cp ('t398.3');
4929                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4930                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4931                                    ->manakai_local_name,                                    ->manakai_local_name,
4932                                token => $token);                                token => $token);
4933                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4934                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4935              }              }
4936              last INSCOPE;              last INSCOPE;
4937            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7322  sub _tree_construction_main ($) { Line 4939  sub _tree_construction_main ($) {
4939              last INSCOPE;              last INSCOPE;
4940            }            }
4941          } # INSCOPE          } # INSCOPE
4942              
4943            ## TODO: <non-ruby><rt> is not allowed.
4944    
4945          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4946    
# Line 7342  sub _tree_construction_main ($) { Line 4961  sub _tree_construction_main ($) {
4961                    
4962          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4963            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4964            !!!ack ('t398.1');            !!!ack ('t398.6');
4965          } else {          } else {
4966            !!!cp ('t398.2');            !!!cp ('t398.7');
4967            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
4968            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
4969            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7355  sub _tree_construction_main ($) { Line 4974  sub _tree_construction_main ($) {
4974          next B;          next B;
4975        } elsif ({        } elsif ({
4976                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
4977                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
4978                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
4979                  thead => 1, tr => 1,                  thead => 1, tr => 1,
4980                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7366  sub _tree_construction_main ($) { Line 4985  sub _tree_construction_main ($) {
4985          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
4986          !!!next-token;          !!!next-token;
4987          next B;          next B;
4988                  } elsif ($token->{tag_name} eq 'param' or
4989          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
4990            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4991            pop @{$self->{open_elements}};
4992    
4993            !!!ack ('t398.5');
4994            !!!next-token;
4995            redo B;
4996        } else {        } else {
4997          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
4998            !!!cp ('t384');            !!!cp ('t384');
# Line 7403  sub _tree_construction_main ($) { Line 5028  sub _tree_construction_main ($) {
5028            !!!ack ('t388.2');            !!!ack ('t388.2');
5029          } elsif ({          } elsif ({
5030                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5031                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
5032                    #image => 1,                    keygen => 1,
5033                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5034            !!!cp ('t388.1');            !!!cp ('t388.1');
5035            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7414  sub _tree_construction_main ($) { Line 5039  sub _tree_construction_main ($) {
5039                    
5040            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5041                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5042                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5043              !!!cp ('t400.1');              !!!cp ('t400.1');
5044              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5045            } else {            } else {
# Line 7431  sub _tree_construction_main ($) { Line 5056  sub _tree_construction_main ($) {
5056        }        }
5057      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5058        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5059          ## has a |body| element in scope  
5060            ## 1. If not "have an element in scope":
5061            ## "has a |body| element in scope"
5062          my $i;          my $i;
5063          INSCOPE: {          INSCOPE: {
5064            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5065              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5066                !!!cp ('t405');                !!!cp ('t405');
5067                $i = $_;                $i = $_;
5068                last INSCOPE;                last INSCOPE;
# Line 7445  sub _tree_construction_main ($) { Line 5072  sub _tree_construction_main ($) {
5072              }              }
5073            }            }
5074    
5075            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
5076    
5077              !!!parse-error (type => 'unmatched end tag',
5078                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5079            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
5080            !!!next-token;            !!!next-token;
5081            next B;            next B;
5082          } # INSCOPE          } # INSCOPE
5083    
5084            ## 2. If unclosed elements:
5085          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5086            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5087                      $_->[1] == OPTGROUP_EL ||
5088                      $_->[1] == OPTION_EL ||
5089                      $_->[1] == RUBY_COMPONENT_EL) {
5090              !!!cp ('t403');              !!!cp ('t403');
5091              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5092                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7464  sub _tree_construction_main ($) { Line 5097  sub _tree_construction_main ($) {
5097            }            }
5098          }          }
5099    
5100            ## 3. Switch the insertion mode.
5101          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5102          !!!next-token;          !!!next-token;
5103          next B;          next B;
# Line 7471  sub _tree_construction_main ($) { Line 5105  sub _tree_construction_main ($) {
5105          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5106          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5107          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5108              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5109            ## ISSUE: There is an issue in the spec.            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
5110              !!!cp ('t406');              !!!cp ('t406');
5111              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5112                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7494  sub _tree_construction_main ($) { Line 5127  sub _tree_construction_main ($) {
5127            next B;            next B;
5128          }          }
5129        } elsif ({        } elsif ({
5130                  address => 1, blockquote => 1, center => 1, dir => 1,                  ## NOTE: End tags for non-phrasing flow content elements
5131                  div => 1, dl => 1, fieldset => 1, listing => 1,  
5132                  menu => 1, ol => 1, pre => 1, ul => 1,                  ## NOTE: The normal ones
5133                    address => 1, article => 1, aside => 1, blockquote => 1,
5134                    center => 1, datagrid => 1, details => 1, dialog => 1,
5135                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
5136                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
5137                    ol => 1, pre => 1, section => 1, ul => 1,
5138    
5139                    ## NOTE: As normal, but ... optional tags
5140                  dd => 1, dt => 1, li => 1,                  dd => 1, dt => 1, li => 1,
5141    
5142                  applet => 1, button => 1, marquee => 1, object => 1,                  applet => 1, button => 1, marquee => 1, object => 1,
5143                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5144            ## NOTE: Code for <li> start tags includes "as if </li>" code.
5145            ## Code for <dt> or <dd> start tags includes "as if </dt> or
5146            ## </dd>" code.
5147    
5148          ## has an element in scope          ## has an element in scope
5149          my $i;          my $i;
5150          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 7560  sub _tree_construction_main ($) { Line 5205  sub _tree_construction_main ($) {
5205          !!!next-token;          !!!next-token;
5206          next B;          next B;
5207        } elsif ($token->{tag_name} eq 'form') {        } elsif ($token->{tag_name} eq 'form') {
5208            ## NOTE: As normal, but interacts with the form element pointer
5209    
5210          undef $self->{form_element};          undef $self->{form_element};
5211    
5212          ## has an element in scope          ## has an element in scope
5213          my $i;          my $i;
5214          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5215            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5216            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5217              !!!cp ('t418');              !!!cp ('t418');
5218              $i = $_;              $i = $_;
5219              last INSCOPE;              last INSCOPE;
# Line 7607  sub _tree_construction_main ($) { Line 5254  sub _tree_construction_main ($) {
5254          !!!next-token;          !!!next-token;
5255          next B;          next B;
5256        } elsif ({        } elsif ({
5257                    ## NOTE: As normal, except acts as a closer for any ...
5258                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
5259                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
5260          ## has an element in scope          ## has an element in scope
5261          my $i;          my $i;
5262          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5263            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5264            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5265              !!!cp ('t423');              !!!cp ('t423');
5266              $i = $_;              $i = $_;
5267              last INSCOPE;              last INSCOPE;
# Line 7652  sub _tree_construction_main ($) { Line 5300  sub _tree_construction_main ($) {
5300          !!!next-token;          !!!next-token;
5301          next B;          next B;
5302        } elsif ($token->{tag_name} eq 'p') {        } elsif ($token->{tag_name} eq 'p') {
5303            ## NOTE: As normal, except </p> implies <p> and ...
5304    
5305          ## has an element in scope          ## has an element in scope
5306            my $non_optional;
5307          my $i;          my $i;
5308          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5309            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5310            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5311              !!!cp ('t410.1');              !!!cp ('t410.1');
5312              $i = $_;              $i = $_;
5313              last INSCOPE;              last INSCOPE;
5314            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
5315              !!!cp ('t411.1');              !!!cp ('t411.1');
5316              last INSCOPE;              last INSCOPE;
5317              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
5318                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
5319                !!!cp ('t411.2');
5320                #
5321              } else {
5322                !!!cp ('t411.3');
5323                $non_optional ||= $node;
5324                #
5325            }            }
5326          } # INSCOPE          } # INSCOPE
5327    
5328          if (defined $i) {          if (defined $i) {
5329            if ($self->{open_elements}->[-1]->[0]->manakai_local_name            ## 1. Generate implied end tags
5330                    ne $token->{tag_name}) {            #
5331    
5332              ## 2. If current node != "p", parse error
5333              if ($non_optional) {
5334              !!!cp ('t412.1');              !!!cp ('t412.1');
5335              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5336                              text => $self->{open_elements}->[-1]->[0]                              text => $non_optional->[0]->manakai_local_name,
                                 ->manakai_local_name,  
5337                              token => $token);                              token => $token);
5338            } else {            } else {
5339              !!!cp ('t414.1');              !!!cp ('t414.1');
5340            }            }
5341    
5342              ## 3. Pop
5343            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
5344          } else {          } else {
5345            !!!cp ('t413.1');            !!!cp ('t413.1');
# Line 7718  sub _tree_construction_main ($) { Line 5380  sub _tree_construction_main ($) {
5380          ## Ignore the token.          ## Ignore the token.
5381          !!!next-token;          !!!next-token;
5382          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  
           
5383        } else {        } else {
5384            if ($token->{tag_name} eq 'sarcasm') {
5385              sleep 0.001; # take a deep breath
5386            }
5387    
5388          ## Step 1          ## Step 1
5389          my $node_i = -1;          my $node_i = -1;
5390          my $node = $self->{open_elements}->[$node_i];          my $node = $self->{open_elements}->[$node_i];
5391    
5392          ## Step 2          ## Step 2
5393          S2: {          S2: {
5394            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5395              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5396              if ($node_tag_name eq $token->{tag_name}) {
5397              ## Step 1              ## Step 1
5398              ## generate implied end tags              ## generate implied end tags
5399              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7759  sub _tree_construction_main ($) { Line 5406  sub _tree_construction_main ($) {
5406              }              }
5407                    
5408              ## Step 2              ## Step 2
5409              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5410                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5411                $current_tag_name =~ tr/A-Z/a-z/;
5412                if ($current_tag_name ne $token->{tag_name}) {
5413                !!!cp ('t431');                !!!cp ('t431');
5414                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5415                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7835  sub _tree_construction_main ($) { Line 5484  sub _tree_construction_main ($) {
5484    ## TODO: script stuffs    ## TODO: script stuffs
5485  } # _tree_construct_main  } # _tree_construct_main
5486    
5487    ## XXX: How this method is organized is somewhat out of date, although
5488    ## it still does what the current spec documents.
5489  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5490    my $class = shift;    my $class = shift;
5491    my $node = shift;    my $node = shift; # /context/
5492    #my $s = \$_[0];    #my $s = \$_[0];
5493    my $onerror = $_[1];    my $onerror = $_[1];
5494    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
5495    
   ## ISSUE: Should {confident} be true?  
   
5496    my $nt = $node->node_type;    my $nt = $node->node_type;
5497    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5498      # MUST      # MUST
5499            
5500      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7860  sub set_inner_html ($$$$;$) { Line 5509  sub set_inner_html ($$$$;$) {
5509    
5510      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5511      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5512    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5513      ## TODO: If non-html element      ## TODO: If non-html element
5514    
5515      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5516    
5517  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5518    
5519      ## Step 1 # MUST      ## F1. Create an HTML document.
5520      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5521      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5522      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5523    
5524        ## F2. Propagate quirkness flag
5525        my $node_doc = $node->owner_document;
5526        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5527    
5528        ## F3. Create an HTML parser
5529      my $p = $class->new;      my $p = $class->new;
5530      $p->{document} = $doc;      $p->{document} = $doc;
5531    
# Line 7998  sub set_inner_html ($$$$;$) { Line 5653  sub set_inner_html ($$$$;$) {
5653      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5654      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5655    
5656      ## Step 2      ## F4. If /context/ is not undef...
5657    
5658        ## F4.1. content model flag
5659      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5660      $p->{content_model} = {      $p->{content_model} = {
5661        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8014  sub set_inner_html ($$$$;$) { Line 5671  sub set_inner_html ($$$$;$) {
5671      }->{$node_ln};      }->{$node_ln};
5672      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5673          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5674    
5675      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5676        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5677    
5678      ## Step 3      ## F4.2. Root |html| element
5679      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5680        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5681    
5682      ## Step 4 # MUST      ## F4.3.
5683      $doc->append_child ($root);      $doc->append_child ($root);
5684    
5685      ## Step 5 # MUST      ## F4.4.
5686      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5687    
5688      undef $p->{head_element};      undef $p->{head_element};
5689        undef $p->{head_element_inserted};
5690    
5691      ## Step 6 # MUST      ## F4.5.
5692      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5693    
5694      ## Step 7 # MUST      ## F4.6.
5695      my $anode = $node;      my $anode = $node;
5696      AN: while (defined $anode) {      AN: while (defined $anode) {
5697        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8049  sub set_inner_html ($$$$;$) { Line 5706  sub set_inner_html ($$$$;$) {
5706        }        }
5707        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5708      } # AN      } # AN
5709        
5710      ## Step 9 # MUST      ## F.5. Set the input stream.
5711        $p->{confident} = 1; ## Confident: irrelevant.
5712    
5713        ## F.6. Start the parser.
5714      {      {
5715        my $self = $p;        my $self = $p;
5716        !!!next-token;        !!!next-token;
5717      }      }
5718      $p->_tree_construction_main;      $p->_tree_construction_main;
5719    
5720      ## Step 10 # MUST      ## F.7.
5721      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5722      for (@cn) {      for (@cn) {
5723        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.194  
changed lines
  Added in v.1.236

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24