/[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.205 by wakaba, Mon Oct 13 06:18:31 2008 UTC revision 1.225 by wakaba, Sun Aug 16 05:57:24 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,
182    eventsource => MISC_SPECIAL_EL,    eventsource => MISC_SPECIAL_EL,
# Line 200  my $el_category = { Line 210  my $el_category = {
210    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
211    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
212    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
213    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
214    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
215    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
216    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 242  my $el_category = { Line 252  my $el_category = {
252  my $el_category_f = {  my $el_category_f = {
253    $MML_NS => {    $MML_NS => {
254      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
255      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
256      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260    },    },
261    $SVG_NS => {    $SVG_NS => {
262      foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265    },    },
266    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
267  };  };
# Line 338  my $foreign_attr_xname = { Line 348  my $foreign_attr_xname = {
348    
349  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
350    
 my $charref_map = {  
   0x0D => 0x000A,  
   0x80 => 0x20AC,  
   0x81 => 0xFFFD,  
   0x82 => 0x201A,  
   0x83 => 0x0192,  
   0x84 => 0x201E,  
   0x85 => 0x2026,  
   0x86 => 0x2020,  
   0x87 => 0x2021,  
   0x88 => 0x02C6,  
   0x89 => 0x2030,  
   0x8A => 0x0160,  
   0x8B => 0x2039,  
   0x8C => 0x0152,  
   0x8D => 0xFFFD,  
   0x8E => 0x017D,  
   0x8F => 0xFFFD,  
   0x90 => 0xFFFD,  
   0x91 => 0x2018,  
   0x92 => 0x2019,  
   0x93 => 0x201C,  
   0x94 => 0x201D,  
   0x95 => 0x2022,  
   0x96 => 0x2013,  
   0x97 => 0x2014,  
   0x98 => 0x02DC,  
   0x99 => 0x2122,  
   0x9A => 0x0161,  
   0x9B => 0x203A,  
   0x9C => 0x0153,  
   0x9D => 0xFFFD,  
   0x9E => 0x017E,  
   0x9F => 0x0178,  
 }; # $charref_map  
 $charref_map->{$_} = 0xFFFD  
     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,  
         0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF  
         0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,  
         0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,  
         0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,  
         0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,  
         0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;  
   
351  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
352  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
353    
# Line 559  sub parse_byte_stream ($$$$;$$) { Line 525  sub parse_byte_stream ($$$$;$$) {
525            
526      if ($char_stream) { # if supported      if ($char_stream) { # if supported
527        ## "Change the encoding" algorithm:        ## "Change the encoding" algorithm:
   
       ## Step 1      
       if ($charset->{category} &  
           Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {  
         $charset = Message::Charset::Info->get_by_html_name ('utf-8');  
         ($char_stream, $e_status) = $charset->get_decode_handle  
             ($byte_stream,  
              byte_buffer => \ $buffer->{buffer});  
       }  
       $charset_name = $charset->get_iana_name;  
528                
529        ## Step 2        ## Step 1
530        if (defined $self->{input_encoding} and        if (defined $self->{input_encoding} and
531            $self->{input_encoding} eq $charset_name) {            $self->{input_encoding} eq $charset_name) {
532          !!!parse-error (type => 'charset label:matching',          !!!parse-error (type => 'charset label:matching',
# Line 580  sub parse_byte_stream ($$$$;$$) { Line 536  sub parse_byte_stream ($$$$;$$) {
536          return;          return;
537        }        }
538    
539          ## Step 2 (HTML5 revision 3205)
540          if (defined $self->{input_encoding} and
541              Message::Charset::Info->get_by_html_name ($self->{input_encoding})
542              ->{category} & Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
543            $self->{confident} = 1;
544            return;
545          }
546    
547          ## Step 3
548          if ($charset->{category} &
549              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
550            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
551            ($char_stream, $e_status) = $charset->get_decode_handle
552                ($byte_stream,
553                 byte_buffer => \ $buffer->{buffer});
554          }
555          $charset_name = $charset->get_iana_name;
556    
557        !!!parse-error (type => 'charset label detected',        !!!parse-error (type => 'charset label detected',
558                        text => $self->{input_encoding},                        text => $self->{input_encoding},
559                        value => $charset_name,                        value => $charset_name,
560                        level => $self->{level}->{warn},                        level => $self->{level}->{warn},
561                        token => $token);                        token => $token);
562                
563        ## Step 3        ## Step 4
564        # if (can) {        # if (can) {
565          ## change the encoding on the fly.          ## change the encoding on the fly.
566          #$self->{confident} = 1;          #$self->{confident} = 1;
567          #return;          #return;
568        # }        # }
569                
570        ## Step 4        ## Step 5
571        throw Whatpm::HTML::RestartParser ();        throw Whatpm::HTML::RestartParser ();
572      }      }
573    }; # $self->{change_encoding}    }; # $self->{change_encoding}
# Line 834  sub new ($) { Line 808  sub new ($) {
808    return $self;    return $self;
809  } # new  } # new
810    
811  sub CM_ENTITY () { 0b001 } # & markup in data  ## Insertion modes
 sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)  
 sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)  
   
 sub PLAINTEXT_CONTENT_MODEL () { 0 }  
 sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }  
 sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  
 sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  
   
 sub DATA_STATE () { 0 }  
 #sub ENTITY_DATA_STATE () { 1 }  
 sub TAG_OPEN_STATE () { 2 }  
 sub CLOSE_TAG_OPEN_STATE () { 3 }  
 sub TAG_NAME_STATE () { 4 }  
 sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }  
 sub ATTRIBUTE_NAME_STATE () { 6 }  
 sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }  
 sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }  
 sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }  
 sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }  
 sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }  
 #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }  
 sub MARKUP_DECLARATION_OPEN_STATE () { 13 }  
 sub COMMENT_START_STATE () { 14 }  
 sub COMMENT_START_DASH_STATE () { 15 }  
 sub COMMENT_STATE () { 16 }  
 sub COMMENT_END_STATE () { 17 }  
 sub COMMENT_END_DASH_STATE () { 18 }  
 sub BOGUS_COMMENT_STATE () { 19 }  
 sub DOCTYPE_STATE () { 20 }  
 sub BEFORE_DOCTYPE_NAME_STATE () { 21 }  
 sub DOCTYPE_NAME_STATE () { 22 }  
 sub AFTER_DOCTYPE_NAME_STATE () { 23 }  
 sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }  
 sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }  
 sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }  
 sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }  
 sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }  
 sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }  
 sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }  
 sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }  
 sub BOGUS_DOCTYPE_STATE () { 32 }  
 sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }  
 sub SELF_CLOSING_START_TAG_STATE () { 34 }  
 sub CDATA_SECTION_STATE () { 35 }  
 sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec  
 sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec  
 sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec  
 sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec  
 sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec  
 sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec  
 sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec  
 sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec  
 ## NOTE: "Entity data state", "entity in attribute value state", and  
 ## "consume a character reference" algorithm are jointly implemented  
 ## using the following six states:  
 sub ENTITY_STATE () { 44 }  
 sub ENTITY_HASH_STATE () { 45 }  
 sub NCR_NUM_STATE () { 46 }  
 sub HEXREF_X_STATE () { 47 }  
 sub HEXREF_HEX_STATE () { 48 }  
 sub ENTITY_NAME_STATE () { 49 }  
 sub PCDATA_STATE () { 50 } # "data state" in the spec  
   
 sub DOCTYPE_TOKEN () { 1 }  
 sub COMMENT_TOKEN () { 2 }  
 sub START_TAG_TOKEN () { 3 }  
 sub END_TAG_TOKEN () { 4 }  
 sub END_OF_FILE_TOKEN () { 5 }  
 sub CHARACTER_TOKEN () { 6 }  
812    
813  sub AFTER_HTML_IMS () { 0b100 }  sub AFTER_HTML_IMS () { 0b100 }
814  sub HEAD_IMS ()       { 0b1000 }  sub HEAD_IMS ()       { 0b1000 }
# Line 914  sub ROW_IMS ()        { 0b10000000 } Line 819  sub ROW_IMS ()        { 0b10000000 }
819  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
820  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
821  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
822  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
823      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
824      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
825      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
# Line 923  sub IN_CDATA_RCDATA_IM () { 0b1000000000 Line 828  sub IN_CDATA_RCDATA_IM () { 0b1000000000
828      ## combined with the original insertion mode.  In thie parser,      ## combined with the original insertion mode.  In thie parser,
829      ## they are stored together in the bit-or'ed form.      ## they are stored together in the bit-or'ed form.
830    
831    sub IM_MASK () { 0b11111111111 }
832    
833  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
834    
835  ## NOTE: "after after body" insertion mode.  ## NOTE: "after after body" insertion mode.
# Line 948  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 855  sub IN_SELECT_IM () { SELECT_IMS | 0b01
855  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
856  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
857    
 ## Implementations MUST act as if state machine in the spec  
   
 sub _initialize_tokenizer ($) {  
   my $self = shift;  
   $self->{state} = DATA_STATE; # MUST  
   #$self->{s_kwd}; # state keyword - initialized when used  
   #$self->{entity__value}; # initialized when used  
   #$self->{entity__match}; # initialized when used  
   $self->{content_model} = PCDATA_CONTENT_MODEL; # be  
   undef $self->{ct}; # current token  
   undef $self->{ca}; # current attribute  
   undef $self->{last_stag_name}; # last emitted start tag name  
   #$self->{prev_state}; # initialized when used  
   delete $self->{self_closing};  
   $self->{char_buffer} = '';  
   $self->{char_buffer_pos} = 0;  
   $self->{nc} = -1; # next input character  
   #$self->{next_nc}  
   !!!next-input-character;  
   $self->{token} = [];  
   # $self->{escape}  
 } # _initialize_tokenizer  
   
 ## A token has:  
 ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,  
 ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  
 ##   ->{name} (DOCTYPE_TOKEN)  
 ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##   ->{pubid} (DOCTYPE_TOKEN)  
 ##   ->{sysid} (DOCTYPE_TOKEN)  
 ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  
 ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##        ->{name}  
 ##        ->{value}  
 ##        ->{has_reference} == 1 or 0  
 ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  
 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.  
 ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  
 ##     while the token is pushed back to the stack.  
   
 ## Emitted token MUST immediately be handled by the tree construction state.  
   
 ## Before each step, UA MAY check to see if either one of the scripts in  
 ## "list of scripts that will execute as soon as possible" or the first  
 ## script in the "list of scripts that will execute asynchronously",  
 ## has completed loading.  If one has, then it MUST be executed  
 ## and removed from the list.  
   
 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)  
 ## (This requirement was dropped from HTML5 spec, unfortunately.)  
   
 my $is_space = {  
   0x0009 => 1, # CHARACTER TABULATION (HT)  
   0x000A => 1, # LINE FEED (LF)  
   #0x000B => 0, # LINE TABULATION (VT)  
   0x000C => 1, # FORM FEED (FF)  
   #0x000D => 1, # CARRIAGE RETURN (CR)  
   0x0020 => 1, # SPACE (SP)  
 };  
   
 sub _get_next_token ($) {  
   my $self = shift;  
   
   if ($self->{self_closing}) {  
     !!!parse-error (type => 'nestc', token => $self->{ct});  
     ## NOTE: The |self_closing| flag is only set by start tag token.  
     ## In addition, when a start tag token is emitted, it is always set to  
     ## |ct|.  
     delete $self->{self_closing};  
   }  
   
   if (@{$self->{token}}) {  
     $self->{self_closing} = $self->{token}->[0]->{self_closing};  
     return shift @{$self->{token}};  
   }  
   
   A: {  
     if ($self->{state} == PCDATA_STATE) {  
       ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.  
   
       if ($self->{nc} == 0x0026) { # &  
         !!!cp (0.1);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity data state".  In this implementation, the tokenizer  
         ## is switched to the |ENTITY_STATE|, which is an implementation  
         ## of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = DATA_STATE;  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003C) { # <  
         !!!cp (0.2);  
         $self->{state} = TAG_OPEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (0.3);  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (0.4);  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       $self->{read_until}->($token->{data}, q[<&], length $token->{data});  
   
       ## Stay in the state.  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == DATA_STATE) {  
       $self->{s_kwd} = '' unless defined $self->{s_kwd};  
       if ($self->{nc} == 0x0026) { # &  
         $self->{s_kwd} = '';  
         if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA  
             not $self->{escape}) {  
           !!!cp (1);  
           ## NOTE: In the spec, the tokenizer is switched to the  
           ## "entity data state".  In this implementation, the tokenizer  
           ## is switched to the |ENTITY_STATE|, which is an implementation  
           ## of the "consume a character reference" algorithm.  
           $self->{entity_add} = -1;  
           $self->{prev_state} = DATA_STATE;  
           $self->{state} = ENTITY_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (2);  
           #  
         }  
       } elsif ($self->{nc} == 0x002D) { # -  
         if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
           $self->{s_kwd} .= '-';  
             
           if ($self->{s_kwd} eq '<!--') {  
             !!!cp (3);  
             $self->{escape} = 1; # unless $self->{escape};  
             $self->{s_kwd} = '--';  
             #  
           } elsif ($self->{s_kwd} eq '---') {  
             !!!cp (4);  
             $self->{s_kwd} = '--';  
             #  
           } else {  
             !!!cp (5);  
             #  
           }  
         }  
           
         #  
       } elsif ($self->{nc} == 0x0021) { # !  
         if (length $self->{s_kwd}) {  
           !!!cp (5.1);  
           $self->{s_kwd} .= '!';  
           #  
         } else {  
           !!!cp (5.2);  
           #$self->{s_kwd} = '';  
           #  
         }  
         #  
       } elsif ($self->{nc} == 0x003C) { # <  
         if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA  
             (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA  
              not $self->{escape})) {  
           !!!cp (6);  
           $self->{state} = TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (7);  
           $self->{s_kwd} = '';  
           #  
         }  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{escape} and  
             ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA  
           if ($self->{s_kwd} eq '--') {  
             !!!cp (8);  
             delete $self->{escape};  
           } else {  
             !!!cp (9);  
           }  
         } else {  
           !!!cp (10);  
         }  
           
         $self->{s_kwd} = '';  
         #  
       } elsif ($self->{nc} == -1) {  
         !!!cp (11);  
         $self->{s_kwd} = '';  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (12);  
         $self->{s_kwd} = '';  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       if ($self->{read_until}->($token->{data}, q[-!<>&],  
                                 length $token->{data})) {  
         $self->{s_kwd} = '';  
       }  
   
       ## Stay in the data state.  
       if ($self->{content_model} == PCDATA_CONTENT_MODEL) {  
         !!!cp (13);  
         $self->{state} = PCDATA_STATE;  
       } else {  
         !!!cp (14);  
         ## Stay in the state.  
       }  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == TAG_OPEN_STATE) {  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if ($self->{nc} == 0x002F) { # /  
           !!!cp (15);  
           !!!next-input-character;  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           redo A;  
         } elsif ($self->{nc} == 0x0021) { # !  
           !!!cp (15.1);  
           $self->{s_kwd} = '<' unless $self->{escape};  
           #  
         } else {  
           !!!cp (16);  
           #  
         }  
   
         ## reconsume  
         $self->{state} = DATA_STATE;  
         !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA  
         if ($self->{nc} == 0x0021) { # !  
           !!!cp (17);  
           $self->{state} = MARKUP_DECLARATION_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x002F) { # /  
           !!!cp (18);  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0041 <= $self->{nc} and  
                  $self->{nc} <= 0x005A) { # A..Z  
           !!!cp (19);  
           $self->{ct}  
             = {type => START_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $self->{line_prev},  
                column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0061 <= $self->{nc} and  
                  $self->{nc} <= 0x007A) { # a..z  
           !!!cp (20);  
           $self->{ct} = {type => START_TAG_TOKEN,  
                                     tag_name => chr ($self->{nc}),  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x003E) { # >  
           !!!cp (21);  
           !!!parse-error (type => 'empty start tag',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<>',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         } elsif ($self->{nc} == 0x003F) { # ?  
           !!!cp (22);  
           !!!parse-error (type => 'pio',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = BOGUS_COMMENT_STATE;  
           $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev},  
                                    };  
           ## $self->{nc} is intentionally left as is  
           redo A;  
         } else {  
           !!!cp (23);  
           !!!parse-error (type => 'bare stago',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           ## reconsume  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         }  
       } else {  
         die "$0: $self->{content_model} in tag open";  
       }  
     } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {  
       ## NOTE: The "close tag open state" in the spec is implemented as  
       ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if (defined $self->{last_stag_name}) {  
           $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;  
           $self->{s_kwd} = '';  
           ## Reconsume.  
           redo A;  
         } else {  
           ## No start tag token has ever been emitted  
           ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.  
           !!!cp (28);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                     line => $l, column => $c,  
                    });  
           redo A;  
         }  
       }  
   
       if (0x0041 <= $self->{nc} and  
           $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (29);  
         $self->{ct}  
             = {type => END_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x007A) { # a..z  
         !!!cp (30);  
         $self->{ct} = {type => END_TAG_TOKEN,  
                                   tag_name => chr ($self->{nc}),  
                                   line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (31);  
         !!!parse-error (type => 'empty end tag',  
                         line => $self->{line_prev}, ## "<" in "</>"  
                         column => $self->{column_prev} - 1);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (32);  
         !!!parse-error (type => 'bare etago');  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                   line => $l, column => $c,  
                  });  
   
         redo A;  
       } else {  
         !!!cp (33);  
         !!!parse-error (type => 'bogus end tag');  
         $self->{state} = BOGUS_COMMENT_STATE;  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev}, # "<" of "</"  
                                   column => $self->{column_prev} - 1,  
                                  };  
         ## NOTE: $self->{nc} is intentionally left as is.  
         ## Although the "anything else" case of the spec not explicitly  
         ## states that the next input character is to be reconsumed,  
         ## it will be included to the |data| of the comment token  
         ## generated from the bogus end tag, as defined in the  
         ## "bogus comment state" entry.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {  
       my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;  
       if (length $ch) {  
         my $CH = $ch;  
         $ch =~ tr/a-z/A-Z/;  
         my $nch = chr $self->{nc};  
         if ($nch eq $ch or $nch eq $CH) {  
           !!!cp (24);  
           ## Stay in the state.  
           $self->{s_kwd} .= $nch;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (25);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         }  
       } else { # after "<{tag-name}"  
         unless ($is_space->{$self->{nc}} or  
                 {  
                  0x003E => 1, # >  
                  0x002F => 1, # /  
                  -1 => 1, # EOF  
                 }->{$self->{nc}}) {  
           !!!cp (26);  
           ## Reconsume.  
           $self->{state} = DATA_STATE;  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (27);  
           $self->{ct}  
               = {type => END_TAG_TOKEN,  
                  tag_name => $self->{last_stag_name},  
                  line => $self->{line_prev},  
                  column => $self->{column_prev} - 1 - length $self->{s_kwd}};  
           $self->{state} = TAG_NAME_STATE;  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == TAG_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (34);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (35);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This should never be reached.  
           #  !!! cp (36);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (37);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (38);  
         $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);  
           # start tag or end tag  
         ## Stay in this state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (39);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This state should never be reached.  
           #  !!! cp (40);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (41);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (42);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (44);  
         $self->{ct}->{tag_name} .= chr $self->{nc};  
           # start tag or end tag  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (45);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (46);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (47);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (48);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (49);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (50);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (52);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (53);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (54);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (55);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (56);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {  
       my $before_leave = sub {  
         if (exists $self->{ct}->{attributes} # start tag or end tag  
             ->{$self->{ca}->{name}}) { # MUST  
           !!!cp (57);  
           !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});  
           ## Discard $self->{ca} # MUST  
         } else {  
           !!!cp (58);  
           $self->{ct}->{attributes}->{$self->{ca}->{name}}  
             = $self->{ca};  
         }  
       }; # $before_leave  
   
       if ($is_space->{$self->{nc}}) {  
         !!!cp (59);  
         $before_leave->();  
         $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (60);  
         $before_leave->();  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (61);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp (62);  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!parse-error (type => 'end tag attribute');  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (63);  
         $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (64);  
         $before_leave->();  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (66);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (67);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (68);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (69);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (70);  
         }  
         $self->{ca}->{name} .= chr ($self->{nc});  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (71);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (72);  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (73);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (74);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (75);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (76);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (77);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (79);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (80);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (81);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (78);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (82);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;          
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (83);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (84);  
         $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (85);  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         ## reconsume  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (86);  
         $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!parse-error (type => 'empty unquoted attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (87);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (88);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (89);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (90);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (91);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (92);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x003D) { # =  
           !!!cp (93);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (94);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (95);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (96);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{prev_state} = $self->{state};  
         $self->{entity_add} = 0x0022; # "  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (97);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (98);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (99);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (100);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (101);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (102);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = 0x0027; # '  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (103);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (104);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (105);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (106);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q['&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (107);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (108);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (109);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (110);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (111);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (112);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (113);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (114);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (115);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (116);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["'=& >],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (118);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (119);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (120);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (121);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (122);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (122.3);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (122.1);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (122.2);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.1');  
         !!!parse-error (type => 'no space between attributes');  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp ('124.2');  
           !!!parse-error (type => 'nestc', token => $self->{ct});  
           ## TODO: Different type than slash in start tag  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp ('124.4');  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp ('124.5');  
           }  
           ## TODO: Test |<title></title/>|  
         } else {  
           !!!cp ('124.3');  
           $self->{self_closing} = 1;  
         }  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (124.7);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (124.5);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (124.6);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.4');  
         !!!parse-error (type => 'nestc');  
         ## TODO: This error type is wrong.  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_COMMENT_STATE) {  
       ## (only happen if PCDATA state)  
   
       ## NOTE: Unlike spec's "bogus comment state", this implementation  
       ## consumes characters one-by-one basis.  
         
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (124);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (125);  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } else {  
         !!!cp (126);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[>],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {  
       ## (only happen if PCDATA state)  
         
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (133);  
         $self->{state} = MD_HYPHEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0044 or # D  
                $self->{nc} == 0x0064) { # d  
         ## ASCII case-insensitive.  
         !!!cp (130);  
         $self->{state} = MD_DOCTYPE_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and  
                $self->{open_elements}->[-1]->[1] & FOREIGN_EL and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.4);                  
         $self->{state} = MD_CDATA_STATE;  
         $self->{s_kwd} = '[';  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (136);  
       }  
   
       !!!parse-error (type => 'bogus comment',  
                       line => $self->{line_prev},  
                       column => $self->{column_prev} - 1);  
       ## Reconsume.  
       $self->{state} = BOGUS_COMMENT_STATE;  
       $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                 line => $self->{line_prev},  
                                 column => $self->{column_prev} - 1,  
                                };  
       redo A;  
     } elsif ($self->{state} == MD_HYPHEN_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (127);  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         $self->{state} = COMMENT_START_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (128);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => '-',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_DOCTYPE_STATE) {  
       ## ASCII case-insensitive.  
       if ($self->{nc} == [  
             undef,  
             0x004F, # O  
             0x0043, # C  
             0x0054, # T  
             0x0059, # Y  
             0x0050, # P  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x006F, # o  
             0x0063, # c  
             0x0074, # t  
             0x0079, # y  
             0x0070, # p  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (131);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 6 and  
                ($self->{nc} == 0x0045 or # E  
                 $self->{nc} == 0x0065)) { # e  
         !!!cp (129);  
         $self->{state} = DOCTYPE_STATE;  
         $self->{ct} = {type => DOCTYPE_TOKEN,  
                                   quirks => 1,  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7,  
                                  };  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (132);          
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_CDATA_STATE) {  
       if ($self->{nc} == {  
             '[' => 0x0043, # C  
             '[C' => 0x0044, # D  
             '[CD' => 0x0041, # A  
             '[CDA' => 0x0054, # T  
             '[CDAT' => 0x0041, # A  
           }->{$self->{s_kwd}}) {  
         !!!cp (135.1);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{s_kwd} eq '[CDATA' and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.2);  
         $self->{ct} = {type => CHARACTER_TOKEN,  
                                   data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7};  
         $self->{state} = CDATA_SECTION_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (135.3);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (137);  
         $self->{state} = COMMENT_START_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (138);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (139);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (140);  
         $self->{ct}->{data} # comment  
             .= chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (141);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (142);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (143);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (144);  
         $self->{ct}->{data} # comment  
             .= '-' . chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (145);  
         $self->{state} = COMMENT_END_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (146);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (147);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[-],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (148);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (149);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (150);  
         $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (151);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == 0x002D) { # -  
         !!!cp (152);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '-'; # comment  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (153);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (154);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (155);  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (156);  
         !!!parse-error (type => 'no space before DOCTYPE name');  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (157);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (158);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (159);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } else {  
         !!!cp (160);  
         $self->{ct}->{name} = chr $self->{nc};  
         delete $self->{ct}->{quirks};  
         $self->{state} = DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_NAME_STATE) {  
 ## ISSUE: Redundant "First," in the spec.  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (161);  
         $self->{state} = AFTER_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (162);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (163);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (164);  
         $self->{ct}->{name}  
           .= chr ($self->{nc}); # DOCTYPE  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (165);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (166);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (167);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == 0x0050 or # P  
                $self->{nc} == 0x0070) { # p  
         $self->{state} = PUBLIC_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0053 or # S  
                $self->{nc} == 0x0073) { # s  
         $self->{state} = SYSTEM_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (180);  
         !!!parse-error (type => 'string after DOCTYPE name');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == PUBLIC_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0055, # U  
             0x0042, # B  
             0x004C, # L  
             0x0049, # I  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0075, # u  
             0x0062, # b  
             0x006C, # l  
             0x0069, # i  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (175);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x0043 or # C  
                 $self->{nc} == 0x0063)) { # c  
         !!!cp (168);  
         $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (169);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == SYSTEM_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0059, # Y  
             0x0053, # S  
             0x0054, # T  
             0x0045, # E  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0079, # y  
             0x0073, # s  
             0x0074, # t  
             0x0065, # e  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (170);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x004D or # M  
                 $self->{nc} == 0x006D)) { # m  
         !!!cp (171);  
         $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (172);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (181);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0022) { # "  
         !!!cp (182);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0027) { # '  
         !!!cp (183);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x003E) { # >  
         !!!cp (184);  
         !!!parse-error (type => 'no PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (185);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (186);  
         !!!parse-error (type => 'string after PUBLIC');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (187);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (188);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (189);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (190);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q[">],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (191);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (192);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (193);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (194);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q['>],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (195);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (196);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (197);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (198);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (199);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (200);  
         !!!parse-error (type => 'string after PUBLIC literal');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (201);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (202);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (203);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (204);  
         !!!parse-error (type => 'no SYSTEM literal');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (205);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (206);  
         !!!parse-error (type => 'string after SYSTEM');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (207);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (208);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (209);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (210);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q[">],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (211);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (212);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (213);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (214);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q['>],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (215);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (216);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (217);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (218);  
         !!!parse-error (type => 'string after SYSTEM literal');  
         #$self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (219);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (220);  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (221);  
         my $s = '';  
         $self->{read_until}->($s, q[>], 0);  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_STATE) {  
       ## NOTE: "CDATA section state" in the state is jointly implemented  
       ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,  
       ## and |CDATA_SECTION_MSE2_STATE|.  
         
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.1);  
         $self->{state} = CDATA_SECTION_MSE1_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.2);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.3);  
           ## No token to emit. $self->{ct} is discarded.  
         }          
         redo A;  
       } else {  
         !!!cp (221.4);  
         $self->{ct}->{data} .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{data},  
                               q<]>,  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
   
       ## ISSUE: "text tokens" in spec.  
     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {  
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.5);  
         $self->{state} = CDATA_SECTION_MSE2_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.6);  
         $self->{ct}->{data} .= ']';  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.7);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.8);  
           ## No token to emit. $self->{ct} is discarded.  
         }  
         redo A;  
       } elsif ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.9); # character  
         $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.11);  
         $self->{ct}->{data} .= ']]'; # character  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_STATE) {  
       if ($is_space->{$self->{nc}} or  
           {  
             0x003C => 1, 0x0026 => 1, -1 => 1, # <, &  
             $self->{entity_add} => 1,  
           }->{$self->{nc}}) {  
         !!!cp (1001);  
         ## Don't consume  
         ## No error  
         ## Return nothing.  
         #  
       } elsif ($self->{nc} == 0x0023) { # #  
         !!!cp (999);  
         $self->{state} = ENTITY_HASH_STATE;  
         $self->{s_kwd} = '#';  
         !!!next-input-character;  
         redo A;  
       } elsif ((0x0041 <= $self->{nc} and  
                 $self->{nc} <= 0x005A) or # A..Z  
                (0x0061 <= $self->{nc} and  
                 $self->{nc} <= 0x007A)) { # a..z  
         !!!cp (998);  
         require Whatpm::_NamedEntityList;  
         $self->{state} = ENTITY_NAME_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         $self->{entity__value} = $self->{s_kwd};  
         $self->{entity__match} = 0;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (1027);  
         !!!parse-error (type => 'bare ero');  
         ## Return nothing.  
         #  
       }  
   
       ## NOTE: No character is consumed by the "consume a character  
       ## reference" algorithm.  In other word, there is an "&" character  
       ## that does not introduce a character reference, which would be  
       ## appended to the parent element or the attribute value in later  
       ## process of the tokenizer.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (997);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } else {  
         !!!cp (996);  
         $self->{ca}->{value} .= '&';  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_HASH_STATE) {  
       if ($self->{nc} == 0x0078 or # x  
           $self->{nc} == 0x0058) { # X  
         !!!cp (995);  
         $self->{state} = HEXREF_X_STATE;  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0030 <= $self->{nc} and  
                $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (994);  
         $self->{state} = NCR_NUM_STATE;  
         $self->{s_kwd} = $self->{nc} - 0x0030;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare nero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" is appended to the parent element or the attribute  
         ## value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1019);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&#',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1,  
                    });  
           redo A;  
         } else {  
           !!!cp (993);  
           $self->{ca}->{value} .= '&#';  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == NCR_NUM_STATE) {  
       if (0x0030 <= $self->{nc} and  
           $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (1012);  
         $self->{s_kwd} *= 10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
           
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1013);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1014);  
         !!!parse-error (type => 'no refc');  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1015);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1016);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (992);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (991);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == HEXREF_X_STATE) {  
       if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or  
           (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or  
           (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {  
         # 0..9, A..F, a..f  
         !!!cp (990);  
         $self->{state} = HEXREF_HEX_STATE;  
         $self->{s_kwd} = 0;  
         ## Reconsume.  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare hcro',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" followed by "X" or "x" is appended to the parent  
         ## element or the attribute value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1005);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (989);  
           $self->{ca}->{value} .= '&' . $self->{s_kwd};  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == HEXREF_HEX_STATE) {  
       if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {  
         # 0..9  
         !!!cp (1002);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x0066) { # a..f  
         !!!cp (1003);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0060 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x0046) { # A..F  
         !!!cp (1004);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0040 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1006);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1007);  
         !!!parse-error (type => 'no refc',  
                         line => $self->{line},  
                         column => $self->{column});  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1008);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1009);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (988);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (987);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_NAME_STATE) {  
       if (length $self->{s_kwd} < 30 and  
           ## NOTE: Some number greater than the maximum length of entity name  
           ((0x0041 <= $self->{nc} and # a  
             $self->{nc} <= 0x005A) or # x  
            (0x0061 <= $self->{nc} and # a  
             $self->{nc} <= 0x007A) or # z  
            (0x0030 <= $self->{nc} and # 0  
             $self->{nc} <= 0x0039) or # 9  
            $self->{nc} == 0x003B)) { # ;  
         our $EntityChar;  
         $self->{s_kwd} .= chr $self->{nc};  
         if (defined $EntityChar->{$self->{s_kwd}}) {  
           if ($self->{nc} == 0x003B) { # ;  
             !!!cp (1020);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = 1;  
             !!!next-input-character;  
             #  
           } else {  
             !!!cp (1021);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = -1;  
             ## Stay in the state.  
             !!!next-input-character;  
             redo A;  
           }  
         } else {  
           !!!cp (1022);  
           $self->{entity__value} .= chr $self->{nc};  
           $self->{entity__match} *= 2;  
           ## Stay in the state.  
           !!!next-input-character;  
           redo A;  
         }  
       }  
   
       my $data;  
       my $has_ref;  
       if ($self->{entity__match} > 0) {  
         !!!cp (1023);  
         $data = $self->{entity__value};  
         $has_ref = 1;  
         #  
       } elsif ($self->{entity__match} < 0) {  
         !!!parse-error (type => 'no refc');  
         if ($self->{prev_state} != DATA_STATE and # in attribute  
             $self->{entity__match} < -1) {  
           !!!cp (1024);  
           $data = '&' . $self->{s_kwd};  
           #  
         } else {  
           !!!cp (1025);  
           $data = $self->{entity__value};  
           $has_ref = 1;  
           #  
         }  
       } else {  
         !!!cp (1026);  
         !!!parse-error (type => 'bare ero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - length $self->{s_kwd});  
         $data = '&' . $self->{s_kwd};  
         #  
       }  
     
       ## NOTE: In these cases, when a character reference is found,  
       ## it is consumed and a character token is returned, or, otherwise,  
       ## nothing is consumed and returned, according to the spec algorithm.  
       ## In this implementation, anything that has been examined by the  
       ## tokenizer is appended to the parent element or the attribute value  
       ## as string, either literal string when no character reference or  
       ## entity-replaced string otherwise, in this stage, since any characters  
       ## that would not be consumed are appended in the data state or in an  
       ## appropriate attribute value state anyway.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (986);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN,  
                   data => $data,  
                   line => $self->{line_prev},  
                   column => $self->{column_prev} + 1 - length $self->{s_kwd},  
                  });  
         redo A;  
       } else {  
         !!!cp (985);  
         $self->{ca}->{value} .= $data;  
         $self->{ca}->{has_reference} = 1 if $has_ref;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } else {  
       die "$0: $self->{state}: Unknown state";  
     }  
   } # A    
   
   die "$0: _get_next_token: unexpected case";  
 } # _get_next_token  
   
858  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
859    my $self = shift;    my $self = shift;
860    ## NOTE: $self->{document} MUST be specified before this method is called    ## NOTE: $self->{document} MUST be specified before this method is called
# Line 3478  sub _construct_tree ($) { Line 891  sub _construct_tree ($) {
891    undef $self->{head_element_inserted};    undef $self->{head_element_inserted};
892    $self->{open_elements} = [];    $self->{open_elements} = [];
893    undef $self->{inner_html_node};    undef $self->{inner_html_node};
894      undef $self->{ignore_newline};
895    
896    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
897    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3530  sub _tree_construction_initial ($) { Line 944  sub _tree_construction_initial ($) {
944        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
945        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
946        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
947        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
948          ## string, while |null| is an allowed value for the attribute
949          ## according to DOM3 Core.
950        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
951                
952        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
# Line 3818  sub _reset_insertion_mode ($) { Line 1234  sub _reset_insertion_mode ($) {
1234          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1235          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1236          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1237        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1238          if ($last) {          if ($last) {
1239            !!!cp ('t28.2');            !!!cp ('t28.2');
1240            #            #
# Line 3847  sub _reset_insertion_mode ($) { Line 1263  sub _reset_insertion_mode ($) {
1263        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1264                
1265        ## Step 15        ## Step 15
1266        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1267          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1268            !!!cp ('t29');            !!!cp ('t29');
1269            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 4003  sub _tree_construction_main ($) { Line 1419  sub _tree_construction_main ($) {
1419      ## Step 3      ## Step 3
1420      ## TODO: Mark as "already executed", if ...      ## TODO: Mark as "already executed", if ...
1421    
1422      ## Step 4      ## Step 4 (HTML5 revision 2702)
1423      $insert->($script_el);      $insert->($script_el);
   
     ## ISSUE: $script_el is not put into the stack  
1424      push @{$self->{open_elements}}, [$script_el, $el_category->{script}];      push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1425    
1426      ## Step 5      ## Step 5
# Line 4194  sub _tree_construction_main ($) { Line 1608  sub _tree_construction_main ($) {
1608          my $foster_parent_element;          my $foster_parent_element;
1609          my $next_sibling;          my $next_sibling;
1610          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1611            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1612                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1613                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1614                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 4272  sub _tree_construction_main ($) { Line 1686  sub _tree_construction_main ($) {
1686        my $foster_parent_element;        my $foster_parent_element;
1687        my $next_sibling;        my $next_sibling;
1688        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1689          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1690                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1691                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1692                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 4428  sub _tree_construction_main ($) { Line 1842  sub _tree_construction_main ($) {
1842                              ->manakai_local_name,                              ->manakai_local_name,
1843                          token => $token);                          token => $token);
1844    
1845          #if ($self->{open_elements}->[-1]->[1] & SCRIPT_EL) {          #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1846          #  ## TODO: Mark as "already executed"          #  ## TODO: Mark as "already executed"
1847          #}          #}
1848    
# Line 4451  sub _tree_construction_main ($) { Line 1865  sub _tree_construction_main ($) {
1865               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1866              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1867              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1868               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1869            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1870            !!!cp ('t87.2');            !!!cp ('t87.2');
1871            #            #
1872          } elsif ({          } elsif ({
1873                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1874                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1875                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1876                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1877                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1878                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1879                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1880                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1881                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1882                     ($token->{tag_name} eq 'font' and
1883                      ($token->{attributes}->{color} or
1884                       $token->{attributes}->{face} or
1885                       $token->{attributes}->{size}))) {
1886            !!!cp ('t87.2');            !!!cp ('t87.2');
1887            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1888                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4540  sub _tree_construction_main ($) { Line 1958  sub _tree_construction_main ($) {
1958          }          }
1959        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
1960          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
1961          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
1962          #            !!!cp ('t87.41');
1963              #
1964              ## XXXscript: Execute script here.
1965            } else {
1966              !!!cp ('t87.5');
1967              #
1968            }
1969        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1970          !!!cp ('t87.6');          !!!cp ('t87.6');
1971          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4841  sub _tree_construction_main ($) { Line 2265  sub _tree_construction_main ($) {
2265    
2266            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2267            $parse_rcdata->(RCDATA_CONTENT_MODEL);            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2268            ## ISSUE: A spec bug [Bug 6038]  
2269              ## NOTE: At this point the stack of open elements contain
2270              ## the |head| element (index == -2) and the |script| element
2271              ## (index == -1).  In the "after head" insertion mode the
2272              ## |head| element is inserted only for the purpose of
2273              ## providing the context for the |script| element, and
2274              ## therefore we can now and have to remove the element from
2275              ## the stack.
2276            splice @{$self->{open_elements}}, -2, 1, () # <head>            splice @{$self->{open_elements}}, -2, 1, () # <head>
2277                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2278            next B;            next B;
2279          } elsif ($token->{tag_name} eq 'style' or          } elsif ($token->{tag_name} eq 'style' or
2280                   $token->{tag_name} eq 'noframes') {                   $token->{tag_name} eq 'noframes') {
# Line 4863  sub _tree_construction_main ($) { Line 2294  sub _tree_construction_main ($) {
2294            $parse_rcdata->(CDATA_CONTENT_MODEL);            $parse_rcdata->(CDATA_CONTENT_MODEL);
2295            ## ISSUE: A spec bug [Bug 6038]            ## ISSUE: A spec bug [Bug 6038]
2296            splice @{$self->{open_elements}}, -2, 1, () # <head>            splice @{$self->{open_elements}}, -2, 1, () # <head>
2297                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2298            next B;            next B;
2299          } elsif ($token->{tag_name} eq 'noscript') {          } elsif ($token->{tag_name} eq 'noscript') {
2300                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
# Line 4911  sub _tree_construction_main ($) { Line 2342  sub _tree_construction_main ($) {
2342            $script_start_tag->();            $script_start_tag->();
2343            ## ISSUE: A spec bug  [Bug 6038]            ## ISSUE: A spec bug  [Bug 6038]
2344            splice @{$self->{open_elements}}, -2, 1 # <head>            splice @{$self->{open_elements}}, -2, 1 # <head>
2345                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2346            next B;            next B;
2347          } elsif ($token->{tag_name} eq 'body' or          } elsif ($token->{tag_name} eq 'body' or
2348                   $token->{tag_name} eq 'frameset') {                   $token->{tag_name} eq 'frameset') {
# Line 5076  sub _tree_construction_main ($) { Line 2507  sub _tree_construction_main ($) {
2507                ## Ignore the token                ## Ignore the token
2508                !!!next-token;                !!!next-token;
2509                next B;                next B;
2510              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2511                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2512                  !!!cp ('t142.2');              !!!cp ('t142.2');
2513                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2514                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2515                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2516                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2517        
2518                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2519                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2520                  !!!cp ('t143.2');              !!!cp ('t143.2');
2521                  ## As if </head>              ## As if </head>
2522                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2523                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2524        
2525                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2526                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2527                  !!!cp ('t143.3');              !!!cp ('t143.3');
2528                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2529                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2530                                  text => 'br', token => $token);                              text => 'br', token => $token);
2531                  ## As if </noscript>              ## As if </noscript>
2532                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2533                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2534    
2535                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2536                  ## As if </head>              ## As if </head>
2537                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2538                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2539    
2540                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2541                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2542                  !!!cp ('t143.4');              !!!cp ('t143.4');
2543                  #              #
2544                } else {            } else {
2545                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2546                }            }
2547    
2548                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2549                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2550                !!!cp ('t145');                !!!cp ('t145');
2551                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2552                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5164  sub _tree_construction_main ($) { Line 2590  sub _tree_construction_main ($) {
2590              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2591              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2592              ## reprocess              ## reprocess
2593              next B;          next B;
2594        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2595          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2596            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5237  sub _tree_construction_main ($) { Line 2663  sub _tree_construction_main ($) {
2663                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2664                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2665                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2666                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2667                  ## have an element in table scope                  ## have an element in table scope
2668                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2669                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2670                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2671                      !!!cp ('t151');                      !!!cp ('t151');
2672    
2673                      ## Close the cell                      ## Close the cell
# Line 5265  sub _tree_construction_main ($) { Line 2691  sub _tree_construction_main ($) {
2691                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2692                  !!!next-token;                  !!!next-token;
2693                  next B;                  next B;
2694                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2695                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2696                                  token => $token);                                  token => $token);
2697                                    
# Line 5275  sub _tree_construction_main ($) { Line 2701  sub _tree_construction_main ($) {
2701                  INSCOPE: {                  INSCOPE: {
2702                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2703                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2704                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2705                        !!!cp ('t155');                        !!!cp ('t155');
2706                        $i = $_;                        $i = $_;
2707                        last INSCOPE;                        last INSCOPE;
# Line 5301  sub _tree_construction_main ($) { Line 2727  sub _tree_construction_main ($) {
2727                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2728                  }                  }
2729    
2730                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2731                    !!!cp ('t159');                    !!!cp ('t159');
2732                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2733                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5330  sub _tree_construction_main ($) { Line 2756  sub _tree_construction_main ($) {
2756              }              }
2757            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2758              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2759                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2760                  ## have an element in table scope                  ## have an element in table scope
2761                  my $i;                  my $i;
2762                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5380  sub _tree_construction_main ($) { Line 2806  sub _tree_construction_main ($) {
2806                                    
2807                  !!!next-token;                  !!!next-token;
2808                  next B;                  next B;
2809                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2810                  !!!cp ('t169');                  !!!cp ('t169');
2811                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2812                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5392  sub _tree_construction_main ($) { Line 2818  sub _tree_construction_main ($) {
2818                  #                  #
2819                }                }
2820              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2821                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2822                  ## have a table element in table scope                  ## have a table element in table scope
2823                  my $i;                  my $i;
2824                  INSCOPE: {                  INSCOPE: {
2825                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2826                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2827                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2828                        !!!cp ('t171');                        !!!cp ('t171');
2829                        $i = $_;                        $i = $_;
2830                        last INSCOPE;                        last INSCOPE;
# Line 5423  sub _tree_construction_main ($) { Line 2849  sub _tree_construction_main ($) {
2849                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2850                  }                  }
2851                                    
2852                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2853                    !!!cp ('t175');                    !!!cp ('t175');
2854                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2855                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5441  sub _tree_construction_main ($) { Line 2867  sub _tree_construction_main ($) {
2867                                    
2868                  !!!next-token;                  !!!next-token;
2869                  next B;                  next B;
2870                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2871                  !!!cp ('t177');                  !!!cp ('t177');
2872                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2873                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5456  sub _tree_construction_main ($) { Line 2882  sub _tree_construction_main ($) {
2882                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2883                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2884                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2885                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2886                ## have an element in table scope                ## have an element in table scope
2887                my $i;                my $i;
2888                my $tn;                my $tn;
# Line 5473  sub _tree_construction_main ($) { Line 2899  sub _tree_construction_main ($) {
2899                                line => $token->{line},                                line => $token->{line},
2900                                column => $token->{column}};                                column => $token->{column}};
2901                      next B;                      next B;
2902                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2903                      !!!cp ('t180');                      !!!cp ('t180');
2904                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2905                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5493  sub _tree_construction_main ($) { Line 2919  sub _tree_construction_main ($) {
2919                  next B;                  next B;
2920                } # INSCOPE                } # INSCOPE
2921              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2922                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2923                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2924                                token => $token);                                token => $token);
2925    
# Line 5502  sub _tree_construction_main ($) { Line 2928  sub _tree_construction_main ($) {
2928                my $i;                my $i;
2929                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2930                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
2931                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
2932                    !!!cp ('t184');                    !!!cp ('t184');
2933                    $i = $_;                    $i = $_;
2934                    last INSCOPE;                    last INSCOPE;
# Line 5513  sub _tree_construction_main ($) { Line 2939  sub _tree_construction_main ($) {
2939                } # INSCOPE                } # INSCOPE
2940                unless (defined $i) {                unless (defined $i) {
2941                  !!!cp ('t186');                  !!!cp ('t186');
2942            ## TODO: Wrong error type?
2943                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2944                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
2945                  ## Ignore the token                  ## Ignore the token
# Line 5526  sub _tree_construction_main ($) { Line 2953  sub _tree_construction_main ($) {
2953                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2954                }                }
2955    
2956                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2957                  !!!cp ('t188');                  !!!cp ('t188');
2958                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
2959                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5558  sub _tree_construction_main ($) { Line 2985  sub _tree_construction_main ($) {
2985                  !!!cp ('t191');                  !!!cp ('t191');
2986                  #                  #
2987                }                }
2988              } elsif ({          } elsif ({
2989                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
2990                        thead => 1, tr => 1,                    thead => 1, tr => 1,
2991                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
2992                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2993                !!!cp ('t192');            !!!cp ('t192');
2994                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
2995                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
2996                ## Ignore the token            ## Ignore the token
2997                !!!next-token;            !!!next-token;
2998                next B;            next B;
2999              } else {          } else {
3000                !!!cp ('t193');            !!!cp ('t193');
3001                #            #
3002              }          }
3003        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3004          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3005            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5616  sub _tree_construction_main ($) { Line 3043  sub _tree_construction_main ($) {
3043            my $next_sibling;            my $next_sibling;
3044            my $prev_sibling;            my $prev_sibling;
3045            OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
3046              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
3047                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3048                if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
3049                  $foster_parent_element = $parent;                  $foster_parent_element = $parent;
# Line 5665  sub _tree_construction_main ($) { Line 3092  sub _tree_construction_main ($) {
3092          next B;          next B;
3093        } elsif ($token->{type} == START_TAG_TOKEN) {        } 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 5681  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 5733  sub _tree_construction_main ($) { Line 3160  sub _tree_construction_main ($) {
3160                    tbody => 1, tfoot => 1, thead => 1,                    tbody => 1, tfoot => 1, thead => 1,
3161                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3162                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
3163            if ($self->{insertion_mode} == IN_ROW_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3164              ## As if </tr>              ## As if </tr>
3165              ## have an element in table scope              ## have an element in table scope
3166              my $i;              my $i;
3167              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3168                my $node = $self->{open_elements}->[$_];                my $node = $self->{open_elements}->[$_];
3169                if ($node->[1] & TABLE_ROW_EL) {                if ($node->[1] == TABLE_ROW_EL) {
3170                  !!!cp ('t208');                  !!!cp ('t208');
3171                  $i = $_;                  $i = $_;
3172                  last INSCOPE;                  last INSCOPE;
# Line 5780  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 5884  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 5911  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 5994  sub _tree_construction_main ($) { Line 3421  sub _tree_construction_main ($) {
3421          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3422          #          #
3423        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3424              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3425                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3426                ## have an element in table scope            ## have an element in table scope
3427                my $i;                my $i;
3428                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3429                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3430                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3431                    !!!cp ('t228');                    !!!cp ('t228');
3432                    $i = $_;                    $i = $_;
3433                    last INSCOPE;                    last INSCOPE;
# Line 6035  sub _tree_construction_main ($) { Line 3462  sub _tree_construction_main ($) {
3462                !!!nack ('t231.1');                !!!nack ('t231.1');
3463                next B;                next B;
3464              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3465                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3466                  ## As if </tr>                  ## As if </tr>
3467                  ## have an element in table scope                  ## have an element in table scope
3468                  my $i;                  my $i;
3469                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3470                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3471                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3472                      !!!cp ('t233');                      !!!cp ('t233');
3473                      $i = $_;                      $i = $_;
3474                      last INSCOPE;                      last INSCOPE;
# Line 6074  sub _tree_construction_main ($) { Line 3501  sub _tree_construction_main ($) {
3501                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3502                }                }
3503    
3504                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3505                  ## have an element in table scope                  ## have an element in table scope
3506                  my $i;                  my $i;
3507                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3508                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3509                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3510                      !!!cp ('t237');                      !!!cp ('t237');
3511                      $i = $_;                      $i = $_;
3512                      last INSCOPE;                      last INSCOPE;
# Line 6126  sub _tree_construction_main ($) { Line 3553  sub _tree_construction_main ($) {
3553                my $i;                my $i;
3554                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3555                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3556                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3557                    !!!cp ('t241');                    !!!cp ('t241');
3558                    $i = $_;                    $i = $_;
3559                    last INSCOPE;                    last INSCOPE;
# Line 6156  sub _tree_construction_main ($) { Line 3583  sub _tree_construction_main ($) {
3583                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3584                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3585                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3586                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3587                  ## have an element in table scope                  ## have an element in table scope
3588                  my $i;                  my $i;
3589                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6185  sub _tree_construction_main ($) { Line 3612  sub _tree_construction_main ($) {
3612                  my $i;                  my $i;
3613                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3614                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3615                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3616                      !!!cp ('t250');                      !!!cp ('t250');
3617                      $i = $_;                      $i = $_;
3618                      last INSCOPE;                      last INSCOPE;
# Line 6275  sub _tree_construction_main ($) { Line 3702  sub _tree_construction_main ($) {
3702            #            #
3703          }          }
3704        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3705          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3706                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3707            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3708            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6290  sub _tree_construction_main ($) { Line 3717  sub _tree_construction_main ($) {
3717        } else {        } else {
3718          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3719        }        }
3720      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3721            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3722              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3723                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6317  sub _tree_construction_main ($) { Line 3744  sub _tree_construction_main ($) {
3744              }              }
3745            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3746              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3747                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3748                  !!!cp ('t264');                  !!!cp ('t264');
3749                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3750                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6343  sub _tree_construction_main ($) { Line 3770  sub _tree_construction_main ($) {
3770                #                #
3771              }              }
3772        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3773          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3774              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3775            !!!cp ('t270.2');            !!!cp ('t270.2');
3776            ## Stop parsing.            ## Stop parsing.
# Line 6361  sub _tree_construction_main ($) { Line 3788  sub _tree_construction_main ($) {
3788        }        }
3789    
3790            ## As if </colgroup>            ## As if </colgroup>
3791            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3792              !!!cp ('t269');              !!!cp ('t269');
3793  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3794              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6386  sub _tree_construction_main ($) { Line 3813  sub _tree_construction_main ($) {
3813          next B;          next B;
3814        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3815          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3816            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3817              !!!cp ('t272');              !!!cp ('t272');
3818              ## As if </option>              ## As if </option>
3819              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6399  sub _tree_construction_main ($) { Line 3826  sub _tree_construction_main ($) {
3826            !!!next-token;            !!!next-token;
3827            next B;            next B;
3828          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3829            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3830              !!!cp ('t274');              !!!cp ('t274');
3831              ## As if </option>              ## As if </option>
3832              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6407  sub _tree_construction_main ($) { Line 3834  sub _tree_construction_main ($) {
3834              !!!cp ('t275');              !!!cp ('t275');
3835            }            }
3836    
3837            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3838              !!!cp ('t276');              !!!cp ('t276');
3839              ## As if </optgroup>              ## As if </optgroup>
3840              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6420  sub _tree_construction_main ($) { Line 3847  sub _tree_construction_main ($) {
3847            !!!next-token;            !!!next-token;
3848            next B;            next B;
3849          } elsif ({          } elsif ({
3850                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3851                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3852                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3853                          == IN_SELECT_IN_TABLE_IM and
3854                    {                    {
3855                     caption => 1, table => 1,                     caption => 1, table => 1,
3856                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3857                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3858                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3859            ## TODO: The type below is not good - <select> is replaced by </select>  
3860            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3861                            token => $token);            if ($token->{tag_name} eq 'select') {
3862            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3863            ## as if there were </select> (otherwise).                                token => $token);
3864            ## have an element in table scope            } else {
3865                !!!parse-error (type => 'not closed', text => 'select',
3866                                token => $token);
3867              }
3868    
3869              ## 2./<select>-1. Unless "have an element in table scope" (select):
3870            my $i;            my $i;
3871            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3872              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3873              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3874                !!!cp ('t278');                !!!cp ('t278');
3875                $i = $_;                $i = $_;
3876                last INSCOPE;                last INSCOPE;
# Line 6448  sub _tree_construction_main ($) { Line 3881  sub _tree_construction_main ($) {
3881            } # INSCOPE            } # INSCOPE
3882            unless (defined $i) {            unless (defined $i) {
3883              !!!cp ('t280');              !!!cp ('t280');
3884              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3885                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3886              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3887                  ## case two errors, "select in select" and "unmatched
3888                  ## end tags" are reported to the user, the latter might
3889                  ## be confusing but this is what the spec requires.
3890                  !!!parse-error (type => 'unmatched end tag',
3891                                  text => 'select',
3892                                  token => $token);
3893                }
3894                ## Ignore the token.
3895              !!!nack ('t280.1');              !!!nack ('t280.1');
3896              !!!next-token;              !!!next-token;
3897              next B;              next B;
3898            }            }
3899    
3900              ## 3. Otherwise, as if there were <select>:
3901                                
3902            !!!cp ('t281');            !!!cp ('t281');
3903            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6482  sub _tree_construction_main ($) { Line 3925  sub _tree_construction_main ($) {
3925          }          }
3926        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3927          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3928            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3929                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3930              !!!cp ('t283');              !!!cp ('t283');
3931              ## As if </option>              ## As if </option>
3932              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3933            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3934              !!!cp ('t284');              !!!cp ('t284');
3935              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3936            } else {            } else {
# Line 6500  sub _tree_construction_main ($) { Line 3943  sub _tree_construction_main ($) {
3943            !!!next-token;            !!!next-token;
3944            next B;            next B;
3945          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3946            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3947              !!!cp ('t286');              !!!cp ('t286');
3948              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3949            } else {            } else {
# Line 6517  sub _tree_construction_main ($) { Line 3960  sub _tree_construction_main ($) {
3960            my $i;            my $i;
3961            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3962              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3963              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3964                !!!cp ('t288');                !!!cp ('t288');
3965                $i = $_;                $i = $_;
3966                last INSCOPE;                last INSCOPE;
# Line 6544  sub _tree_construction_main ($) { Line 3987  sub _tree_construction_main ($) {
3987            !!!nack ('t291.1');            !!!nack ('t291.1');
3988            !!!next-token;            !!!next-token;
3989            next B;            next B;
3990          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
3991                         == IN_SELECT_IN_TABLE_IM and
3992                   {                   {
3993                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
3994                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6579  sub _tree_construction_main ($) { Line 4023  sub _tree_construction_main ($) {
4023            undef $i;            undef $i;
4024            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4025              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4026              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4027                !!!cp ('t295');                !!!cp ('t295');
4028                $i = $_;                $i = $_;
4029                last INSCOPE;                last INSCOPE;
# Line 6618  sub _tree_construction_main ($) { Line 4062  sub _tree_construction_main ($) {
4062            next B;            next B;
4063          }          }
4064        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4065          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4066                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4067            !!!cp ('t299.1');            !!!cp ('t299.1');
4068            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6805  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4250          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4251              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4252            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4253                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4254              !!!cp ('t325');              !!!cp ('t325');
4255              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6819  sub _tree_construction_main ($) { Line 4263  sub _tree_construction_main ($) {
4263            }            }
4264    
4265            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4266                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4267              !!!cp ('t327');              !!!cp ('t327');
4268              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4269            } else {            } else {
# Line 6851  sub _tree_construction_main ($) { Line 4295  sub _tree_construction_main ($) {
4295            next B;            next B;
4296          }          }
4297        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4298          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4299                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4300            !!!cp ('t331.1');            !!!cp ('t331.1');
4301            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6954  sub _tree_construction_main ($) { Line 4398  sub _tree_construction_main ($) {
4398          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4399                                
4400          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4401              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4402            !!!cp ('t342');            !!!cp ('t342');
4403            ## Ignore the token            ## Ignore the token
4404          } else {          } else {
# Line 6989  sub _tree_construction_main ($) { Line 4433  sub _tree_construction_main ($) {
4433                  table => 1,                  table => 1,
4434                  hr => 1,                  hr => 1,
4435                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4436    
4437            ## 1. When there is an opening |form| element:
4438          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4439            !!!cp ('t350');            !!!cp ('t350');
4440            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6998  sub _tree_construction_main ($) { Line 4444  sub _tree_construction_main ($) {
4444            next B;            next B;
4445          }          }
4446    
4447          ## has a p element in scope          ## 2. Close the |p| element, if any.
4448          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4449            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4450              !!!cp ('t344');            ## has a p element in scope
4451              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4452              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4453                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4454              next B;                !!!back-token; # <form>
4455            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4456              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4457              last INSCOPE;                next B;
4458                } elsif ($_->[1] & SCOPING_EL) {
4459                  !!!cp ('t345');
4460                  last INSCOPE;
4461                }
4462              } # INSCOPE
4463            }
4464    
4465            ## 3. Close the opening <hn> element, if any.
4466            if ({h1 => 1, h2 => 1, h3 => 1,
4467                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4468              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4469                !!!parse-error (type => 'not closed',
4470                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4471                                token => $token);
4472                pop @{$self->{open_elements}};
4473            }            }
4474          } # INSCOPE          }
4475              
4476            ## 4. Insertion.
4477          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4478          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4479            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 7055  sub _tree_construction_main ($) { Line 4517  sub _tree_construction_main ($) {
4517        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4518          ## NOTE: As normal, but imply </li> when there's another <li> ...          ## NOTE: As normal, but imply </li> when there's another <li> ...
4519    
4520          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4521            ## Interpreted as <li><foo/></li><li/> (non-conforming)            ## Interpreted as <li><foo/></li><li/> (non-conforming):
4522            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4523            ## 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),
4524            ## 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),
4525            ## object (Fx)            ## object (Fx)
4526            ## Generate non-tree (non-conforming)            ## Generate non-tree (non-conforming):
4527            ## basefont (IE7 (where basefont is non-void)), center (IE),            ## basefont (IE7 (where basefont is non-void)), center (IE),
4528            ## form (IE), hn (IE)            ## form (IE), hn (IE)
4529          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4530            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)            ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4531            ## div (Fx, S)            ## div (Fx, S)
4532    
4533          my $non_optional;          my $non_optional;
# Line 7073  sub _tree_construction_main ($) { Line 4535  sub _tree_construction_main ($) {
4535    
4536          ## 1.          ## 1.
4537          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4538            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4539              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4540              {              {
4541                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7104  sub _tree_construction_main ($) { Line 4566  sub _tree_construction_main ($) {
4566                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4567                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4568                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4569                       (not $node->[1] & ADDRESS_DIV_P_EL)
4570                     (not $node->[1] & ADDRESS_EL) &                    ) {
                    (not $node->[1] & DIV_EL) &  
                    (not $node->[1] & P_EL)) {  
4571              ## 3.              ## 3.
4572              !!!cp ('t357');              !!!cp ('t357');
4573              last; ## goto 5.              last; ## goto 5.
# Line 7126  sub _tree_construction_main ($) { Line 4586  sub _tree_construction_main ($) {
4586    
4587          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4588          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4589            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4590              !!!cp ('t353');              !!!cp ('t353');
4591    
4592              ## NOTE: |<p><li>|, for example.              ## NOTE: |<p><li>|, for example.
# Line 7155  sub _tree_construction_main ($) { Line 4615  sub _tree_construction_main ($) {
4615    
4616          ## 1.          ## 1.
4617          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4618            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4619              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4620              {              {
4621                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7187  sub _tree_construction_main ($) { Line 4647  sub _tree_construction_main ($) {
4647                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4648                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4649    
4650                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4651                     (not $node->[1] & DIV_EL) &                    ) {
                    (not $node->[1] & P_EL)) {  
4652              ## 3.              ## 3.
4653              !!!cp ('t357.1');              !!!cp ('t357.1');
4654              last; ## goto 5.              last; ## goto 5.
# Line 7208  sub _tree_construction_main ($) { Line 4667  sub _tree_construction_main ($) {
4667    
4668          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4669          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4670            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4671              !!!cp ('t353.1');              !!!cp ('t353.1');
4672              !!!back-token; # <x>              !!!back-token; # <x>
4673              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7230  sub _tree_construction_main ($) { Line 4689  sub _tree_construction_main ($) {
4689    
4690          ## has a p element in scope          ## has a p element in scope
4691          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4692            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4693              !!!cp ('t367');              !!!cp ('t367');
4694              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4695              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7252  sub _tree_construction_main ($) { Line 4711  sub _tree_construction_main ($) {
4711        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4712          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4713            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4714            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4715              !!!cp ('t371');              !!!cp ('t371');
4716              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4717                            
# Line 7296  sub _tree_construction_main ($) { Line 4755  sub _tree_construction_main ($) {
4755          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4756          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4757            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4758            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4759              !!!cp ('t376');              !!!cp ('t376');
4760              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4761              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7319  sub _tree_construction_main ($) { Line 4778  sub _tree_construction_main ($) {
4778          ## has a button element in scope          ## has a button element in scope
4779          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4780            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4781            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4782              !!!cp ('t378');              !!!cp ('t378');
4783              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4784              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7384  sub _tree_construction_main ($) { Line 4843  sub _tree_construction_main ($) {
4843                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4844                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4845                           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}},  
4846                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4847                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4848                         );                         );
# Line 7408  sub _tree_construction_main ($) { Line 4865  sub _tree_construction_main ($) {
4865                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4866                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4867                           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}},  
4868                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4869                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4870                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7419  sub _tree_construction_main ($) { Line 4874  sub _tree_construction_main ($) {
4874            next B;            next B;
4875          }          }
4876        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4877          ## Step 1          ## 1. Insert
4878          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4879                    
4880          ## Step 2          ## Step 2 # XXX
4881          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4882    
4883          ## Step 3          ## 2. Drop U+000A LINE FEED
4884          $self->{ignore_newline} = 1;          $self->{ignore_newline} = 1;
4885    
4886          ## Step 4          ## 3. RCDATA
         ## ISSUE: This step is wrong. (r2302 enbugged)  
   
         ## Step 5  
4887          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4888          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4889    
4890          ## Step 6-7          ## 4., 6. Insertion mode
4891          $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;          $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4892    
4893            ## XXX: 5. frameset-ok flag
4894    
4895          !!!nack ('t392.1');          !!!nack ('t392.1');
4896          !!!next-token;          !!!next-token;
4897          next B;          next B;
# Line 7446  sub _tree_construction_main ($) { Line 4900  sub _tree_construction_main ($) {
4900          ## has an |option| element in scope          ## has an |option| element in scope
4901          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4902            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4903            if ($node->[1] & OPTION_EL) {            if ($node->[1] == OPTION_EL) {
4904              !!!cp ('t397.1');              !!!cp ('t397.1');
4905              ## NOTE: As if </option>              ## NOTE: As if </option>
4906              !!!back-token; # <option> or <optgroup>              !!!back-token; # <option> or <optgroup>
# Line 7471  sub _tree_construction_main ($) { Line 4925  sub _tree_construction_main ($) {
4925          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4926          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4927            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4928            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4929              !!!cp ('t398.1');              !!!cp ('t398.1');
4930              ## generate implied end tags              ## generate implied end tags
4931              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4932                !!!cp ('t398.2');                !!!cp ('t398.2');
4933                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4934              }              }
4935              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4936                !!!cp ('t398.3');                !!!cp ('t398.3');
4937                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4938                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4939                                    ->manakai_local_name,                                    ->manakai_local_name,
4940                                token => $token);                                token => $token);
4941                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4942                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4943              }              }
4944              last INSCOPE;              last INSCOPE;
4945            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7493  sub _tree_construction_main ($) { Line 4947  sub _tree_construction_main ($) {
4947              last INSCOPE;              last INSCOPE;
4948            }            }
4949          } # INSCOPE          } # INSCOPE
4950              
4951            ## TODO: <non-ruby><rt> is not allowed.
4952    
4953          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4954    
# Line 7590  sub _tree_construction_main ($) { Line 5046  sub _tree_construction_main ($) {
5046                    
5047            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5048                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5049                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5050              !!!cp ('t400.1');              !!!cp ('t400.1');
5051              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5052            } else {            } else {
# Line 7607  sub _tree_construction_main ($) { Line 5063  sub _tree_construction_main ($) {
5063        }        }
5064      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5065        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5066          ## has a |body| element in scope  
5067            ## 1. If not "have an element in scope":
5068            ## "has a |body| element in scope"
5069          my $i;          my $i;
5070          INSCOPE: {          INSCOPE: {
5071            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5072              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5073                !!!cp ('t405');                !!!cp ('t405');
5074                $i = $_;                $i = $_;
5075                last INSCOPE;                last INSCOPE;
# Line 7630  sub _tree_construction_main ($) { Line 5088  sub _tree_construction_main ($) {
5088            next B;            next B;
5089          } # INSCOPE          } # INSCOPE
5090    
5091            ## 2. If unclosed elements:
5092          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5093            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5094                      $_->[1] == OPTGROUP_EL ||
5095                      $_->[1] == OPTION_EL ||
5096                      $_->[1] == RUBY_COMPONENT_EL) {
5097              !!!cp ('t403');              !!!cp ('t403');
5098              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5099                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7642  sub _tree_construction_main ($) { Line 5104  sub _tree_construction_main ($) {
5104            }            }
5105          }          }
5106    
5107            ## 3. Switch the insertion mode.
5108          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5109          !!!next-token;          !!!next-token;
5110          next B;          next B;
# Line 7649  sub _tree_construction_main ($) { Line 5112  sub _tree_construction_main ($) {
5112          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5113          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5114          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5115              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5116            unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
5117              !!!cp ('t406');              !!!cp ('t406');
5118              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5119                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7757  sub _tree_construction_main ($) { Line 5220  sub _tree_construction_main ($) {
5220          my $i;          my $i;
5221          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5222            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5223            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5224              !!!cp ('t418');              !!!cp ('t418');
5225              $i = $_;              $i = $_;
5226              last INSCOPE;              last INSCOPE;
# Line 7805  sub _tree_construction_main ($) { Line 5268  sub _tree_construction_main ($) {
5268          my $i;          my $i;
5269          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5270            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5271            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5272              !!!cp ('t423');              !!!cp ('t423');
5273              $i = $_;              $i = $_;
5274              last INSCOPE;              last INSCOPE;
# Line 7851  sub _tree_construction_main ($) { Line 5314  sub _tree_construction_main ($) {
5314          my $i;          my $i;
5315          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5316            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5317            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5318              !!!cp ('t410.1');              !!!cp ('t410.1');
5319              $i = $_;              $i = $_;
5320              last INSCOPE;              last INSCOPE;
# Line 8028  sub _tree_construction_main ($) { Line 5491  sub _tree_construction_main ($) {
5491    ## TODO: script stuffs    ## TODO: script stuffs
5492  } # _tree_construct_main  } # _tree_construct_main
5493    
5494    ## XXX: How this method is organized is somewhat out of date, although
5495    ## it still does what the current spec documents.
5496  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5497    my $class = shift;    my $class = shift;
5498    my $node = shift;    my $node = shift; # /context/
5499    #my $s = \$_[0];    #my $s = \$_[0];
5500    my $onerror = $_[1];    my $onerror = $_[1];
5501    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
# Line 8038  sub set_inner_html ($$$$;$) { Line 5503  sub set_inner_html ($$$$;$) {
5503    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
5504    
5505    my $nt = $node->node_type;    my $nt = $node->node_type;
5506    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5507      # MUST      # MUST
5508            
5509      ## Step 1 # MUST      ## Step 1 # MUST
# Line 8053  sub set_inner_html ($$$$;$) { Line 5518  sub set_inner_html ($$$$;$) {
5518    
5519      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5520      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5521    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5522      ## TODO: If non-html element      ## TODO: If non-html element
5523    
5524      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5525    
5526  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5527    
5528      ## Step 1 # MUST      ## F1. Create an HTML document.
5529      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5530      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5531      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5532    
5533        ## F2. Propagate quirkness flag
5534        my $node_doc = $node->owner_document;
5535        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5536    
5537        ## F3. Create an HTML parser
5538      my $p = $class->new;      my $p = $class->new;
5539      $p->{document} = $doc;      $p->{document} = $doc;
5540    
# Line 8191  sub set_inner_html ($$$$;$) { Line 5662  sub set_inner_html ($$$$;$) {
5662      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5663      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5664    
5665      ## Step 2      ## F4. If /context/ is not undef...
5666    
5667        ## F4.1. content model flag
5668      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5669      $p->{content_model} = {      $p->{content_model} = {
5670        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8207  sub set_inner_html ($$$$;$) { Line 5680  sub set_inner_html ($$$$;$) {
5680      }->{$node_ln};      }->{$node_ln};
5681      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5682          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5683    
5684      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5685        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5686    
5687      ## Step 3      ## F4.2. Root |html| element
5688      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5689        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5690    
5691      ## Step 4 # MUST      ## F4.3.
5692      $doc->append_child ($root);      $doc->append_child ($root);
5693    
5694      ## Step 5 # MUST      ## F4.4.
5695      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5696    
5697      undef $p->{head_element};      undef $p->{head_element};
5698      undef $p->{head_element_inserted};      undef $p->{head_element_inserted};
5699    
5700      ## Step 6 # MUST      ## F4.5.
5701      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5702    
5703      ## Step 7 # MUST      ## F4.6.
5704      my $anode = $node;      my $anode = $node;
5705      AN: while (defined $anode) {      AN: while (defined $anode) {
5706        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8243  sub set_inner_html ($$$$;$) { Line 5715  sub set_inner_html ($$$$;$) {
5715        }        }
5716        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5717      } # AN      } # AN
5718        
5719      ## Step 9 # MUST      ## F.6. Start the parser.
5720      {      {
5721        my $self = $p;        my $self = $p;
5722        !!!next-token;        !!!next-token;
5723      }      }
5724      $p->_tree_construction_main;      $p->_tree_construction_main;
5725    
5726      ## Step 10 # MUST      ## F.7.
5727      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5728      for (@cn) {      for (@cn) {
5729        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.205  
changed lines
  Added in v.1.225

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24