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

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

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

revision 1.201 by wakaba, Sat Oct 4 12:20:35 2008 UTC revision 1.235 by wakaba, Sun Sep 6 08:02:54 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
36  sub ADDRESS_EL () { 0b10 }  
37  sub BODY_EL () { 0b100 }  ## Bits 12-15
38  sub BUTTON_EL () { 0b1000 }  sub SPECIAL_EL () { 0b1_000000000000000 }
39  sub CAPTION_EL () { 0b10000 }  sub SCOPING_EL () { 0b1_00000000000000 }
40  sub DD_EL () { 0b100000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub DIV_EL () { 0b1000000 }  sub PHRASING_EL () { 0b1_000000000000 }
42  sub DT_EL () { 0b10000000 }  
43  sub FORM_EL () { 0b100000000 }  ## Bits 10-11
44  sub FORMATTING_EL () { 0b1000000000 }  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45  sub FRAMESET_EL () { 0b10000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46  sub HEADING_EL () { 0b100000000000 }  
47  sub HTML_EL () { 0b1000000000000 }  ## Bits 6-9
48  sub LI_EL () { 0b10000000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
49  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50  sub OPTION_EL () { 0b1000000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51  sub OPTGROUP_EL () { 0b10000000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
52  sub P_EL () { 0b100000000000000000 }  
53  sub SELECT_EL () { 0b1000000000000000000 }  ## Bit 5
54  sub TABLE_EL () { 0b10000000000000000000 }  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55  sub TABLE_CELL_EL () { 0b100000000000000000000 }  
56  sub TABLE_ROW_EL () { 0b1000000000000000000000 }  ## NOTE: Used in </body> and EOF algorithms.
57  sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  ## Bit 4
58  sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
61  ## NOTE: There is a code where a modified version of  ## NOTE: There is a code where a modified version of
62  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63  ## implementation (search for the algorithm name).  ## implementation (search for the algorithm name).
64  sub END_TAG_OPTIONAL_EL () {  ## Bit 3
65    DD_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
   DT_EL |  
   LI_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
66    
67  ## NOTE: Used in </body> and EOF algorithms.  ## Bits 0-2
 sub ALL_END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   ## ISSUE: option, optgroup, rt, rp?  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
68    
69  sub SCOPING_EL () {  sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    BUTTON_EL |  sub FORM_EL () { SPECIAL_EL | 0b001 }
71    CAPTION_EL |  sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    HTML_EL |  sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    TABLE_EL |  sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    TABLE_CELL_EL |  sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    MISC_SCOPING_EL  
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
   FORM_EL |  
   FRAMESET_EL |  
   HEADING_EL |  
   SELECT_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
124  }  }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 160  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
   eventsource => MISC_SPECIAL_EL,  
182    fieldset => MISC_SPECIAL_EL,    fieldset => MISC_SPECIAL_EL,
183    figure => MISC_SPECIAL_EL,    figure => MISC_SPECIAL_EL,
184    font => FORMATTING_EL,    font => FORMATTING_EL,
# Line 193  my $el_category = { Line 202  my $el_category = {
202    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.    #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
203    input => MISC_SPECIAL_EL,    input => MISC_SPECIAL_EL,
204    isindex => MISC_SPECIAL_EL,    isindex => MISC_SPECIAL_EL,
205      ## XXX keygen? (Whether a void element is in Special or not does not
206      ## affect to the processing, however.)
207    li => LI_EL,    li => LI_EL,
208    link => MISC_SPECIAL_EL,    link => MISC_SPECIAL_EL,
209    listing => MISC_SPECIAL_EL,    listing => MISC_SPECIAL_EL,
# Line 200  my $el_category = { Line 211  my $el_category = {
211    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
212    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
213    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
214    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
215    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
216    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
217    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 242  my $el_category = { Line 253  my $el_category = {
253  my $el_category_f = {  my $el_category_f = {
254    $MML_NS => {    $MML_NS => {
255      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
256      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
261    },    },
262    $SVG_NS => {    $SVG_NS => {
263      foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
266    },    },
267    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
268  };  };
# Line 338  my $foreign_attr_xname = { Line 349  my $foreign_attr_xname = {
349    
350  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
351    
 my $charref_map = {  
   0x0D => 0x000A,  
   0x80 => 0x20AC,  
   0x81 => 0xFFFD,  
   0x82 => 0x201A,  
   0x83 => 0x0192,  
   0x84 => 0x201E,  
   0x85 => 0x2026,  
   0x86 => 0x2020,  
   0x87 => 0x2021,  
   0x88 => 0x02C6,  
   0x89 => 0x2030,  
   0x8A => 0x0160,  
   0x8B => 0x2039,  
   0x8C => 0x0152,  
   0x8D => 0xFFFD,  
   0x8E => 0x017D,  
   0x8F => 0xFFFD,  
   0x90 => 0xFFFD,  
   0x91 => 0x2018,  
   0x92 => 0x2019,  
   0x93 => 0x201C,  
   0x94 => 0x201D,  
   0x95 => 0x2022,  
   0x96 => 0x2013,  
   0x97 => 0x2014,  
   0x98 => 0x02DC,  
   0x99 => 0x2122,  
   0x9A => 0x0161,  
   0x9B => 0x203A,  
   0x9C => 0x0153,  
   0x9D => 0xFFFD,  
   0x9E => 0x017E,  
   0x9F => 0x0178,  
 }; # $charref_map  
 $charref_map->{$_} = 0xFFFD  
     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,  
         0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF  
         0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,  
         0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,  
         0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,  
         0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,  
         0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;  
   
352  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
353  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
354    
# Line 559  sub parse_byte_stream ($$$$;$$) { Line 526  sub parse_byte_stream ($$$$;$$) {
526            
527      if ($char_stream) { # if supported      if ($char_stream) { # if supported
528        ## "Change the encoding" algorithm:        ## "Change the encoding" algorithm:
   
       ## Step 1      
       if ($charset->{category} &  
           Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {  
         $charset = Message::Charset::Info->get_by_html_name ('utf-8');  
         ($char_stream, $e_status) = $charset->get_decode_handle  
             ($byte_stream,  
              byte_buffer => \ $buffer->{buffer});  
       }  
       $charset_name = $charset->get_iana_name;  
529                
530        ## Step 2        ## Step 1
531        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
532            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
533          !!!parse-error (type => 'charset label:matching',          !!!parse-error (type => 'charset label:matching',
# Line 580  sub parse_byte_stream ($$$$;$$) { Line 537  sub parse_byte_stream ($$$$;$$) {
537          return;          return;
538        }        }
539    
540          ## Step 2 (HTML5 revision 3205)
541          if (defined $self->{input_encoding} and
542              Message::Charset::Info->get_by_html_name ($self->{input_encoding})
543              ->{category} & Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
544            $self->{confident} = 1;
545            return;
546          }
547    
548          ## Step 3
549          if ($charset->{category} &
550              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
551            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
552            ($char_stream, $e_status) = $charset->get_decode_handle
553                ($byte_stream,
554                 byte_buffer => \ $buffer->{buffer});
555          }
556          $charset_name = $charset->get_iana_name;
557    
558        !!!parse-error (type => 'charset label detected',        !!!parse-error (type => 'charset label detected',
559                        text => $self->{input_encoding},                        text => $self->{input_encoding},
560                        value => $charset_name,                        value => $charset_name,
561                        level => $self->{level}->{warn},                        level => $self->{level}->{warn},
562                        token => $token);                        token => $token);
563                
564        ## Step 3        ## Step 4
565        # if (can) {        # if (can) {
566          ## change the encoding on the fly.          ## change the encoding on the fly.
567          #$self->{confident} = 1;          #$self->{confident} = 1;
568          #return;          #return;
569        # }        # }
570                
571        ## Step 4        ## Step 5
572        throw Whatpm::HTML::RestartParser ();        throw Whatpm::HTML::RestartParser ();
573      }      }
574    }; # $self->{change_encoding}    }; # $self->{change_encoding}
# Line 673  sub parse_char_stream ($$$;$$) { Line 648  sub parse_char_stream ($$$;$$) {
648    
649    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
650    
651      ## Confidence: irrelevant.
652    $self->{confident} = 1 unless exists $self->{confident};    $self->{confident} = 1 unless exists $self->{confident};
653    
654    $self->{document}->input_encoding ($self->{input_encoding})    $self->{document}->input_encoding ($self->{input_encoding})
655        if defined $self->{input_encoding};        if defined $self->{input_encoding};
656  ## TODO: |{input_encoding}| is needless?  ## TODO: |{input_encoding}| is needless?
# Line 834  sub new ($) { Line 811  sub new ($) {
811    return $self;    return $self;
812  } # new  } # new
813    
814  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 }  
815    
816  sub AFTER_HTML_IMS () { 0b100 }  sub AFTER_HTML_IMS () { 0b100 }
817  sub HEAD_IMS ()       { 0b1000 }  sub HEAD_IMS ()       { 0b1000 }
# Line 914  sub ROW_IMS ()        { 0b10000000 } Line 822  sub ROW_IMS ()        { 0b10000000 }
822  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
823  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
824  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
825  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
826      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
827      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
828      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
829    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
830        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
831        ## combined with the original insertion mode.  In thie parser,
832        ## they are stored together in the bit-or'ed form.
833    
834    sub IM_MASK () { 0b11111111111 }
835    
836  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
837    
# Line 944  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 858  sub IN_SELECT_IM () { SELECT_IMS | 0b01
858  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
859  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
860    
 ## Implementations MUST act as if state machine in the spec  
   
 sub _initialize_tokenizer ($) {  
   my $self = shift;  
   $self->{state} = DATA_STATE; # MUST  
   #$self->{s_kwd}; # state keyword - initialized when used  
   #$self->{entity__value}; # initialized when used  
   #$self->{entity__match}; # initialized when used  
   $self->{content_model} = PCDATA_CONTENT_MODEL; # be  
   undef $self->{ct}; # current token  
   undef $self->{ca}; # current attribute  
   undef $self->{last_stag_name}; # last emitted start tag name  
   #$self->{prev_state}; # initialized when used  
   delete $self->{self_closing};  
   $self->{char_buffer} = '';  
   $self->{char_buffer_pos} = 0;  
   $self->{nc} = -1; # next input character  
   #$self->{next_nc}  
   !!!next-input-character;  
   $self->{token} = [];  
   # $self->{escape}  
 } # _initialize_tokenizer  
   
 ## A token has:  
 ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,  
 ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  
 ##   ->{name} (DOCTYPE_TOKEN)  
 ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##   ->{pubid} (DOCTYPE_TOKEN)  
 ##   ->{sysid} (DOCTYPE_TOKEN)  
 ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  
 ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##        ->{name}  
 ##        ->{value}  
 ##        ->{has_reference} == 1 or 0  
 ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  
 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.  
 ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  
 ##     while the token is pushed back to the stack.  
   
 ## Emitted token MUST immediately be handled by the tree construction state.  
   
 ## Before each step, UA MAY check to see if either one of the scripts in  
 ## "list of scripts that will execute as soon as possible" or the first  
 ## script in the "list of scripts that will execute asynchronously",  
 ## has completed loading.  If one has, then it MUST be executed  
 ## and removed from the list.  
   
 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)  
 ## (This requirement was dropped from HTML5 spec, unfortunately.)  
   
 my $is_space = {  
   0x0009 => 1, # CHARACTER TABULATION (HT)  
   0x000A => 1, # LINE FEED (LF)  
   #0x000B => 0, # LINE TABULATION (VT)  
   0x000C => 1, # FORM FEED (FF)  
   #0x000D => 1, # CARRIAGE RETURN (CR)  
   0x0020 => 1, # SPACE (SP)  
 };  
   
 sub _get_next_token ($) {  
   my $self = shift;  
   
   if ($self->{self_closing}) {  
     !!!parse-error (type => 'nestc', token => $self->{ct});  
     ## NOTE: The |self_closing| flag is only set by start tag token.  
     ## In addition, when a start tag token is emitted, it is always set to  
     ## |ct|.  
     delete $self->{self_closing};  
   }  
   
   if (@{$self->{token}}) {  
     $self->{self_closing} = $self->{token}->[0]->{self_closing};  
     return shift @{$self->{token}};  
   }  
   
   A: {  
     if ($self->{state} == PCDATA_STATE) {  
       ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.  
   
       if ($self->{nc} == 0x0026) { # &  
         !!!cp (0.1);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity data state".  In this implementation, the tokenizer  
         ## is switched to the |ENTITY_STATE|, which is an implementation  
         ## of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = DATA_STATE;  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003C) { # <  
         !!!cp (0.2);  
         $self->{state} = TAG_OPEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (0.3);  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (0.4);  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       $self->{read_until}->($token->{data}, q[<&], length $token->{data});  
   
       ## Stay in the state.  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == DATA_STATE) {  
       $self->{s_kwd} = '' unless defined $self->{s_kwd};  
       if ($self->{nc} == 0x0026) { # &  
         $self->{s_kwd} = '';  
         if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA  
             not $self->{escape}) {  
           !!!cp (1);  
           ## NOTE: In the spec, the tokenizer is switched to the  
           ## "entity data state".  In this implementation, the tokenizer  
           ## is switched to the |ENTITY_STATE|, which is an implementation  
           ## of the "consume a character reference" algorithm.  
           $self->{entity_add} = -1;  
           $self->{prev_state} = DATA_STATE;  
           $self->{state} = ENTITY_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (2);  
           #  
         }  
       } elsif ($self->{nc} == 0x002D) { # -  
         if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
           $self->{s_kwd} .= '-';  
             
           if ($self->{s_kwd} eq '<!--') {  
             !!!cp (3);  
             $self->{escape} = 1; # unless $self->{escape};  
             $self->{s_kwd} = '--';  
             #  
           } elsif ($self->{s_kwd} eq '---') {  
             !!!cp (4);  
             $self->{s_kwd} = '--';  
             #  
           } else {  
             !!!cp (5);  
             #  
           }  
         }  
           
         #  
       } elsif ($self->{nc} == 0x0021) { # !  
         if (length $self->{s_kwd}) {  
           !!!cp (5.1);  
           $self->{s_kwd} .= '!';  
           #  
         } else {  
           !!!cp (5.2);  
           #$self->{s_kwd} = '';  
           #  
         }  
         #  
       } elsif ($self->{nc} == 0x003C) { # <  
         if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA  
             (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA  
              not $self->{escape})) {  
           !!!cp (6);  
           $self->{state} = TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (7);  
           $self->{s_kwd} = '';  
           #  
         }  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{escape} and  
             ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA  
           if ($self->{s_kwd} eq '--') {  
             !!!cp (8);  
             delete $self->{escape};  
           } else {  
             !!!cp (9);  
           }  
         } else {  
           !!!cp (10);  
         }  
           
         $self->{s_kwd} = '';  
         #  
       } elsif ($self->{nc} == -1) {  
         !!!cp (11);  
         $self->{s_kwd} = '';  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (12);  
         $self->{s_kwd} = '';  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       if ($self->{read_until}->($token->{data}, q[-!<>&],  
                                 length $token->{data})) {  
         $self->{s_kwd} = '';  
       }  
   
       ## Stay in the data state.  
       if ($self->{content_model} == PCDATA_CONTENT_MODEL) {  
         !!!cp (13);  
         $self->{state} = PCDATA_STATE;  
       } else {  
         !!!cp (14);  
         ## Stay in the state.  
       }  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == TAG_OPEN_STATE) {  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if ($self->{nc} == 0x002F) { # /  
           !!!cp (15);  
           !!!next-input-character;  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           redo A;  
         } elsif ($self->{nc} == 0x0021) { # !  
           !!!cp (15.1);  
           $self->{s_kwd} = '<' unless $self->{escape};  
           #  
         } else {  
           !!!cp (16);  
           #  
         }  
   
         ## reconsume  
         $self->{state} = DATA_STATE;  
         !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA  
         if ($self->{nc} == 0x0021) { # !  
           !!!cp (17);  
           $self->{state} = MARKUP_DECLARATION_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x002F) { # /  
           !!!cp (18);  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0041 <= $self->{nc} and  
                  $self->{nc} <= 0x005A) { # A..Z  
           !!!cp (19);  
           $self->{ct}  
             = {type => START_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $self->{line_prev},  
                column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0061 <= $self->{nc} and  
                  $self->{nc} <= 0x007A) { # a..z  
           !!!cp (20);  
           $self->{ct} = {type => START_TAG_TOKEN,  
                                     tag_name => chr ($self->{nc}),  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x003E) { # >  
           !!!cp (21);  
           !!!parse-error (type => 'empty start tag',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<>',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         } elsif ($self->{nc} == 0x003F) { # ?  
           !!!cp (22);  
           !!!parse-error (type => 'pio',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = BOGUS_COMMENT_STATE;  
           $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev},  
                                    };  
           ## $self->{nc} is intentionally left as is  
           redo A;  
         } else {  
           !!!cp (23);  
           !!!parse-error (type => 'bare stago',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           ## reconsume  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         }  
       } else {  
         die "$0: $self->{content_model} in tag open";  
       }  
     } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {  
       ## NOTE: The "close tag open state" in the spec is implemented as  
       ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if (defined $self->{last_stag_name}) {  
           $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;  
           $self->{s_kwd} = '';  
           ## Reconsume.  
           redo A;  
         } else {  
           ## No start tag token has ever been emitted  
           ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.  
           !!!cp (28);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                     line => $l, column => $c,  
                    });  
           redo A;  
         }  
       }  
   
       if (0x0041 <= $self->{nc} and  
           $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (29);  
         $self->{ct}  
             = {type => END_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x007A) { # a..z  
         !!!cp (30);  
         $self->{ct} = {type => END_TAG_TOKEN,  
                                   tag_name => chr ($self->{nc}),  
                                   line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (31);  
         !!!parse-error (type => 'empty end tag',  
                         line => $self->{line_prev}, ## "<" in "</>"  
                         column => $self->{column_prev} - 1);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (32);  
         !!!parse-error (type => 'bare etago');  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                   line => $l, column => $c,  
                  });  
   
         redo A;  
       } else {  
         !!!cp (33);  
         !!!parse-error (type => 'bogus end tag');  
         $self->{state} = BOGUS_COMMENT_STATE;  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev}, # "<" of "</"  
                                   column => $self->{column_prev} - 1,  
                                  };  
         ## NOTE: $self->{nc} is intentionally left as is.  
         ## Although the "anything else" case of the spec not explicitly  
         ## states that the next input character is to be reconsumed,  
         ## it will be included to the |data| of the comment token  
         ## generated from the bogus end tag, as defined in the  
         ## "bogus comment state" entry.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {  
       my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;  
       if (length $ch) {  
         my $CH = $ch;  
         $ch =~ tr/a-z/A-Z/;  
         my $nch = chr $self->{nc};  
         if ($nch eq $ch or $nch eq $CH) {  
           !!!cp (24);  
           ## Stay in the state.  
           $self->{s_kwd} .= $nch;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (25);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         }  
       } else { # after "<{tag-name}"  
         unless ($is_space->{$self->{nc}} or  
                 {  
                  0x003E => 1, # >  
                  0x002F => 1, # /  
                  -1 => 1, # EOF  
                 }->{$self->{nc}}) {  
           !!!cp (26);  
           ## Reconsume.  
           $self->{state} = DATA_STATE;  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (27);  
           $self->{ct}  
               = {type => END_TAG_TOKEN,  
                  tag_name => $self->{last_stag_name},  
                  line => $self->{line_prev},  
                  column => $self->{column_prev} - 1 - length $self->{s_kwd}};  
           $self->{state} = TAG_NAME_STATE;  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == TAG_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (34);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (35);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This should never be reached.  
           #  !!! cp (36);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (37);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (38);  
         $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);  
           # start tag or end tag  
         ## Stay in this state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (39);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This state should never be reached.  
           #  !!! cp (40);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (41);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (42);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (44);  
         $self->{ct}->{tag_name} .= chr $self->{nc};  
           # start tag or end tag  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (45);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (46);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (47);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (48);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (49);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (50);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (52);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (53);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (54);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (55);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (56);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {  
       my $before_leave = sub {  
         if (exists $self->{ct}->{attributes} # start tag or end tag  
             ->{$self->{ca}->{name}}) { # MUST  
           !!!cp (57);  
           !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});  
           ## Discard $self->{ca} # MUST  
         } else {  
           !!!cp (58);  
           $self->{ct}->{attributes}->{$self->{ca}->{name}}  
             = $self->{ca};  
         }  
       }; # $before_leave  
   
       if ($is_space->{$self->{nc}}) {  
         !!!cp (59);  
         $before_leave->();  
         $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (60);  
         $before_leave->();  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (61);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp (62);  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!parse-error (type => 'end tag attribute');  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (63);  
         $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (64);  
         $before_leave->();  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (66);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (67);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (68);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (69);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (70);  
         }  
         $self->{ca}->{name} .= chr ($self->{nc});  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (71);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (72);  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (73);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (74);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (75);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (76);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (77);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (79);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (80);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (81);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (78);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (82);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;          
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (83);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (84);  
         $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (85);  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         ## reconsume  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (86);  
         $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!parse-error (type => 'empty unquoted attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (87);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (88);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (89);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (90);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (91);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (92);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x003D) { # =  
           !!!cp (93);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (94);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (95);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (96);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{prev_state} = $self->{state};  
         $self->{entity_add} = 0x0022; # "  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (97);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (98);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (99);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (100);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (101);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (102);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = 0x0027; # '  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (103);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (104);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (105);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (106);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q['&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (107);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (108);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (109);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (110);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (111);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (112);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (113);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (114);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (115);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (116);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["'=& >],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (118);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (119);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (120);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (121);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (122);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (122.3);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (122.1);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (122.2);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.1');  
         !!!parse-error (type => 'no space between attributes');  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp ('124.2');  
           !!!parse-error (type => 'nestc', token => $self->{ct});  
           ## TODO: Different type than slash in start tag  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp ('124.4');  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp ('124.5');  
           }  
           ## TODO: Test |<title></title/>|  
         } else {  
           !!!cp ('124.3');  
           $self->{self_closing} = 1;  
         }  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (124.7);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (124.5);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (124.6);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.4');  
         !!!parse-error (type => 'nestc');  
         ## TODO: This error type is wrong.  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_COMMENT_STATE) {  
       ## (only happen if PCDATA state)  
   
       ## NOTE: Unlike spec's "bogus comment state", this implementation  
       ## consumes characters one-by-one basis.  
         
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (124);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (125);  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } else {  
         !!!cp (126);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[>],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {  
       ## (only happen if PCDATA state)  
         
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (133);  
         $self->{state} = MD_HYPHEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0044 or # D  
                $self->{nc} == 0x0064) { # d  
         ## ASCII case-insensitive.  
         !!!cp (130);  
         $self->{state} = MD_DOCTYPE_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and  
                $self->{open_elements}->[-1]->[1] & FOREIGN_EL and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.4);                  
         $self->{state} = MD_CDATA_STATE;  
         $self->{s_kwd} = '[';  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (136);  
       }  
   
       !!!parse-error (type => 'bogus comment',  
                       line => $self->{line_prev},  
                       column => $self->{column_prev} - 1);  
       ## Reconsume.  
       $self->{state} = BOGUS_COMMENT_STATE;  
       $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                 line => $self->{line_prev},  
                                 column => $self->{column_prev} - 1,  
                                };  
       redo A;  
     } elsif ($self->{state} == MD_HYPHEN_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (127);  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         $self->{state} = COMMENT_START_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (128);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => '-',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_DOCTYPE_STATE) {  
       ## ASCII case-insensitive.  
       if ($self->{nc} == [  
             undef,  
             0x004F, # O  
             0x0043, # C  
             0x0054, # T  
             0x0059, # Y  
             0x0050, # P  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x006F, # o  
             0x0063, # c  
             0x0074, # t  
             0x0079, # y  
             0x0070, # p  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (131);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 6 and  
                ($self->{nc} == 0x0045 or # E  
                 $self->{nc} == 0x0065)) { # e  
         !!!cp (129);  
         $self->{state} = DOCTYPE_STATE;  
         $self->{ct} = {type => DOCTYPE_TOKEN,  
                                   quirks => 1,  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7,  
                                  };  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (132);          
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_CDATA_STATE) {  
       if ($self->{nc} == {  
             '[' => 0x0043, # C  
             '[C' => 0x0044, # D  
             '[CD' => 0x0041, # A  
             '[CDA' => 0x0054, # T  
             '[CDAT' => 0x0041, # A  
           }->{$self->{s_kwd}}) {  
         !!!cp (135.1);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{s_kwd} eq '[CDATA' and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.2);  
         $self->{ct} = {type => CHARACTER_TOKEN,  
                                   data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7};  
         $self->{state} = CDATA_SECTION_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (135.3);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (137);  
         $self->{state} = COMMENT_START_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (138);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (139);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (140);  
         $self->{ct}->{data} # comment  
             .= chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (141);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (142);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (143);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (144);  
         $self->{ct}->{data} # comment  
             .= '-' . chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (145);  
         $self->{state} = COMMENT_END_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (146);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (147);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[-],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (148);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (149);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (150);  
         $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (151);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == 0x002D) { # -  
         !!!cp (152);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '-'; # comment  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (153);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (154);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (155);  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (156);  
         !!!parse-error (type => 'no space before DOCTYPE name');  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (157);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (158);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (159);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } else {  
         !!!cp (160);  
         $self->{ct}->{name} = chr $self->{nc};  
         delete $self->{ct}->{quirks};  
         $self->{state} = DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_NAME_STATE) {  
 ## ISSUE: Redundant "First," in the spec.  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (161);  
         $self->{state} = AFTER_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (162);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (163);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (164);  
         $self->{ct}->{name}  
           .= chr ($self->{nc}); # DOCTYPE  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (165);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (166);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (167);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == 0x0050 or # P  
                $self->{nc} == 0x0070) { # p  
         $self->{state} = PUBLIC_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0053 or # S  
                $self->{nc} == 0x0073) { # s  
         $self->{state} = SYSTEM_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (180);  
         !!!parse-error (type => 'string after DOCTYPE name');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == PUBLIC_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0055, # U  
             0x0042, # B  
             0x004C, # L  
             0x0049, # I  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0075, # u  
             0x0062, # b  
             0x006C, # l  
             0x0069, # i  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (175);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x0043 or # C  
                 $self->{nc} == 0x0063)) { # c  
         !!!cp (168);  
         $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (169);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == SYSTEM_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0059, # Y  
             0x0053, # S  
             0x0054, # T  
             0x0045, # E  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0079, # y  
             0x0073, # s  
             0x0074, # t  
             0x0065, # e  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (170);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x004D or # M  
                 $self->{nc} == 0x006D)) { # m  
         !!!cp (171);  
         $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (172);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (181);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0022) { # "  
         !!!cp (182);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0027) { # '  
         !!!cp (183);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x003E) { # >  
         !!!cp (184);  
         !!!parse-error (type => 'no PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (185);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (186);  
         !!!parse-error (type => 'string after PUBLIC');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (187);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (188);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (189);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (190);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q[">],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (191);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (192);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (193);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (194);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q['>],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (195);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (196);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (197);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (198);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (199);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (200);  
         !!!parse-error (type => 'string after PUBLIC literal');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (201);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (202);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (203);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (204);  
         !!!parse-error (type => 'no SYSTEM literal');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (205);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (206);  
         !!!parse-error (type => 'string after SYSTEM');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (207);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (208);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (209);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (210);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q[">],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (211);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (212);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (213);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (214);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q['>],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (215);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (216);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (217);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (218);  
         !!!parse-error (type => 'string after SYSTEM literal');  
         #$self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (219);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (220);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (221);  
         my $s = '';  
         $self->{read_until}->($s, q[>], 0);  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_STATE) {  
       ## NOTE: "CDATA section state" in the state is jointly implemented  
       ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,  
       ## and |CDATA_SECTION_MSE2_STATE|.  
         
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.1);  
         $self->{state} = CDATA_SECTION_MSE1_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.2);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.3);  
           ## No token to emit. $self->{ct} is discarded.  
         }          
         redo A;  
       } else {  
         !!!cp (221.4);  
         $self->{ct}->{data} .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{data},  
                               q<]>,  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
   
       ## ISSUE: "text tokens" in spec.  
     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {  
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.5);  
         $self->{state} = CDATA_SECTION_MSE2_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.6);  
         $self->{ct}->{data} .= ']';  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.7);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.8);  
           ## No token to emit. $self->{ct} is discarded.  
         }  
         redo A;  
       } elsif ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.9); # character  
         $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.11);  
         $self->{ct}->{data} .= ']]'; # character  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_STATE) {  
       if ($is_space->{$self->{nc}} or  
           {  
             0x003C => 1, 0x0026 => 1, -1 => 1, # <, &  
             $self->{entity_add} => 1,  
           }->{$self->{nc}}) {  
         !!!cp (1001);  
         ## Don't consume  
         ## No error  
         ## Return nothing.  
         #  
       } elsif ($self->{nc} == 0x0023) { # #  
         !!!cp (999);  
         $self->{state} = ENTITY_HASH_STATE;  
         $self->{s_kwd} = '#';  
         !!!next-input-character;  
         redo A;  
       } elsif ((0x0041 <= $self->{nc} and  
                 $self->{nc} <= 0x005A) or # A..Z  
                (0x0061 <= $self->{nc} and  
                 $self->{nc} <= 0x007A)) { # a..z  
         !!!cp (998);  
         require Whatpm::_NamedEntityList;  
         $self->{state} = ENTITY_NAME_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         $self->{entity__value} = $self->{s_kwd};  
         $self->{entity__match} = 0;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (1027);  
         !!!parse-error (type => 'bare ero');  
         ## Return nothing.  
         #  
       }  
   
       ## NOTE: No character is consumed by the "consume a character  
       ## reference" algorithm.  In other word, there is an "&" character  
       ## that does not introduce a character reference, which would be  
       ## appended to the parent element or the attribute value in later  
       ## process of the tokenizer.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (997);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } else {  
         !!!cp (996);  
         $self->{ca}->{value} .= '&';  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_HASH_STATE) {  
       if ($self->{nc} == 0x0078 or # x  
           $self->{nc} == 0x0058) { # X  
         !!!cp (995);  
         $self->{state} = HEXREF_X_STATE;  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0030 <= $self->{nc} and  
                $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (994);  
         $self->{state} = NCR_NUM_STATE;  
         $self->{s_kwd} = $self->{nc} - 0x0030;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare nero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" is appended to the parent element or the attribute  
         ## value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1019);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&#',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1,  
                    });  
           redo A;  
         } else {  
           !!!cp (993);  
           $self->{ca}->{value} .= '&#';  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == NCR_NUM_STATE) {  
       if (0x0030 <= $self->{nc} and  
           $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (1012);  
         $self->{s_kwd} *= 10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
           
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1013);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1014);  
         !!!parse-error (type => 'no refc');  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1015);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1016);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (992);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (991);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == HEXREF_X_STATE) {  
       if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or  
           (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or  
           (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {  
         # 0..9, A..F, a..f  
         !!!cp (990);  
         $self->{state} = HEXREF_HEX_STATE;  
         $self->{s_kwd} = 0;  
         ## Reconsume.  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare hcro',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" followed by "X" or "x" is appended to the parent  
         ## element or the attribute value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1005);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (989);  
           $self->{ca}->{value} .= '&' . $self->{s_kwd};  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == HEXREF_HEX_STATE) {  
       if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {  
         # 0..9  
         !!!cp (1002);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x0066) { # a..f  
         !!!cp (1003);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0060 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x0046) { # A..F  
         !!!cp (1004);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0040 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1006);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1007);  
         !!!parse-error (type => 'no refc',  
                         line => $self->{line},  
                         column => $self->{column});  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1008);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1009);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (988);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (987);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_NAME_STATE) {  
       if (length $self->{s_kwd} < 30 and  
           ## NOTE: Some number greater than the maximum length of entity name  
           ((0x0041 <= $self->{nc} and # a  
             $self->{nc} <= 0x005A) or # x  
            (0x0061 <= $self->{nc} and # a  
             $self->{nc} <= 0x007A) or # z  
            (0x0030 <= $self->{nc} and # 0  
             $self->{nc} <= 0x0039) or # 9  
            $self->{nc} == 0x003B)) { # ;  
         our $EntityChar;  
         $self->{s_kwd} .= chr $self->{nc};  
         if (defined $EntityChar->{$self->{s_kwd}}) {  
           if ($self->{nc} == 0x003B) { # ;  
             !!!cp (1020);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = 1;  
             !!!next-input-character;  
             #  
           } else {  
             !!!cp (1021);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = -1;  
             ## Stay in the state.  
             !!!next-input-character;  
             redo A;  
           }  
         } else {  
           !!!cp (1022);  
           $self->{entity__value} .= chr $self->{nc};  
           $self->{entity__match} *= 2;  
           ## Stay in the state.  
           !!!next-input-character;  
           redo A;  
         }  
       }  
   
       my $data;  
       my $has_ref;  
       if ($self->{entity__match} > 0) {  
         !!!cp (1023);  
         $data = $self->{entity__value};  
         $has_ref = 1;  
         #  
       } elsif ($self->{entity__match} < 0) {  
         !!!parse-error (type => 'no refc');  
         if ($self->{prev_state} != DATA_STATE and # in attribute  
             $self->{entity__match} < -1) {  
           !!!cp (1024);  
           $data = '&' . $self->{s_kwd};  
           #  
         } else {  
           !!!cp (1025);  
           $data = $self->{entity__value};  
           $has_ref = 1;  
           #  
         }  
       } else {  
         !!!cp (1026);  
         !!!parse-error (type => 'bare ero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - length $self->{s_kwd});  
         $data = '&' . $self->{s_kwd};  
         #  
       }  
     
       ## NOTE: In these cases, when a character reference is found,  
       ## it is consumed and a character token is returned, or, otherwise,  
       ## nothing is consumed and returned, according to the spec algorithm.  
       ## In this implementation, anything that has been examined by the  
       ## tokenizer is appended to the parent element or the attribute value  
       ## as string, either literal string when no character reference or  
       ## entity-replaced string otherwise, in this stage, since any characters  
       ## that would not be consumed are appended in the data state or in an  
       ## appropriate attribute value state anyway.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (986);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN,  
                   data => $data,  
                   line => $self->{line_prev},  
                   column => $self->{column_prev} + 1 - length $self->{s_kwd},  
                  });  
         redo A;  
       } else {  
         !!!cp (985);  
         $self->{ca}->{value} .= $data;  
         $self->{ca}->{has_reference} = 1 if $has_ref;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } else {  
       die "$0: $self->{state}: Unknown state";  
     }  
   } # A    
   
   die "$0: _get_next_token: unexpected case";  
 } # _get_next_token  
   
861  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
862    my $self = shift;    my $self = shift;
863    ## NOTE: $self->{document} MUST be specified before this method is called    ## NOTE: $self->{document} MUST be specified before this method is called
# Line 3467  sub _construct_tree ($) { Line 886  sub _construct_tree ($) {
886    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
887    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
888    ## 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  
889        
890    !!!next-token;    !!!next-token;
891    
892    undef $self->{form_element};    undef $self->{form_element};
893    undef $self->{head_element};    undef $self->{head_element};
894      undef $self->{head_element_inserted};
895    $self->{open_elements} = [];    $self->{open_elements} = [];
896    undef $self->{inner_html_node};    undef $self->{inner_html_node};
897      undef $self->{ignore_newline};
898    
899    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
900    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3497  sub _tree_construction_initial ($) { Line 914  sub _tree_construction_initial ($) {
914    
915    INITIAL: {    INITIAL: {
916      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
917        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
918        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
919        ## language.        ## another language.  (We don't support such mode switchings; it
920          ## is nonsense to do anything different from what browsers do.)
921        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
922        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
923        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        my $doctype = $self->{document}->create_document_type_definition
924        if (not defined $token->{name} or # <!DOCTYPE>            ($doctype_name);
925            defined $token->{sysid}) {  
926          $doctype_name =~ tr/A-Z/a-z/; # ASCII case-insensitive
927          if ($doctype_name ne 'html') {
928          !!!cp ('t1');          !!!cp ('t1');
929          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
930        } elsif ($doctype_name ne 'HTML') {        } elsif (defined $token->{pubid}) {
931          !!!cp ('t2');          !!!cp ('t2');
932            ## XXX Obsolete permitted DOCTYPEs
933          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
934        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{sysid}) {
935          if ($token->{pubid} eq 'XSLT-compat') {          if ($token->{sysid} eq 'about:legacy-compat') {
936            !!!cp ('t1.2');            !!!cp ('t1.2'); ## <!DOCTYPE HTML SYSTEM "about:legacy-compat">
937            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
938                            level => $self->{level}->{should});                            level => $self->{level}->{should});
939          } else {          } else {
940            !!!parse-error (type => 'not HTML5', token => $token);            !!!parse-error (type => 'not HTML5', token => $token);
941          }          }
942        } else {        } else { ## <!DOCTYPE HTML>
943          !!!cp ('t3');          !!!cp ('t3');
944          #          #
945        }        }
946                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
947        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
948        ## 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.
949        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
950        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
951    
952        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
953        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
954          ## string, while |null| is an allowed value for the attribute
955          ## according to DOM3 Core.
956        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
957                
958        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'html') {
959          !!!cp ('t4');          !!!cp ('t4');
960          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
961        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{pubid}) {
# Line 3818  sub _reset_insertion_mode ($) { Line 1240  sub _reset_insertion_mode ($) {
1240          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1241          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1242          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1243        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1244          if ($last) {          if ($last) {
1245            !!!cp ('t28.2');            !!!cp ('t28.2');
1246            #            #
# Line 3847  sub _reset_insertion_mode ($) { Line 1269  sub _reset_insertion_mode ($) {
1269        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1270                
1271        ## Step 15        ## Step 15
1272        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1273          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1274            !!!cp ('t29');            !!!cp ('t29');
1275            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3979  sub _tree_construction_main ($) { Line 1401  sub _tree_construction_main ($) {
1401    
1402      ## Step 1      ## Step 1
1403      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1404      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1405    
1406      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1407      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1408      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1409    
1410      ## Step 4      ## Step 3, 4
1411      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;  
     }  
1412    
1413      ## 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";  
       }  
     }  
1414      !!!next-token;      !!!next-token;
1415    }; # $parse_rcdata    }; # $parse_rcdata
1416    
1417    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1418        ## Step 1
1419      my $script_el;      my $script_el;
1420      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1421    
1422        ## Step 2
1423      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1424    
1425        ## Step 3
1426        ## TODO: Mark as "already executed", if ...
1427    
1428        ## Step 4 (HTML5 revision 2702)
1429        $insert->($script_el);
1430        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1431    
1432        ## Step 5
1433      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1434      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;  
1435    
1436      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1437          $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  
1438    
1439        $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...  
     }  
       
1440      !!!next-token;      !!!next-token;
1441    }; # $script_start_tag    }; # $script_start_tag
1442    
1443    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1444    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag (OBSOLETE; unused).
1445      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1446    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1447    
1448    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4167  sub _tree_construction_main ($) { Line 1527  sub _tree_construction_main ($) {
1527            !!!cp ('t59');            !!!cp ('t59');
1528            $furthest_block = $node;            $furthest_block = $node;
1529            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1530              ## NOTE: The topmost (eldest) node.
1531          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1532            !!!cp ('t60');            !!!cp ('t60');
1533            last OE;            last OE;
# Line 4250  sub _tree_construction_main ($) { Line 1611  sub _tree_construction_main ($) {
1611                
1612        ## Step 8        ## Step 8
1613        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
1614            ## Foster parenting.
1615          my $foster_parent_element;          my $foster_parent_element;
1616          my $next_sibling;          my $next_sibling;
1617          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1618            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1619                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;              !!!cp ('t65.2');
1620                               if (defined $parent and $parent->node_type == 1) {              $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1621                                 !!!cp ('t65.1');              $next_sibling = $self->{open_elements}->[$_]->[0];
1622                                 $foster_parent_element = $parent;              undef $next_sibling
1623                                 $next_sibling = $self->{open_elements}->[$_]->[0];                  unless $next_sibling->parent_node eq $foster_parent_element;
1624                               } else {              last OE;
1625                                 !!!cp ('t65.2');            }
1626                                 $foster_parent_element          } # OE
1627                                   = $self->{open_elements}->[$_ - 1]->[0];          $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1628                               }  
                              last OE;  
                            }  
                          } # OE  
                          $foster_parent_element = $self->{open_elements}->[0]->[0]  
                            unless defined $foster_parent_element;  
1629          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);          $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
1630          $open_tables->[-1]->[1] = 1; # tainted          $open_tables->[-1]->[1] = 1; # tainted
1631        } else {        } else {
# Line 4313  sub _tree_construction_main ($) { Line 1670  sub _tree_construction_main ($) {
1670            $i = $_;            $i = $_;
1671          }          }
1672        } # OE        } # OE
1673        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1674                
1675        ## Step 14        ## Step 14
1676        redo FET;        redo FET;
# Line 4324  sub _tree_construction_main ($) { Line 1681  sub _tree_construction_main ($) {
1681      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
1682    }; # $insert_to_current    }; # $insert_to_current
1683    
1684      ## Foster parenting.  Note that there are three "foster parenting"
1685      ## code in the parser: for elements (this one), for texts, and for
1686      ## elements in the AAA code.
1687    my $insert_to_foster = sub {    my $insert_to_foster = sub {
1688      my $child = shift;      my $child = shift;
1689      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
# Line 4331  sub _tree_construction_main ($) { Line 1691  sub _tree_construction_main ($) {
1691        my $foster_parent_element;        my $foster_parent_element;
1692        my $next_sibling;        my $next_sibling;
1693        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1694          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1695                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;            !!!cp ('t71');
1696                               if (defined $parent and $parent->node_type == 1) {            $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1697                                 !!!cp ('t70');            $next_sibling = $self->{open_elements}->[$_]->[0];
1698                                 $foster_parent_element = $parent;            undef $next_sibling
1699                                 $next_sibling = $self->{open_elements}->[$_]->[0];                unless $next_sibling->parent_node eq $foster_parent_element;
1700                               } else {            last OE;
1701                                 !!!cp ('t71');          }
1702                                 $foster_parent_element        } # OE
1703                                   = $self->{open_elements}->[$_ - 1]->[0];        $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1704                               }  
1705                               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);  
1706        $open_tables->[-1]->[1] = 1; # tainted        $open_tables->[-1]->[1] = 1; # tainted
1707      } else {      } else {
1708        !!!cp ('t72');        !!!cp ('t72');
# Line 4356  sub _tree_construction_main ($) { Line 1710  sub _tree_construction_main ($) {
1710      }      }
1711    }; # $insert_to_foster    }; # $insert_to_foster
1712    
1713      ## NOTE: Insert a character (MUST): When a character is inserted, if
1714      ## the last node that was inserted by the parser is a Text node and
1715      ## the character has to be inserted after that node, then the
1716      ## character is appended to the Text node.  However, if any other
1717      ## node is inserted by the parser, then a new Text node is created
1718      ## and the character is appended as that Text node.  If I'm not
1719      ## wrong, for a parser with scripting disabled, there are only two
1720      ## cases where this occurs.  One is the case where an element node
1721      ## is inserted to the |head| element.  This is covered by using the
1722      ## |$self->{head_element_inserted}| flag.  Another is the case where
1723      ## an element or comment is inserted into the |table| subtree while
1724      ## foster parenting happens.  This is covered by using the [2] flag
1725      ## of the |$open_tables| structure.  All other cases are handled
1726      ## simply by calling |manakai_append_text| method.
1727    
1728      ## TODO: |<body><script>document.write("a<br>");
1729      ## document.body.removeChild (document.body.lastChild);
1730      ## document.write ("b")</script>|
1731    
1732    B: while (1) {    B: while (1) {
1733    
1734        ## The "in table text" insertion mode.
1735        if ($self->{insertion_mode} & TABLE_IMS and
1736            not $self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
1737            not $self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1738          C: {
1739            my $s;
1740            if ($token->{type} == CHARACTER_TOKEN) {
1741              !!!cp ('t194');
1742              $self->{pending_chars} ||= [];
1743              push @{$self->{pending_chars}}, $token;
1744              !!!next-token;
1745              next B;
1746            } else {
1747              if ($self->{pending_chars}) {
1748                $s = join '', map { $_->{data} } @{$self->{pending_chars}};
1749                delete $self->{pending_chars};
1750                if ($s =~ /[^\x09\x0A\x0C\x0D\x20]/) {
1751                  !!!cp ('t195');
1752                  #
1753                } else {
1754                  !!!cp ('t195.1');
1755                  #$self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1756                  $self->{open_elements}->[-1]->[0]->append_child
1757                      ($self->{document}->create_text_node ($s));
1758                  last C;
1759                }
1760              } else {
1761                !!!cp ('t195.2');
1762                last C;
1763              }
1764            }
1765    
1766            ## Foster parenting.
1767            !!!parse-error (type => 'in table:#text', token => $token);
1768    
1769            ## NOTE: As if in body, but insert into the foster parent element.
1770            $reconstruct_active_formatting_elements->($insert_to_foster);
1771                
1772            if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
1773              # MUST
1774              my $foster_parent_element;
1775              my $next_sibling;
1776              OE: for (reverse 0..$#{$self->{open_elements}}) {
1777                if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1778                  !!!cp ('t197');
1779                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
1780                  $next_sibling = $self->{open_elements}->[$_]->[0];
1781                  undef $next_sibling
1782                    unless $next_sibling->parent_node eq $foster_parent_element;
1783                  last OE;
1784                }
1785              } # OE
1786              $foster_parent_element ||= $self->{open_elements}->[0]->[0];
1787    
1788              !!!cp ('t199');
1789              $foster_parent_element->insert_before
1790                  ($self->{document}->create_text_node ($s), $next_sibling);
1791    
1792              $open_tables->[-1]->[1] = 1; # tainted
1793              $open_tables->[-1]->[2] = 1; # ~node inserted
1794            } else {
1795              ## NOTE: Fragment case or in a foster parent'ed element
1796              ## (e.g. |<table><span>a|).  In fragment case, whether the
1797              ## character is appended to existing node or a new node is
1798              ## created is irrelevant, since the foster parent'ed nodes
1799              ## are discarded and fragment parsing does not invoke any
1800              ## script.
1801              !!!cp ('t200');
1802              $self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
1803            }
1804          } # C
1805        } # TABLE_IMS
1806    
1807      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1808        !!!cp ('t73');        !!!cp ('t73');
1809        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
# Line 4403  sub _tree_construction_main ($) { Line 1850  sub _tree_construction_main ($) {
1850        } else {        } else {
1851          !!!cp ('t87');          !!!cp ('t87');
1852          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1853            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1854        }        }
1855        !!!next-token;        !!!next-token;
1856        next B;        next B;
1857        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1858          if ($token->{type} == CHARACTER_TOKEN) {
1859            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1860            delete $self->{ignore_newline};
1861    
1862            if (length $token->{data}) {
1863              !!!cp ('t43');
1864              $self->{open_elements}->[-1]->[0]->manakai_append_text
1865                  ($token->{data});
1866            } else {
1867              !!!cp ('t43.1');
1868            }
1869            !!!next-token;
1870            next B;
1871          } elsif ($token->{type} == END_TAG_TOKEN) {
1872            delete $self->{ignore_newline};
1873    
1874            if ($token->{tag_name} eq 'script') {
1875              !!!cp ('t50');
1876              
1877              ## Para 1-2
1878              my $script = pop @{$self->{open_elements}};
1879              
1880              ## Para 3
1881              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1882    
1883              ## Para 4
1884              ## TODO: $old_insertion_point = $current_insertion_point;
1885              ## TODO: $current_insertion_point = just before $self->{nc};
1886    
1887              ## Para 5
1888              ## TODO: Run the $script->[0].
1889    
1890              ## Para 6
1891              ## TODO: $current_insertion_point = $old_insertion_point;
1892    
1893              ## Para 7
1894              ## TODO: if ($pending_external_script) {
1895                ## TODO: ...
1896              ## TODO: }
1897    
1898              !!!next-token;
1899              next B;
1900            } else {
1901              !!!cp ('t42');
1902    
1903              pop @{$self->{open_elements}};
1904    
1905              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1906              !!!next-token;
1907              next B;
1908            }
1909          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1910            delete $self->{ignore_newline};
1911    
1912            !!!cp ('t44');
1913            !!!parse-error (type => 'not closed',
1914                            text => $self->{open_elements}->[-1]->[0]
1915                                ->manakai_local_name,
1916                            token => $token);
1917    
1918            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1919            #  ## TODO: Mark as "already executed"
1920            #}
1921    
1922            pop @{$self->{open_elements}};
1923    
1924            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1925            ## Reprocess.
1926            next B;
1927          } else {
1928            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1929          }
1930      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1931        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1932          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4417  sub _tree_construction_main ($) { Line 1938  sub _tree_construction_main ($) {
1938               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1939              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1940              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1941               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1942            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1943            !!!cp ('t87.2');            !!!cp ('t87.2');
1944            #            #
1945          } elsif ({          } elsif ({
1946                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1947                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1948                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1949                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1950                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1951                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1952                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1953                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1954                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1955                     ($token->{tag_name} eq 'font' and
1956                      ($token->{attributes}->{color} or
1957                       $token->{attributes}->{face} or
1958                       $token->{attributes}->{size}))) {
1959            !!!cp ('t87.2');            !!!cp ('t87.2');
1960            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1961                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4506  sub _tree_construction_main ($) { Line 2031  sub _tree_construction_main ($) {
2031          }          }
2032        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
2033          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
2034          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
2035          #            !!!cp ('t87.41');
2036              #
2037              ## XXXscript: Execute script here.
2038            } else {
2039              !!!cp ('t87.5');
2040              #
2041            }
2042        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2043          !!!cp ('t87.6');          !!!cp ('t87.6');
2044          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4532  sub _tree_construction_main ($) { Line 2063  sub _tree_construction_main ($) {
2063        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
2064          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
2065            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2066              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
2067              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
2068              #                $self->{open_elements}->[-1]->[0]->append_child
2069                    ($self->{document}->create_text_node ($1));
2070                  delete $self->{head_element_inserted};
2071                  ## NOTE: |</head> <link> |
2072                  #
2073                } else {
2074                  !!!cp ('t88.2');
2075                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2076                  ## NOTE: |</head> &#x20;|
2077                  #
2078                }
2079            } else {            } else {
2080              !!!cp ('t88.1');              !!!cp ('t88.1');
2081              ## Ignore the token.              ## Ignore the token.
# Line 4630  sub _tree_construction_main ($) { Line 2171  sub _tree_construction_main ($) {
2171            !!!cp ('t97');            !!!cp ('t97');
2172          }          }
2173    
2174              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2175                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2176                  !!!cp ('t98');              !!!cp ('t98');
2177                  ## As if </noscript>              ## As if </noscript>
2178                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2179                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2180                                  token => $token);                              token => $token);
2181                            
2182                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2183                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2184                } else {            } else {
2185                  !!!cp ('t99');              !!!cp ('t99');
2186                }            }
2187    
2188                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2189                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2190                  !!!cp ('t100');              !!!cp ('t100');
2191                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2192                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2193                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2194                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2195                } else {              $self->{head_element_inserted} = 1;
2196                  !!!cp ('t101');            } else {
2197                }              !!!cp ('t101');
2198                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2199                pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2200                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2201                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2202                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2203                !!!next-token;            !!!nack ('t101.1');
2204                next B;            !!!next-token;
2205              next B;
2206          } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
2207            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2208            if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
# Line 4669  sub _tree_construction_main ($) { Line 2211  sub _tree_construction_main ($) {
2211                              text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2212              push @{$self->{open_elements}},              push @{$self->{open_elements}},
2213                  [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2214                $self->{head_element_inserted} = 1;
2215            } else {            } else {
2216              !!!cp ('t103');              !!!cp ('t103');
2217            }            }
# Line 4679  sub _tree_construction_main ($) { Line 2222  sub _tree_construction_main ($) {
2222            !!!ack ('t103.1');            !!!ack ('t103.1');
2223            !!!next-token;            !!!next-token;
2224            next B;            next B;
2225          } elsif ($token->{tag_name} eq 'command' or          } elsif ($token->{tag_name} eq 'command') {
                  $token->{tag_name} eq 'eventsource') {  
2226            if ($self->{insertion_mode} == IN_HEAD_IM) {            if ($self->{insertion_mode} == IN_HEAD_IM) {
2227              ## NOTE: If the insertion mode at the time of the emission              ## NOTE: If the insertion mode at the time of the emission
2228              ## of the token was "before head", $self->{insertion_mode}              ## of the token was "before head", $self->{insertion_mode}
# Line 4701  sub _tree_construction_main ($) { Line 2243  sub _tree_construction_main ($) {
2243              !!!cp ('t103.3');              !!!cp ('t103.3');
2244              #              #
2245            }            }
2246              } elsif ($token->{tag_name} eq 'meta') {          } elsif ($token->{tag_name} eq 'meta') {
2247                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2248                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2249                  !!!cp ('t104');              !!!cp ('t104');
2250                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2251                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2252                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2253                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2254                } else {              $self->{head_element_inserted} = 1;
2255                  !!!cp ('t105');            } else {
2256                }              !!!cp ('t105');
2257                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2258                my $meta_el = pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2259              my $meta_el = pop @{$self->{open_elements}};
2260    
2261                unless ($self->{confident}) {                unless ($self->{confident}) {
2262                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4771  sub _tree_construction_main ($) { Line 2314  sub _tree_construction_main ($) {
2314                !!!ack ('t110.1');                !!!ack ('t110.1');
2315                !!!next-token;                !!!next-token;
2316                next B;                next B;
2317              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2318                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2319                  !!!cp ('t111');              !!!cp ('t111');
2320                  ## As if </noscript>              ## As if </noscript>
2321                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2322                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2323                                  token => $token);                              token => $token);
2324                            
2325                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2326                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2327                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2328                  !!!cp ('t112');              !!!cp ('t112');
2329                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2330                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2331                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2332                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2333                } else {              $self->{head_element_inserted} = 1;
2334                  !!!cp ('t113');            } else {
2335                }              !!!cp ('t113');
2336              }
2337    
2338                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2339                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2340                    : $self->{open_elements}->[-1]->[0];  
2341                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2342                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2343                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2344                next B;            ## |head| element is inserted only for the purpose of
2345              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2346                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2347                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2348                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2349                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2350                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2351                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2352                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2353                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2354                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2355                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2356                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2357                  !!!cp ('t115');              !!!cp ('t114');
2358                }              !!!parse-error (type => 'after head',
2359                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2360                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2361                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2362                next B;              $self->{head_element_inserted} = 1;
2363              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2364                !!!cp ('t115');
2365              }
2366              $parse_rcdata->(CDATA_CONTENT_MODEL);
2367              ## ISSUE: A spec bug [Bug 6038]
2368              splice @{$self->{open_elements}}, -2, 1, () # <head>
2369                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2370              next B;
2371            } elsif ($token->{tag_name} eq 'noscript') {
2372                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2373                  !!!cp ('t116');                  !!!cp ('t116');
2374                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4837  sub _tree_construction_main ($) { Line 2389  sub _tree_construction_main ($) {
2389                  !!!cp ('t118');                  !!!cp ('t118');
2390                  #                  #
2391                }                }
2392              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2393                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2394                  !!!cp ('t119');              !!!cp ('t119');
2395                  ## As if </noscript>              ## As if </noscript>
2396                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2397                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2398                                  token => $token);                              token => $token);
2399                            
2400                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2401                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2402                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2403                  !!!cp ('t120');              !!!cp ('t120');
2404                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2405                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2406                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2407                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2408                } else {              $self->{head_element_inserted} = 1;
2409                  !!!cp ('t121');            } else {
2410                }              !!!cp ('t121');
2411              }
2412    
2413                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2414                $script_start_tag->();            $script_start_tag->();
2415                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2416                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2417                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2418              } elsif ($token->{tag_name} eq 'body' or            next B;
2419                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2420                     $token->{tag_name} eq 'frameset') {
2421                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2422                  !!!cp ('t122');                  !!!cp ('t122');
2423                  ## As if </noscript>                  ## As if </noscript>
# Line 4998  sub _tree_construction_main ($) { Line 2552  sub _tree_construction_main ($) {
2552              } elsif ({              } elsif ({
2553                        body => 1, html => 1,                        body => 1, html => 1,
2554                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2555                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2556                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2557                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2558                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2559                  !!!cp ('t140');                  !!!cp ('t140');
# Line 5024  sub _tree_construction_main ($) { Line 2579  sub _tree_construction_main ($) {
2579                ## Ignore the token                ## Ignore the token
2580                !!!next-token;                !!!next-token;
2581                next B;                next B;
2582              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2583                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2584                  !!!cp ('t142.2');              !!!cp ('t142.2');
2585                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2586                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2587                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2588                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2589        
2590                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2591                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2592                  !!!cp ('t143.2');              !!!cp ('t143.2');
2593                  ## As if </head>              ## As if </head>
2594                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2595                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2596        
2597                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2598                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2599                  !!!cp ('t143.3');              !!!cp ('t143.3');
2600                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2601                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2602                                  text => 'br', token => $token);                              text => 'br', token => $token);
2603                  ## As if </noscript>              ## As if </noscript>
2604                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2605                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2606    
2607                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2608                  ## As if </head>              ## As if </head>
2609                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2610                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2611    
2612                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2613                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2614                  !!!cp ('t143.4');              !!!cp ('t143.4');
2615                  #              #
2616                } else {            } else {
2617                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2618                }            }
2619    
2620                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2621                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2622                !!!cp ('t145');                !!!cp ('t145');
2623                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2624                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5112  sub _tree_construction_main ($) { Line 2662  sub _tree_construction_main ($) {
2662              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2663              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2664              ## reprocess              ## reprocess
2665              next B;          next B;
2666        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2667          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2668            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5185  sub _tree_construction_main ($) { Line 2735  sub _tree_construction_main ($) {
2735                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2736                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2737                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2738                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2739                  ## have an element in table scope                  ## have an element in table scope
2740                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2741                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2742                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2743                      !!!cp ('t151');                      !!!cp ('t151');
2744    
2745                      ## Close the cell                      ## Close the cell
# Line 5213  sub _tree_construction_main ($) { Line 2763  sub _tree_construction_main ($) {
2763                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2764                  !!!next-token;                  !!!next-token;
2765                  next B;                  next B;
2766                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2767                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2768                                  token => $token);                                  token => $token);
2769                                    
# Line 5223  sub _tree_construction_main ($) { Line 2773  sub _tree_construction_main ($) {
2773                  INSCOPE: {                  INSCOPE: {
2774                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2775                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2776                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2777                        !!!cp ('t155');                        !!!cp ('t155');
2778                        $i = $_;                        $i = $_;
2779                        last INSCOPE;                        last INSCOPE;
# Line 5249  sub _tree_construction_main ($) { Line 2799  sub _tree_construction_main ($) {
2799                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2800                  }                  }
2801    
2802                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2803                    !!!cp ('t159');                    !!!cp ('t159');
2804                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2805                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5278  sub _tree_construction_main ($) { Line 2828  sub _tree_construction_main ($) {
2828              }              }
2829            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2830              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2831                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2832                  ## have an element in table scope                  ## have an element in table scope
2833                  my $i;                  my $i;
2834                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5328  sub _tree_construction_main ($) { Line 2878  sub _tree_construction_main ($) {
2878                                    
2879                  !!!next-token;                  !!!next-token;
2880                  next B;                  next B;
2881                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2882                  !!!cp ('t169');                  !!!cp ('t169');
2883                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2884                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5340  sub _tree_construction_main ($) { Line 2890  sub _tree_construction_main ($) {
2890                  #                  #
2891                }                }
2892              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2893                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2894                  ## have a table element in table scope                  ## have a table element in table scope
2895                  my $i;                  my $i;
2896                  INSCOPE: {                  INSCOPE: {
2897                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2898                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2899                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2900                        !!!cp ('t171');                        !!!cp ('t171');
2901                        $i = $_;                        $i = $_;
2902                        last INSCOPE;                        last INSCOPE;
# Line 5371  sub _tree_construction_main ($) { Line 2921  sub _tree_construction_main ($) {
2921                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2922                  }                  }
2923                                    
2924                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2925                    !!!cp ('t175');                    !!!cp ('t175');
2926                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2927                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5389  sub _tree_construction_main ($) { Line 2939  sub _tree_construction_main ($) {
2939                                    
2940                  !!!next-token;                  !!!next-token;
2941                  next B;                  next B;
2942                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2943                  !!!cp ('t177');                  !!!cp ('t177');
2944                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2945                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5404  sub _tree_construction_main ($) { Line 2954  sub _tree_construction_main ($) {
2954                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2955                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2956                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2957                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2958                ## have an element in table scope                ## have an element in table scope
2959                my $i;                my $i;
2960                my $tn;                my $tn;
# Line 5421  sub _tree_construction_main ($) { Line 2971  sub _tree_construction_main ($) {
2971                                line => $token->{line},                                line => $token->{line},
2972                                column => $token->{column}};                                column => $token->{column}};
2973                      next B;                      next B;
2974                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2975                      !!!cp ('t180');                      !!!cp ('t180');
2976                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2977                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5441  sub _tree_construction_main ($) { Line 2991  sub _tree_construction_main ($) {
2991                  next B;                  next B;
2992                } # INSCOPE                } # INSCOPE
2993              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2994                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2995                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2996                                token => $token);                                token => $token);
2997    
# Line 5450  sub _tree_construction_main ($) { Line 3000  sub _tree_construction_main ($) {
3000                my $i;                my $i;
3001                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3002                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3003                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
3004                    !!!cp ('t184');                    !!!cp ('t184');
3005                    $i = $_;                    $i = $_;
3006                    last INSCOPE;                    last INSCOPE;
# Line 5461  sub _tree_construction_main ($) { Line 3011  sub _tree_construction_main ($) {
3011                } # INSCOPE                } # INSCOPE
3012                unless (defined $i) {                unless (defined $i) {
3013                  !!!cp ('t186');                  !!!cp ('t186');
3014            ## TODO: Wrong error type?
3015                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3016                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
3017                  ## Ignore the token                  ## Ignore the token
# Line 5474  sub _tree_construction_main ($) { Line 3025  sub _tree_construction_main ($) {
3025                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3026                }                }
3027    
3028                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
3029                  !!!cp ('t188');                  !!!cp ('t188');
3030                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
3031                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5506  sub _tree_construction_main ($) { Line 3057  sub _tree_construction_main ($) {
3057                  !!!cp ('t191');                  !!!cp ('t191');
3058                  #                  #
3059                }                }
3060              } elsif ({          } elsif ({
3061                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
3062                        thead => 1, tr => 1,                    thead => 1, tr => 1,
3063                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
3064                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
3065                !!!cp ('t192');            !!!cp ('t192');
3066                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
3067                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
3068                ## Ignore the token            ## Ignore the token
3069                !!!next-token;            !!!next-token;
3070                next B;            next B;
3071              } else {          } else {
3072                !!!cp ('t193');            !!!cp ('t193');
3073                #            #
3074              }          }
3075        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3076          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3077            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5539  sub _tree_construction_main ($) { Line 3090  sub _tree_construction_main ($) {
3090        $insert = $insert_to_current;        $insert = $insert_to_current;
3091        #        #
3092      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3093        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) {  
3094          if ({          if ({
3095               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3096               th => 1, td => 1,               th => 1, td => 1,
3097              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3098            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3099              ## Clear back to table context              ## Clear back to table context
3100              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3101                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5621  sub _tree_construction_main ($) { Line 3108  sub _tree_construction_main ($) {
3108              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3109            }            }
3110                        
3111            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3112              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3113                !!!cp ('t202');                !!!cp ('t202');
3114                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5635  sub _tree_construction_main ($) { Line 3122  sub _tree_construction_main ($) {
3122                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3123              }              }
3124                                    
3125                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3126                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3127                    !!!cp ('t204');                !!!cp ('t204');
3128                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3129                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3130                    !!!next-token;                !!!nack ('t204');
3131                    next B;                !!!next-token;
3132                  } else {                next B;
3133                    !!!cp ('t205');              } else {
3134                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3135                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3136                  }                ## reprocess in the "in row" insertion mode
3137                } else {              }
3138                  !!!cp ('t206');            } else {
3139                }              !!!cp ('t206');
3140              }
3141    
3142                ## Clear back to table row context                ## Clear back to table row context
3143                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5658  sub _tree_construction_main ($) { Line 3146  sub _tree_construction_main ($) {
3146                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3147                }                }
3148                                
3149                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3150                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3151              $self->{insertion_mode} = IN_CELL_IM;
3152    
3153                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3154                                
3155                !!!nack ('t207.1');            !!!nack ('t207.1');
3156              !!!next-token;
3157              next B;
3158            } elsif ({
3159                      caption => 1, col => 1, colgroup => 1,
3160                      tbody => 1, tfoot => 1, thead => 1,
3161                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3162                     }->{$token->{tag_name}}) {
3163              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3164                ## As if </tr>
3165                ## have an element in table scope
3166                my $i;
3167                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3168                  my $node = $self->{open_elements}->[$_];
3169                  if ($node->[1] == TABLE_ROW_EL) {
3170                    !!!cp ('t208');
3171                    $i = $_;
3172                    last INSCOPE;
3173                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3174                    !!!cp ('t209');
3175                    last INSCOPE;
3176                  }
3177                } # INSCOPE
3178                unless (defined $i) {
3179                  !!!cp ('t210');
3180                  ## TODO: This type is wrong.
3181                  !!!parse-error (type => 'unmacthed end tag',
3182                                  text => $token->{tag_name}, token => $token);
3183                  ## Ignore the token
3184                  !!!nack ('t210.1');
3185                !!!next-token;                !!!next-token;
3186                next B;                next B;
3187              } 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;  
                 }  
3188                                    
3189                  ## Clear back to table row context                  ## Clear back to table row context
3190                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5718  sub _tree_construction_main ($) { Line 3207  sub _tree_construction_main ($) {
3207                  }                  }
3208                }                }
3209    
3210                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3211                  ## have an element in table scope                  ## have an element in table scope
3212                  my $i;                  my $i;
3213                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3214                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3215                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3216                      !!!cp ('t214');                      !!!cp ('t214');
3217                      $i = $_;                      $i = $_;
3218                      last INSCOPE;                      last INSCOPE;
# Line 5765  sub _tree_construction_main ($) { Line 3254  sub _tree_construction_main ($) {
3254                  !!!cp ('t218');                  !!!cp ('t218');
3255                }                }
3256    
3257                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3258                  ## Clear back to table context              ## Clear back to table context
3259                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3260                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3261                    !!!cp ('t219');                !!!cp ('t219');
3262                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3263                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3264                  }              }
3265                                
3266                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3267                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3268                  ## reprocess              ## reprocess
3269                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3270                  next B;              !!!ack-later;
3271                } elsif ({              next B;
3272                          caption => 1,            } elsif ({
3273                          colgroup => 1,                      caption => 1,
3274                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3275                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3276                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3277                ## Clear back to table context
3278                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3279                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3280                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5792  sub _tree_construction_main ($) { Line 3282  sub _tree_construction_main ($) {
3282                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3283                  }                  }
3284                                    
3285                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3286                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3287                                    
3288                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3289                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3290                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3291                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3292                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3293                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3294                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3295                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3296                  !!!next-token;                                        }->{$token->{tag_name}};
3297                  !!!nack ('t220.1');              !!!next-token;
3298                  next B;              !!!nack ('t220.1');
3299                } else {              next B;
3300                  die "$0: in table: <>: $token->{tag_name}";            } else {
3301                }              die "$0: in table: <>: $token->{tag_name}";
3302              }
3303              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3304                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3305                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5820  sub _tree_construction_main ($) { Line 3311  sub _tree_construction_main ($) {
3311                my $i;                my $i;
3312                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3313                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3314                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3315                    !!!cp ('t221');                    !!!cp ('t221');
3316                    $i = $_;                    $i = $_;
3317                    last INSCOPE;                    last INSCOPE;
# Line 5847  sub _tree_construction_main ($) { Line 3338  sub _tree_construction_main ($) {
3338                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3339                }                }
3340    
3341                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3342                  !!!cp ('t225');                  !!!cp ('t225');
3343                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3344                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5867  sub _tree_construction_main ($) { Line 3358  sub _tree_construction_main ($) {
3358            !!!ack-later;            !!!ack-later;
3359            next B;            next B;
3360          } elsif ($token->{tag_name} eq 'style') {          } elsif ($token->{tag_name} eq 'style') {
3361            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.8');
3362              !!!cp ('t227.8');            ## NOTE: This is a "as if in head" code clone.
3363              ## NOTE: This is a "as if in head" code clone.            $parse_rcdata->(CDATA_CONTENT_MODEL);
3364              $parse_rcdata->(CDATA_CONTENT_MODEL);            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3365              next B;            next B;
           } else {  
             !!!cp ('t227.7');  
             #  
           }  
3366          } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
3367            if (not $open_tables->[-1]->[1]) { # tainted            !!!cp ('t227.6');
3368              !!!cp ('t227.6');            ## NOTE: This is a "as if in head" code clone.
3369              ## NOTE: This is a "as if in head" code clone.            $script_start_tag->();
3370              $script_start_tag->();            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3371              next B;            next B;
           } else {  
             !!!cp ('t227.5');  
             #  
           }  
3372          } elsif ($token->{tag_name} eq 'input') {          } elsif ($token->{tag_name} eq 'input') {
3373            if (not $open_tables->[-1]->[1]) { # tainted            if ($token->{attributes}->{type}) {
3374              if ($token->{attributes}->{type}) { ## TODO: case              my $type = $token->{attributes}->{type}->{value};
3375                my $type = lc $token->{attributes}->{type}->{value};              $type =~ tr/A-Z/a-z/; ## ASCII case-insensitive.
3376                if ($type eq 'hidden') {              if ($type eq 'hidden') {
3377                  !!!cp ('t227.3');                !!!cp ('t227.3');
3378                  !!!parse-error (type => 'in table',                !!!parse-error (type => 'in table',
3379                                  text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
3380    
3381                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3382                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3383    
3384                  ## TODO: form element pointer                ## TODO: form element pointer
3385    
3386                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3387    
3388                  !!!next-token;                !!!next-token;
3389                  !!!ack ('t227.2.1');                !!!ack ('t227.2.1');
3390                  next B;                next B;
               } else {  
                 !!!cp ('t227.2');  
                 #  
               }  
3391              } else {              } else {
3392                !!!cp ('t227.1');                !!!cp ('t227.1');
3393                #                #
# Line 5927  sub _tree_construction_main ($) { Line 3407  sub _tree_construction_main ($) {
3407          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3408          #          #
3409        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3410              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3411                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3412                ## have an element in table scope            ## have an element in table scope
3413                my $i;                my $i;
3414                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3415                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3416                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3417                    !!!cp ('t228');                    !!!cp ('t228');
3418                    $i = $_;                    $i = $_;
3419                    last INSCOPE;                    last INSCOPE;
# Line 5968  sub _tree_construction_main ($) { Line 3448  sub _tree_construction_main ($) {
3448                !!!nack ('t231.1');                !!!nack ('t231.1');
3449                next B;                next B;
3450              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3451                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3452                  ## As if </tr>                  ## As if </tr>
3453                  ## have an element in table scope                  ## have an element in table scope
3454                  my $i;                  my $i;
3455                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3456                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3457                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3458                      !!!cp ('t233');                      !!!cp ('t233');
3459                      $i = $_;                      $i = $_;
3460                      last INSCOPE;                      last INSCOPE;
# Line 6007  sub _tree_construction_main ($) { Line 3487  sub _tree_construction_main ($) {
3487                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3488                }                }
3489    
3490                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3491                  ## have an element in table scope                  ## have an element in table scope
3492                  my $i;                  my $i;
3493                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3494                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3495                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3496                      !!!cp ('t237');                      !!!cp ('t237');
3497                      $i = $_;                      $i = $_;
3498                      last INSCOPE;                      last INSCOPE;
# Line 6059  sub _tree_construction_main ($) { Line 3539  sub _tree_construction_main ($) {
3539                my $i;                my $i;
3540                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3541                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3542                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3543                    !!!cp ('t241');                    !!!cp ('t241');
3544                    $i = $_;                    $i = $_;
3545                    last INSCOPE;                    last INSCOPE;
# Line 6089  sub _tree_construction_main ($) { Line 3569  sub _tree_construction_main ($) {
3569                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3570                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3571                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3572                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3573                  ## have an element in table scope                  ## have an element in table scope
3574                  my $i;                  my $i;
3575                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6118  sub _tree_construction_main ($) { Line 3598  sub _tree_construction_main ($) {
3598                  my $i;                  my $i;
3599                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3600                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3601                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3602                      !!!cp ('t250');                      !!!cp ('t250');
3603                      $i = $_;                      $i = $_;
3604                      last INSCOPE;                      last INSCOPE;
# Line 6208  sub _tree_construction_main ($) { Line 3688  sub _tree_construction_main ($) {
3688            #            #
3689          }          }
3690        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3691          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3692                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3693            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3694            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6223  sub _tree_construction_main ($) { Line 3703  sub _tree_construction_main ($) {
3703        } else {        } else {
3704          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3705        }        }
3706      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3707            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3708              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3709                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6250  sub _tree_construction_main ($) { Line 3730  sub _tree_construction_main ($) {
3730              }              }
3731            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3732              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3733                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3734                  !!!cp ('t264');                  !!!cp ('t264');
3735                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3736                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6276  sub _tree_construction_main ($) { Line 3756  sub _tree_construction_main ($) {
3756                #                #
3757              }              }
3758        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3759          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3760              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3761            !!!cp ('t270.2');            !!!cp ('t270.2');
3762            ## Stop parsing.            ## Stop parsing.
# Line 6294  sub _tree_construction_main ($) { Line 3774  sub _tree_construction_main ($) {
3774        }        }
3775    
3776            ## As if </colgroup>            ## As if </colgroup>
3777            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3778              !!!cp ('t269');              !!!cp ('t269');
3779  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3780              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6319  sub _tree_construction_main ($) { Line 3799  sub _tree_construction_main ($) {
3799          next B;          next B;
3800        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3801          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3802            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3803              !!!cp ('t272');              !!!cp ('t272');
3804              ## As if </option>              ## As if </option>
3805              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6332  sub _tree_construction_main ($) { Line 3812  sub _tree_construction_main ($) {
3812            !!!next-token;            !!!next-token;
3813            next B;            next B;
3814          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3815            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3816              !!!cp ('t274');              !!!cp ('t274');
3817              ## As if </option>              ## As if </option>
3818              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6340  sub _tree_construction_main ($) { Line 3820  sub _tree_construction_main ($) {
3820              !!!cp ('t275');              !!!cp ('t275');
3821            }            }
3822    
3823            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3824              !!!cp ('t276');              !!!cp ('t276');
3825              ## As if </optgroup>              ## As if </optgroup>
3826              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6353  sub _tree_construction_main ($) { Line 3833  sub _tree_construction_main ($) {
3833            !!!next-token;            !!!next-token;
3834            next B;            next B;
3835          } elsif ({          } elsif ({
3836                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3837                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3838                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3839                          == IN_SELECT_IN_TABLE_IM and
3840                    {                    {
3841                     caption => 1, table => 1,                     caption => 1, table => 1,
3842                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3843                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3844                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3845            ## TODO: The type below is not good - <select> is replaced by </select>  
3846            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3847                            token => $token);            if ($token->{tag_name} eq 'select') {
3848            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3849            ## as if there were </select> (otherwise).                                token => $token);
3850            ## have an element in table scope            } else {
3851                !!!parse-error (type => 'not closed', text => 'select',
3852                                token => $token);
3853              }
3854    
3855              ## 2./<select>-1. Unless "have an element in table scope" (select):
3856            my $i;            my $i;
3857            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3858              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3859              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3860                !!!cp ('t278');                !!!cp ('t278');
3861                $i = $_;                $i = $_;
3862                last INSCOPE;                last INSCOPE;
# Line 6381  sub _tree_construction_main ($) { Line 3867  sub _tree_construction_main ($) {
3867            } # INSCOPE            } # INSCOPE
3868            unless (defined $i) {            unless (defined $i) {
3869              !!!cp ('t280');              !!!cp ('t280');
3870              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3871                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3872              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3873                  ## case two errors, "select in select" and "unmatched
3874                  ## end tags" are reported to the user, the latter might
3875                  ## be confusing but this is what the spec requires.
3876                  !!!parse-error (type => 'unmatched end tag',
3877                                  text => 'select',
3878                                  token => $token);
3879                }
3880                ## Ignore the token.
3881              !!!nack ('t280.1');              !!!nack ('t280.1');
3882              !!!next-token;              !!!next-token;
3883              next B;              next B;
3884            }            }
3885    
3886              ## 3. Otherwise, as if there were <select>:
3887                                
3888            !!!cp ('t281');            !!!cp ('t281');
3889            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6404  sub _tree_construction_main ($) { Line 3900  sub _tree_construction_main ($) {
3900              ## Reprocess the token.              ## Reprocess the token.
3901              next B;              next B;
3902            }            }
3903            } elsif ($token->{tag_name} eq 'script') {
3904              !!!cp ('t281.3');
3905              ## NOTE: This is an "as if in head" code clone
3906              $script_start_tag->();
3907              next B;
3908          } else {          } else {
3909            !!!cp ('t282');            !!!cp ('t282');
3910            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6415  sub _tree_construction_main ($) { Line 3916  sub _tree_construction_main ($) {
3916          }          }
3917        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3918          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3919            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3920                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3921              !!!cp ('t283');              !!!cp ('t283');
3922              ## As if </option>              ## As if </option>
3923              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3924            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3925              !!!cp ('t284');              !!!cp ('t284');
3926              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3927            } else {            } else {
# Line 6433  sub _tree_construction_main ($) { Line 3934  sub _tree_construction_main ($) {
3934            !!!next-token;            !!!next-token;
3935            next B;            next B;
3936          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3937            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3938              !!!cp ('t286');              !!!cp ('t286');
3939              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3940            } else {            } else {
# Line 6450  sub _tree_construction_main ($) { Line 3951  sub _tree_construction_main ($) {
3951            my $i;            my $i;
3952            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3953              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3954              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3955                !!!cp ('t288');                !!!cp ('t288');
3956                $i = $_;                $i = $_;
3957                last INSCOPE;                last INSCOPE;
# Line 6477  sub _tree_construction_main ($) { Line 3978  sub _tree_construction_main ($) {
3978            !!!nack ('t291.1');            !!!nack ('t291.1');
3979            !!!next-token;            !!!next-token;
3980            next B;            next B;
3981          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3982                         == IN_SELECT_IN_TABLE_IM and
3983                   {                   {
3984                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3985                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6512  sub _tree_construction_main ($) { Line 4014  sub _tree_construction_main ($) {
4014            undef $i;            undef $i;
4015            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4016              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4017              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4018                !!!cp ('t295');                !!!cp ('t295');
4019                $i = $_;                $i = $_;
4020                last INSCOPE;                last INSCOPE;
# Line 6551  sub _tree_construction_main ($) { Line 4053  sub _tree_construction_main ($) {
4053            next B;            next B;
4054          }          }
4055        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4056          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4057                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4058            !!!cp ('t299.1');            !!!cp ('t299.1');
4059            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6738  sub _tree_construction_main ($) { Line 4240  sub _tree_construction_main ($) {
4240        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4241          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4242              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4243            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4244                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4245              !!!cp ('t325');              !!!cp ('t325');
4246              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6752  sub _tree_construction_main ($) { Line 4254  sub _tree_construction_main ($) {
4254            }            }
4255    
4256            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4257                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4258              !!!cp ('t327');              !!!cp ('t327');
4259              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4260            } else {            } else {
# Line 6784  sub _tree_construction_main ($) { Line 4286  sub _tree_construction_main ($) {
4286            next B;            next B;
4287          }          }
4288        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4289          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4290                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4291            !!!cp ('t331.1');            !!!cp ('t331.1');
4292            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6814  sub _tree_construction_main ($) { Line 4316  sub _tree_construction_main ($) {
4316          $parse_rcdata->(CDATA_CONTENT_MODEL);          $parse_rcdata->(CDATA_CONTENT_MODEL);
4317          next B;          next B;
4318        } elsif ({        } elsif ({
4319                  base => 1, command => 1, eventsource => 1, link => 1,                  base => 1, command => 1, link => 1,
4320                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4321          !!!cp ('t334');          !!!cp ('t334');
4322          ## NOTE: This is an "as if in head" code clone, only "-t" differs          ## NOTE: This is an "as if in head" code clone, only "-t" differs
# Line 6887  sub _tree_construction_main ($) { Line 4389  sub _tree_construction_main ($) {
4389          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4390                                
4391          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4392              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4393            !!!cp ('t342');            !!!cp ('t342');
4394            ## Ignore the token            ## Ignore the token
4395          } else {          } else {
# Line 6922  sub _tree_construction_main ($) { Line 4424  sub _tree_construction_main ($) {
4424                  table => 1,                  table => 1,
4425                  hr => 1,                  hr => 1,
4426                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4427    
4428            ## 1. When there is an opening |form| element:
4429          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4430            !!!cp ('t350');            !!!cp ('t350');
4431            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6931  sub _tree_construction_main ($) { Line 4435  sub _tree_construction_main ($) {
4435            next B;            next B;
4436          }          }
4437    
4438          ## has a p element in scope          ## 2. Close the |p| element, if any.
4439          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4440            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4441              !!!cp ('t344');            ## has a p element in scope
4442              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4443              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4444                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4445              next B;                !!!back-token; # <form>
4446            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4447              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4448              last INSCOPE;                next B;
4449                } elsif ($_->[1] & SCOPING_EL) {
4450                  !!!cp ('t345');
4451                  last INSCOPE;
4452                }
4453              } # INSCOPE
4454            }
4455    
4456            ## 3. Close the opening <hn> element, if any.
4457            if ({h1 => 1, h2 => 1, h3 => 1,
4458                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4459              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4460                !!!parse-error (type => 'not closed',
4461                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4462                                token => $token);
4463                pop @{$self->{open_elements}};
4464            }            }
4465          } # INSCOPE          }
4466              
4467            ## 4. Insertion.
4468          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4469          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4470            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6988  sub _tree_construction_main ($) { Line 4508  sub _tree_construction_main ($) {
4508        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4509          ## NOTE: As normal, but imply </li> when there's another <li> ...          ## NOTE: As normal, but imply </li> when there's another <li> ...
4510    
4511          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4512            ## Interpreted as <li><foo/></li><li/> (non-conforming)            ## Interpreted as <li><foo/></li><li/> (non-conforming):
4513            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4514            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
4515            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
4516            ## object (Fx)            ## object (Fx)
4517            ## Generate non-tree (non-conforming)            ## Generate non-tree (non-conforming):
4518            ## basefont (IE7 (where basefont is non-void)), center (IE),            ## basefont (IE7 (where basefont is non-void)), center (IE),
4519            ## form (IE), hn (IE)            ## form (IE), hn (IE)
4520          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4521            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)            ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4522            ## div (Fx, S)            ## div (Fx, S)
4523    
4524          my $non_optional;          my $non_optional;
# Line 7006  sub _tree_construction_main ($) { Line 4526  sub _tree_construction_main ($) {
4526    
4527          ## 1.          ## 1.
4528          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4529            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4530              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4531              {              {
4532                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7037  sub _tree_construction_main ($) { Line 4557  sub _tree_construction_main ($) {
4557                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4558                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4559                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4560                       (not $node->[1] & ADDRESS_DIV_P_EL)
4561                     (not $node->[1] & ADDRESS_EL) &                    ) {
                    (not $node->[1] & DIV_EL) &  
                    (not $node->[1] & P_EL)) {  
4562              ## 3.              ## 3.
4563              !!!cp ('t357');              !!!cp ('t357');
4564              last; ## goto 5.              last; ## goto 5.
# Line 7059  sub _tree_construction_main ($) { Line 4577  sub _tree_construction_main ($) {
4577    
4578          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4579          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4580            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4581              !!!cp ('t353');              !!!cp ('t353');
4582    
4583              ## NOTE: |<p><li>|, for example.              ## NOTE: |<p><li>|, for example.
# Line 7088  sub _tree_construction_main ($) { Line 4606  sub _tree_construction_main ($) {
4606    
4607          ## 1.          ## 1.
4608          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4609            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4610              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4611              {              {
4612                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7120  sub _tree_construction_main ($) { Line 4638  sub _tree_construction_main ($) {
4638                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4639                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4640    
4641                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4642                     (not $node->[1] & DIV_EL) &                    ) {
                    (not $node->[1] & P_EL)) {  
4643              ## 3.              ## 3.
4644              !!!cp ('t357.1');              !!!cp ('t357.1');
4645              last; ## goto 5.              last; ## goto 5.
# Line 7141  sub _tree_construction_main ($) { Line 4658  sub _tree_construction_main ($) {
4658    
4659          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4660          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4661            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4662              !!!cp ('t353.1');              !!!cp ('t353.1');
4663              !!!back-token; # <x>              !!!back-token; # <x>
4664              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7163  sub _tree_construction_main ($) { Line 4680  sub _tree_construction_main ($) {
4680    
4681          ## has a p element in scope          ## has a p element in scope
4682          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4683            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4684              !!!cp ('t367');              !!!cp ('t367');
4685              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4686              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7185  sub _tree_construction_main ($) { Line 4702  sub _tree_construction_main ($) {
4702        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4703          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4704            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4705            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4706              !!!cp ('t371');              !!!cp ('t371');
4707              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4708                            
# Line 7229  sub _tree_construction_main ($) { Line 4746  sub _tree_construction_main ($) {
4746          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4747          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4748            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4749            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4750              !!!cp ('t376');              !!!cp ('t376');
4751              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4752              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7252  sub _tree_construction_main ($) { Line 4769  sub _tree_construction_main ($) {
4769          ## has a button element in scope          ## has a button element in scope
4770          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4771            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4772            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4773              !!!cp ('t378');              !!!cp ('t378');
4774              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4775              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7317  sub _tree_construction_main ($) { Line 4834  sub _tree_construction_main ($) {
4834                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4835                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4836                           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}},  
4837                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4838                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4839                         );                         );
# Line 7341  sub _tree_construction_main ($) { Line 4856  sub _tree_construction_main ($) {
4856                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4857                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4858                           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}},  
4859                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4860                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4861                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7352  sub _tree_construction_main ($) { Line 4865  sub _tree_construction_main ($) {
4865            next B;            next B;
4866          }          }
4867        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4868          my $tag_name = $token->{tag_name};          ## 1. Insert
4869          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4870                    
4871            ## Step 2 # XXX
4872          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4873    
4874            ## 2. Drop U+000A LINE FEED
4875            $self->{ignore_newline} = 1;
4876    
4877            ## 3. RCDATA
4878          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4879          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4880            
4881          $insert->($el);          ## 4., 6. Insertion mode
4882                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4883          my $text = '';  
4884            ## XXX: 5. frameset-ok flag
4885    
4886          !!!nack ('t392.1');          !!!nack ('t392.1');
4887          !!!next-token;          !!!next-token;
         if ($token->{type} == CHARACTER_TOKEN) {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!cp ('t392');  
             !!!next-token;  
           } else {  
             !!!cp ('t393');  
           }  
         } else {  
           !!!cp ('t394');  
         }  
         while ($token->{type} == CHARACTER_TOKEN) {  
           !!!cp ('t395');  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
         !!!next-token;  
4888          next B;          next B;
4889        } elsif ($token->{tag_name} eq 'optgroup' or        } elsif ($token->{tag_name} eq 'optgroup' or
4890                 $token->{tag_name} eq 'option') {                 $token->{tag_name} eq 'option') {
4891          ## has an |option| element in scope          ## has an |option| element in scope
4892          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4893            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4894            if ($node->[1] & OPTION_EL) {            if ($node->[1] == OPTION_EL) {
4895              !!!cp ('t397.1');              !!!cp ('t397.1');
4896              ## NOTE: As if </option>              ## NOTE: As if </option>
4897              !!!back-token; # <option> or <optgroup>              !!!back-token; # <option> or <optgroup>
# Line 7428  sub _tree_construction_main ($) { Line 4916  sub _tree_construction_main ($) {
4916          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4917          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4918            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4919            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4920              !!!cp ('t398.1');              !!!cp ('t398.1');
4921              ## generate implied end tags              ## generate implied end tags
4922              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4923                !!!cp ('t398.2');                !!!cp ('t398.2');
4924                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4925              }              }
4926              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4927                !!!cp ('t398.3');                !!!cp ('t398.3');
4928                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4929                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4930                                    ->manakai_local_name,                                    ->manakai_local_name,
4931                                token => $token);                                token => $token);
4932                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4933                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4934              }              }
4935              last INSCOPE;              last INSCOPE;
4936            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7450  sub _tree_construction_main ($) { Line 4938  sub _tree_construction_main ($) {
4938              last INSCOPE;              last INSCOPE;
4939            }            }
4940          } # INSCOPE          } # INSCOPE
4941              
4942            ## TODO: <non-ruby><rt> is not allowed.
4943    
4944          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4945    
# Line 7538  sub _tree_construction_main ($) { Line 5028  sub _tree_construction_main ($) {
5028          } elsif ({          } elsif ({
5029                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5030                    embed => 1, img => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
5031                      keygen => 1,
5032                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5033            !!!cp ('t388.1');            !!!cp ('t388.1');
5034            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7547  sub _tree_construction_main ($) { Line 5038  sub _tree_construction_main ($) {
5038                    
5039            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5040                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5041                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5042              !!!cp ('t400.1');              !!!cp ('t400.1');
5043              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5044            } else {            } else {
# Line 7564  sub _tree_construction_main ($) { Line 5055  sub _tree_construction_main ($) {
5055        }        }
5056      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5057        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5058          ## has a |body| element in scope  
5059            ## 1. If not "have an element in scope":
5060            ## "has a |body| element in scope"
5061          my $i;          my $i;
5062          INSCOPE: {          INSCOPE: {
5063            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5064              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5065                !!!cp ('t405');                !!!cp ('t405');
5066                $i = $_;                $i = $_;
5067                last INSCOPE;                last INSCOPE;
# Line 7587  sub _tree_construction_main ($) { Line 5080  sub _tree_construction_main ($) {
5080            next B;            next B;
5081          } # INSCOPE          } # INSCOPE
5082    
5083            ## 2. If unclosed elements:
5084          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5085            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5086                      $_->[1] == OPTGROUP_EL ||
5087                      $_->[1] == OPTION_EL ||
5088                      $_->[1] == RUBY_COMPONENT_EL) {
5089              !!!cp ('t403');              !!!cp ('t403');
5090              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5091                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7599  sub _tree_construction_main ($) { Line 5096  sub _tree_construction_main ($) {
5096            }            }
5097          }          }
5098    
5099            ## 3. Switch the insertion mode.
5100          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5101          !!!next-token;          !!!next-token;
5102          next B;          next B;
# Line 7606  sub _tree_construction_main ($) { Line 5104  sub _tree_construction_main ($) {
5104          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5105          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5106          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5107              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5108            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
5109              !!!cp ('t406');              !!!cp ('t406');
5110              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5111                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7714  sub _tree_construction_main ($) { Line 5212  sub _tree_construction_main ($) {
5212          my $i;          my $i;
5213          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5214            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5215            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5216              !!!cp ('t418');              !!!cp ('t418');
5217              $i = $_;              $i = $_;
5218              last INSCOPE;              last INSCOPE;
# Line 7762  sub _tree_construction_main ($) { Line 5260  sub _tree_construction_main ($) {
5260          my $i;          my $i;
5261          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5262            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5263            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5264              !!!cp ('t423');              !!!cp ('t423');
5265              $i = $_;              $i = $_;
5266              last INSCOPE;              last INSCOPE;
# Line 7808  sub _tree_construction_main ($) { Line 5306  sub _tree_construction_main ($) {
5306          my $i;          my $i;
5307          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5308            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5309            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5310              !!!cp ('t410.1');              !!!cp ('t410.1');
5311              $i = $_;              $i = $_;
5312              last INSCOPE;              last INSCOPE;
# Line 7985  sub _tree_construction_main ($) { Line 5483  sub _tree_construction_main ($) {
5483    ## TODO: script stuffs    ## TODO: script stuffs
5484  } # _tree_construct_main  } # _tree_construct_main
5485    
5486    ## XXX: How this method is organized is somewhat out of date, although
5487    ## it still does what the current spec documents.
5488  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5489    my $class = shift;    my $class = shift;
5490    my $node = shift;    my $node = shift; # /context/
5491    #my $s = \$_[0];    #my $s = \$_[0];
5492    my $onerror = $_[1];    my $onerror = $_[1];
5493    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
5494    
   ## ISSUE: Should {confident} be true?  
   
5495    my $nt = $node->node_type;    my $nt = $node->node_type;
5496    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5497      # MUST      # MUST
5498            
5499      ## Step 1 # MUST      ## Step 1 # MUST
# Line 8010  sub set_inner_html ($$$$;$) { Line 5508  sub set_inner_html ($$$$;$) {
5508    
5509      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5510      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5511    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5512      ## TODO: If non-html element      ## TODO: If non-html element
5513    
5514      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5515    
5516  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5517    
5518      ## Step 1 # MUST      ## F1. Create an HTML document.
5519      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5520      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5521      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5522    
5523        ## F2. Propagate quirkness flag
5524        my $node_doc = $node->owner_document;
5525        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5526    
5527        ## F3. Create an HTML parser
5528      my $p = $class->new;      my $p = $class->new;
5529      $p->{document} = $doc;      $p->{document} = $doc;
5530    
# Line 8148  sub set_inner_html ($$$$;$) { Line 5652  sub set_inner_html ($$$$;$) {
5652      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5653      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5654    
5655      ## Step 2      ## F4. If /context/ is not undef...
5656    
5657        ## F4.1. content model flag
5658      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5659      $p->{content_model} = {      $p->{content_model} = {
5660        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8164  sub set_inner_html ($$$$;$) { Line 5670  sub set_inner_html ($$$$;$) {
5670      }->{$node_ln};      }->{$node_ln};
5671      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5672          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5673    
5674      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5675        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5676    
5677      ## Step 3      ## F4.2. Root |html| element
5678      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5679        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5680    
5681      ## Step 4 # MUST      ## F4.3.
5682      $doc->append_child ($root);      $doc->append_child ($root);
5683    
5684      ## Step 5 # MUST      ## F4.4.
5685      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5686    
5687      undef $p->{head_element};      undef $p->{head_element};
5688        undef $p->{head_element_inserted};
5689    
5690      ## Step 6 # MUST      ## F4.5.
5691      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5692    
5693      ## Step 7 # MUST      ## F4.6.
5694      my $anode = $node;      my $anode = $node;
5695      AN: while (defined $anode) {      AN: while (defined $anode) {
5696        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8199  sub set_inner_html ($$$$;$) { Line 5705  sub set_inner_html ($$$$;$) {
5705        }        }
5706        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5707      } # AN      } # AN
5708        
5709      ## Step 9 # MUST      ## F.5. Set the input stream.
5710        $self->{confident} = 1; ## Confident: irrelevant.
5711    
5712        ## F.6. Start the parser.
5713      {      {
5714        my $self = $p;        my $self = $p;
5715        !!!next-token;        !!!next-token;
5716      }      }
5717      $p->_tree_construction_main;      $p->_tree_construction_main;
5718    
5719      ## Step 10 # MUST      ## F.7.
5720      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5721      for (@cn) {      for (@cn) {
5722        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.201  
changed lines
  Added in v.1.235

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24