/[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.197 by wakaba, Sat Oct 4 08:29:19 2008 UTC revision 1.229 by wakaba, Sat Sep 5 13:30:43 2009 UTC
# Line 3  use strict; Line 3  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4  use Error qw(:try);  use Error qw(:try);
5    
6    use Whatpm::HTML::Tokenizer;
7    
8  ## NOTE: This module don't check all HTML5 parse errors; character  ## NOTE: This module don't check all HTML5 parse errors; character
9  ## encoding related parse errors are expected to be handled by relevant  ## encoding related parse errors are expected to be handled by relevant
10  ## modules.  ## modules.
# Line 21  use Error qw(:try); Line 23  use Error qw(:try);
23    
24  require IO::Handle;  require IO::Handle;
25    
26    ## Namespace URLs
27    
28  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
29  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
30  my $SVG_NS = q<http://www.w3.org/2000/svg>;  my $SVG_NS = q<http://www.w3.org/2000/svg>;
# Line 28  my $XLINK_NS = q<http://www.w3.org/1999/ Line 32  my $XLINK_NS = q<http://www.w3.org/1999/
32  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
33  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
34    
35  sub A_EL () { 0b1 }  ## Element categories
36  sub ADDRESS_EL () { 0b10 }  
37  sub BODY_EL () { 0b100 }  ## Bits 12-15
38  sub BUTTON_EL () { 0b1000 }  sub SPECIAL_EL () { 0b1_000000000000000 }
39  sub CAPTION_EL () { 0b10000 }  sub SCOPING_EL () { 0b1_00000000000000 }
40  sub DD_EL () { 0b100000 }  sub FORMATTING_EL () { 0b1_0000000000000 }
41  sub DIV_EL () { 0b1000000 }  sub PHRASING_EL () { 0b1_000000000000 }
42  sub DT_EL () { 0b10000000 }  
43  sub FORM_EL () { 0b100000000 }  ## Bits 10-11
44  sub FORMATTING_EL () { 0b1000000000 }  #sub FOREIGN_EL () { 0b1_00000000000 } # see Whatpm::HTML::Tokenizer
45  sub FRAMESET_EL () { 0b10000000000 }  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
46  sub HEADING_EL () { 0b100000000000 }  
47  sub HTML_EL () { 0b1000000000000 }  ## Bits 6-9
48  sub LI_EL () { 0b10000000000000 }  sub TABLE_SCOPING_EL () { 0b1_000000000 }
49  sub NOBR_EL () { 0b100000000000000 }  sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
50  sub OPTION_EL () { 0b1000000000000000 }  sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
51  sub OPTGROUP_EL () { 0b10000000000000000 }  sub TABLE_ROWS_EL () { 0b1_000000 }
52  sub P_EL () { 0b100000000000000000 }  
53  sub SELECT_EL () { 0b1000000000000000000 }  ## Bit 5
54  sub TABLE_EL () { 0b10000000000000000000 }  sub ADDRESS_DIV_P_EL () { 0b1_00000 }
55  sub TABLE_CELL_EL () { 0b100000000000000000000 }  
56  sub TABLE_ROW_EL () { 0b1000000000000000000000 }  ## NOTE: Used in </body> and EOF algorithms.
57  sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }  ## Bit 4
58  sub MISC_SCOPING_EL () { 0b100000000000000000000000 }  sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
 sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }  
 sub FOREIGN_EL () { 0b10000000000000000000000000 }  
 sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }  
 sub MML_AXML_EL () { 0b1000000000000000000000000000 }  
 sub RUBY_EL () { 0b10000000000000000000000000000 }  
 sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }  
   
 sub TABLE_ROWS_EL () {  
   TABLE_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
59    
60  ## NOTE: Used in "generate implied end tags" algorithm.  ## NOTE: Used in "generate implied end tags" algorithm.
61  ## NOTE: There is a code where a modified version of  ## NOTE: There is a code where a modified version of
62  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"  ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
63  ## implementation (search for the algorithm name).  ## implementation (search for the algorithm name).
64  sub END_TAG_OPTIONAL_EL () {  ## Bit 3
65    DD_EL |  sub END_TAG_OPTIONAL_EL () { 0b1_000 }
   DT_EL |  
   LI_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   P_EL |  
   RUBY_COMPONENT_EL  
 }  
66    
67  ## NOTE: Used in </body> and EOF algorithms.  ## Bits 0-2
 sub ALL_END_TAG_OPTIONAL_EL () {  
   DD_EL |  
   DT_EL |  
   LI_EL |  
   P_EL |  
   
   BODY_EL |  
   HTML_EL |  
   TABLE_CELL_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL  
 }  
68    
69  sub SCOPING_EL () {  sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
70    BUTTON_EL |  sub FORM_EL () { SPECIAL_EL | 0b001 }
71    CAPTION_EL |  sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
72    HTML_EL |  sub HEADING_EL () { SPECIAL_EL | 0b011 }
73    TABLE_EL |  sub SELECT_EL () { SPECIAL_EL | 0b100 }
74    TABLE_CELL_EL |  sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
75    MISC_SCOPING_EL  
76    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
77    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
78    
79    sub DTDD_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84  }  }
85    sub LI_EL () {
86  sub TABLE_SCOPING_EL () {    SPECIAL_EL |
87    HTML_EL |    END_TAG_OPTIONAL_EL |
88    TABLE_EL    ALL_END_TAG_OPTIONAL_EL |
89      0b100
90  }  }
91    sub P_EL () {
92  sub TABLE_ROWS_SCOPING_EL () {    SPECIAL_EL |
93    HTML_EL |    ADDRESS_DIV_P_EL |
94    TABLE_ROW_GROUP_EL    END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97  }  }
98    
99  sub TABLE_ROW_SCOPING_EL () {  sub TABLE_ROW_EL () {
100    HTML_EL |    SPECIAL_EL |
101    TABLE_ROW_EL    TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112  }  }
113    
114  sub SPECIAL_EL () {  sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    ADDRESS_EL |  sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    BODY_EL |  sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    DIV_EL |  sub HTML_EL () {
118      SCOPING_EL |
119    DD_EL |    TABLE_SCOPING_EL |
120    DT_EL |    TABLE_ROWS_SCOPING_EL |
121    LI_EL |    TABLE_ROW_SCOPING_EL |
122    P_EL |    ALL_END_TAG_OPTIONAL_EL |
123      0b001
   FORM_EL |  
   FRAMESET_EL |  
   HEADING_EL |  
   OPTION_EL |  
   OPTGROUP_EL |  
   SELECT_EL |  
   TABLE_ROW_EL |  
   TABLE_ROW_GROUP_EL |  
   MISC_SPECIAL_EL  
124  }  }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151  my $el_category = {  my $el_category = {
152    a => A_EL | FORMATTING_EL,    a => A_EL,
153    address => ADDRESS_EL,    address => ADDRESS_DIV_EL,
154    applet => MISC_SCOPING_EL,    applet => MISC_SCOPING_EL,
155    area => MISC_SPECIAL_EL,    area => MISC_SPECIAL_EL,
156    article => MISC_SPECIAL_EL,    article => MISC_SPECIAL_EL,
# Line 160  my $el_category = { Line 170  my $el_category = {
170    colgroup => MISC_SPECIAL_EL,    colgroup => MISC_SPECIAL_EL,
171    command => MISC_SPECIAL_EL,    command => MISC_SPECIAL_EL,
172    datagrid => MISC_SPECIAL_EL,    datagrid => MISC_SPECIAL_EL,
173    dd => DD_EL,    dd => DTDD_EL,
174    details => MISC_SPECIAL_EL,    details => MISC_SPECIAL_EL,
175    dialog => MISC_SPECIAL_EL,    dialog => MISC_SPECIAL_EL,
176    dir => MISC_SPECIAL_EL,    dir => MISC_SPECIAL_EL,
177    div => DIV_EL,    div => ADDRESS_DIV_EL,
178    dl => MISC_SPECIAL_EL,    dl => MISC_SPECIAL_EL,
179    dt => DT_EL,    dt => DTDD_EL,
180    em => FORMATTING_EL,    em => FORMATTING_EL,
181    embed => MISC_SPECIAL_EL,    embed => MISC_SPECIAL_EL,
182    eventsource => MISC_SPECIAL_EL,    eventsource => MISC_SPECIAL_EL,
# Line 200  my $el_category = { Line 210  my $el_category = {
210    menu => MISC_SPECIAL_EL,    menu => MISC_SPECIAL_EL,
211    meta => MISC_SPECIAL_EL,    meta => MISC_SPECIAL_EL,
212    nav => MISC_SPECIAL_EL,    nav => MISC_SPECIAL_EL,
213    nobr => NOBR_EL | FORMATTING_EL,    nobr => NOBR_EL,
214    noembed => MISC_SPECIAL_EL,    noembed => MISC_SPECIAL_EL,
215    noframes => MISC_SPECIAL_EL,    noframes => MISC_SPECIAL_EL,
216    noscript => MISC_SPECIAL_EL,    noscript => MISC_SPECIAL_EL,
# Line 242  my $el_category = { Line 252  my $el_category = {
252  my $el_category_f = {  my $el_category_f = {
253    $MML_NS => {    $MML_NS => {
254      'annotation-xml' => MML_AXML_EL,      'annotation-xml' => MML_AXML_EL,
255      mi => FOREIGN_FLOW_CONTENT_EL,      mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
256      mo => FOREIGN_FLOW_CONTENT_EL,      mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257      mn => FOREIGN_FLOW_CONTENT_EL,      mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258      ms => FOREIGN_FLOW_CONTENT_EL,      ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      mtext => FOREIGN_FLOW_CONTENT_EL,      mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260    },    },
261    $SVG_NS => {    $SVG_NS => {
262      foreignObject => FOREIGN_FLOW_CONTENT_EL,      foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263      desc => FOREIGN_FLOW_CONTENT_EL,      desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264      title => FOREIGN_FLOW_CONTENT_EL,      title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265    },    },
266    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
267  };  };
# Line 338  my $foreign_attr_xname = { Line 348  my $foreign_attr_xname = {
348    
349  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
350    
 my $charref_map = {  
   0x0D => 0x000A,  
   0x80 => 0x20AC,  
   0x81 => 0xFFFD,  
   0x82 => 0x201A,  
   0x83 => 0x0192,  
   0x84 => 0x201E,  
   0x85 => 0x2026,  
   0x86 => 0x2020,  
   0x87 => 0x2021,  
   0x88 => 0x02C6,  
   0x89 => 0x2030,  
   0x8A => 0x0160,  
   0x8B => 0x2039,  
   0x8C => 0x0152,  
   0x8D => 0xFFFD,  
   0x8E => 0x017D,  
   0x8F => 0xFFFD,  
   0x90 => 0xFFFD,  
   0x91 => 0x2018,  
   0x92 => 0x2019,  
   0x93 => 0x201C,  
   0x94 => 0x201D,  
   0x95 => 0x2022,  
   0x96 => 0x2013,  
   0x97 => 0x2014,  
   0x98 => 0x02DC,  
   0x99 => 0x2122,  
   0x9A => 0x0161,  
   0x9B => 0x203A,  
   0x9C => 0x0153,  
   0x9D => 0xFFFD,  
   0x9E => 0x017E,  
   0x9F => 0x0178,  
 }; # $charref_map  
 $charref_map->{$_} = 0xFFFD  
     for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,  
         0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF  
         0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,  
         0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,  
         0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,  
         0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,  
         0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;  
   
351  ## TODO: Invoke the reset algorithm when a resettable element is  ## TODO: Invoke the reset algorithm when a resettable element is
352  ## created (cf. HTML5 revision 2259).  ## created (cf. HTML5 revision 2259).
353    
# Line 488  sub parse_byte_stream ($$$$;$$) { Line 454  sub parse_byte_stream ($$$$;$$) {
454      if (defined $charset_name) {      if (defined $charset_name) {
455        $charset = Message::Charset::Info->get_by_html_name ($charset_name);        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
456    
       ## ISSUE: Unsupported encoding is not ignored according to the spec.  
457        require Whatpm::Charset::DecodeHandle;        require Whatpm::Charset::DecodeHandle;
458        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
459            ($byte_stream);            ($byte_stream);
# Line 560  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 581  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 835  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 915  sub ROW_IMS ()        { 0b10000000 } Line 819  sub ROW_IMS ()        { 0b10000000 }
819  sub BODY_AFTER_IMS () { 0b100000000 }  sub BODY_AFTER_IMS () { 0b100000000 }
820  sub FRAME_IMS ()      { 0b1000000000 }  sub FRAME_IMS ()      { 0b1000000000 }
821  sub SELECT_IMS ()     { 0b10000000000 }  sub SELECT_IMS ()     { 0b10000000000 }
822  sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }  #sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 } # see Whatpm::HTML::Tokenizer
823      ## NOTE: "in foreign content" insertion mode is special; it is combined      ## NOTE: "in foreign content" insertion mode is special; it is combined
824      ## with the secondary insertion mode.  In this parser, they are stored      ## with the secondary insertion mode.  In this parser, they are stored
825      ## together in the bit-or'ed form.      ## together in the bit-or'ed form.
826    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
827        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
828        ## combined with the original insertion mode.  In thie parser,
829        ## they are stored together in the bit-or'ed form.
830    
831    sub IM_MASK () { 0b11111111111 }
832    
833  ## NOTE: "initial" and "before html" insertion modes have no constants.  ## NOTE: "initial" and "before html" insertion modes have no constants.
834    
# Line 945  sub IN_SELECT_IM () { SELECT_IMS | 0b01 Line 855  sub IN_SELECT_IM () { SELECT_IMS | 0b01
855  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }  sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
856  sub IN_COLUMN_GROUP_IM () { 0b10 }  sub IN_COLUMN_GROUP_IM () { 0b10 }
857    
 ## Implementations MUST act as if state machine in the spec  
   
 sub _initialize_tokenizer ($) {  
   my $self = shift;  
   $self->{state} = DATA_STATE; # MUST  
   #$self->{s_kwd}; # state keyword - initialized when used  
   #$self->{entity__value}; # initialized when used  
   #$self->{entity__match}; # initialized when used  
   $self->{content_model} = PCDATA_CONTENT_MODEL; # be  
   undef $self->{ct}; # current token  
   undef $self->{ca}; # current attribute  
   undef $self->{last_stag_name}; # last emitted start tag name  
   #$self->{prev_state}; # initialized when used  
   delete $self->{self_closing};  
   $self->{char_buffer} = '';  
   $self->{char_buffer_pos} = 0;  
   $self->{nc} = -1; # next input character  
   #$self->{next_nc}  
   !!!next-input-character;  
   $self->{token} = [];  
   # $self->{escape}  
 } # _initialize_tokenizer  
   
 ## A token has:  
 ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,  
 ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN  
 ##   ->{name} (DOCTYPE_TOKEN)  
 ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##   ->{pubid} (DOCTYPE_TOKEN)  
 ##   ->{sysid} (DOCTYPE_TOKEN)  
 ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag  
 ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)  
 ##        ->{name}  
 ##        ->{value}  
 ##        ->{has_reference} == 1 or 0  
 ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)  
 ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.  
 ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|  
 ##     while the token is pushed back to the stack.  
   
 ## Emitted token MUST immediately be handled by the tree construction state.  
   
 ## Before each step, UA MAY check to see if either one of the scripts in  
 ## "list of scripts that will execute as soon as possible" or the first  
 ## script in the "list of scripts that will execute asynchronously",  
 ## has completed loading.  If one has, then it MUST be executed  
 ## and removed from the list.  
   
 ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)  
 ## (This requirement was dropped from HTML5 spec, unfortunately.)  
   
 my $is_space = {  
   0x0009 => 1, # CHARACTER TABULATION (HT)  
   0x000A => 1, # LINE FEED (LF)  
   #0x000B => 0, # LINE TABULATION (VT)  
   0x000C => 1, # FORM FEED (FF)  
   #0x000D => 1, # CARRIAGE RETURN (CR)  
   0x0020 => 1, # SPACE (SP)  
 };  
   
 sub _get_next_token ($) {  
   my $self = shift;  
   
   if ($self->{self_closing}) {  
     !!!parse-error (type => 'nestc', token => $self->{ct});  
     ## NOTE: The |self_closing| flag is only set by start tag token.  
     ## In addition, when a start tag token is emitted, it is always set to  
     ## |ct|.  
     delete $self->{self_closing};  
   }  
   
   if (@{$self->{token}}) {  
     $self->{self_closing} = $self->{token}->[0]->{self_closing};  
     return shift @{$self->{token}};  
   }  
   
   A: {  
     if ($self->{state} == PCDATA_STATE) {  
       ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.  
   
       if ($self->{nc} == 0x0026) { # &  
         !!!cp (0.1);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity data state".  In this implementation, the tokenizer  
         ## is switched to the |ENTITY_STATE|, which is an implementation  
         ## of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = DATA_STATE;  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003C) { # <  
         !!!cp (0.2);  
         $self->{state} = TAG_OPEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (0.3);  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (0.4);  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       $self->{read_until}->($token->{data}, q[<&], length $token->{data});  
   
       ## Stay in the state.  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == DATA_STATE) {  
       $self->{s_kwd} = '' unless defined $self->{s_kwd};  
       if ($self->{nc} == 0x0026) { # &  
         $self->{s_kwd} = '';  
         if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA  
             not $self->{escape}) {  
           !!!cp (1);  
           ## NOTE: In the spec, the tokenizer is switched to the  
           ## "entity data state".  In this implementation, the tokenizer  
           ## is switched to the |ENTITY_STATE|, which is an implementation  
           ## of the "consume a character reference" algorithm.  
           $self->{entity_add} = -1;  
           $self->{prev_state} = DATA_STATE;  
           $self->{state} = ENTITY_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (2);  
           #  
         }  
       } elsif ($self->{nc} == 0x002D) { # -  
         if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
           $self->{s_kwd} .= '-';  
             
           if ($self->{s_kwd} eq '<!--') {  
             !!!cp (3);  
             $self->{escape} = 1; # unless $self->{escape};  
             $self->{s_kwd} = '--';  
             #  
           } elsif ($self->{s_kwd} eq '---') {  
             !!!cp (4);  
             $self->{s_kwd} = '--';  
             #  
           } else {  
             !!!cp (5);  
             #  
           }  
         }  
           
         #  
       } elsif ($self->{nc} == 0x0021) { # !  
         if (length $self->{s_kwd}) {  
           !!!cp (5.1);  
           $self->{s_kwd} .= '!';  
           #  
         } else {  
           !!!cp (5.2);  
           #$self->{s_kwd} = '';  
           #  
         }  
         #  
       } elsif ($self->{nc} == 0x003C) { # <  
         if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA  
             (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA  
              not $self->{escape})) {  
           !!!cp (6);  
           $self->{state} = TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (7);  
           $self->{s_kwd} = '';  
           #  
         }  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{escape} and  
             ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA  
           if ($self->{s_kwd} eq '--') {  
             !!!cp (8);  
             delete $self->{escape};  
           } else {  
             !!!cp (9);  
           }  
         } else {  
           !!!cp (10);  
         }  
           
         $self->{s_kwd} = '';  
         #  
       } elsif ($self->{nc} == -1) {  
         !!!cp (11);  
         $self->{s_kwd} = '';  
         !!!emit ({type => END_OF_FILE_TOKEN,  
                   line => $self->{line}, column => $self->{column}});  
         last A; ## TODO: ok?  
       } else {  
         !!!cp (12);  
         $self->{s_kwd} = '';  
         #  
       }  
   
       # Anything else  
       my $token = {type => CHARACTER_TOKEN,  
                    data => chr $self->{nc},  
                    line => $self->{line}, column => $self->{column},  
                   };  
       if ($self->{read_until}->($token->{data}, q[-!<>&],  
                                 length $token->{data})) {  
         $self->{s_kwd} = '';  
       }  
   
       ## Stay in the data state.  
       if ($self->{content_model} == PCDATA_CONTENT_MODEL) {  
         !!!cp (13);  
         $self->{state} = PCDATA_STATE;  
       } else {  
         !!!cp (14);  
         ## Stay in the state.  
       }  
       !!!next-input-character;  
       !!!emit ($token);  
       redo A;  
     } elsif ($self->{state} == TAG_OPEN_STATE) {  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if ($self->{nc} == 0x002F) { # /  
           !!!cp (15);  
           !!!next-input-character;  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           redo A;  
         } elsif ($self->{nc} == 0x0021) { # !  
           !!!cp (15.1);  
           $self->{s_kwd} = '<' unless $self->{escape};  
           #  
         } else {  
           !!!cp (16);  
           #  
         }  
   
         ## reconsume  
         $self->{state} = DATA_STATE;  
         !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA  
         if ($self->{nc} == 0x0021) { # !  
           !!!cp (17);  
           $self->{state} = MARKUP_DECLARATION_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x002F) { # /  
           !!!cp (18);  
           $self->{state} = CLOSE_TAG_OPEN_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0041 <= $self->{nc} and  
                  $self->{nc} <= 0x005A) { # A..Z  
           !!!cp (19);  
           $self->{ct}  
             = {type => START_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $self->{line_prev},  
                column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif (0x0061 <= $self->{nc} and  
                  $self->{nc} <= 0x007A) { # a..z  
           !!!cp (20);  
           $self->{ct} = {type => START_TAG_TOKEN,  
                                     tag_name => chr ($self->{nc}),  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev}};  
           $self->{state} = TAG_NAME_STATE;  
           !!!next-input-character;  
           redo A;  
         } elsif ($self->{nc} == 0x003E) { # >  
           !!!cp (21);  
           !!!parse-error (type => 'empty start tag',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           !!!next-input-character;  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<>',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         } elsif ($self->{nc} == 0x003F) { # ?  
           !!!cp (22);  
           !!!parse-error (type => 'pio',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = BOGUS_COMMENT_STATE;  
           $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                     line => $self->{line_prev},  
                                     column => $self->{column_prev},  
                                    };  
           ## $self->{nc} is intentionally left as is  
           redo A;  
         } else {  
           !!!cp (23);  
           !!!parse-error (type => 'bare stago',  
                           line => $self->{line_prev},  
                           column => $self->{column_prev});  
           $self->{state} = DATA_STATE;  
           ## reconsume  
   
           !!!emit ({type => CHARACTER_TOKEN, data => '<',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev},  
                    });  
   
           redo A;  
         }  
       } else {  
         die "$0: $self->{content_model} in tag open";  
       }  
     } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {  
       ## NOTE: The "close tag open state" in the spec is implemented as  
       ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.  
   
       my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"  
       if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA  
         if (defined $self->{last_stag_name}) {  
           $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;  
           $self->{s_kwd} = '';  
           ## Reconsume.  
           redo A;  
         } else {  
           ## No start tag token has ever been emitted  
           ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.  
           !!!cp (28);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                     line => $l, column => $c,  
                    });  
           redo A;  
         }  
       }  
   
       if (0x0041 <= $self->{nc} and  
           $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (29);  
         $self->{ct}  
             = {type => END_TAG_TOKEN,  
                tag_name => chr ($self->{nc} + 0x0020),  
                line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x007A) { # a..z  
         !!!cp (30);  
         $self->{ct} = {type => END_TAG_TOKEN,  
                                   tag_name => chr ($self->{nc}),  
                                   line => $l, column => $c};  
         $self->{state} = TAG_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (31);  
         !!!parse-error (type => 'empty end tag',  
                         line => $self->{line_prev}, ## "<" in "</>"  
                         column => $self->{column_prev} - 1);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (32);  
         !!!parse-error (type => 'bare etago');  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ({type => CHARACTER_TOKEN, data => '</',  
                   line => $l, column => $c,  
                  });  
   
         redo A;  
       } else {  
         !!!cp (33);  
         !!!parse-error (type => 'bogus end tag');  
         $self->{state} = BOGUS_COMMENT_STATE;  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev}, # "<" of "</"  
                                   column => $self->{column_prev} - 1,  
                                  };  
         ## NOTE: $self->{nc} is intentionally left as is.  
         ## Although the "anything else" case of the spec not explicitly  
         ## states that the next input character is to be reconsumed,  
         ## it will be included to the |data| of the comment token  
         ## generated from the bogus end tag, as defined in the  
         ## "bogus comment state" entry.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {  
       my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;  
       if (length $ch) {  
         my $CH = $ch;  
         $ch =~ tr/a-z/A-Z/;  
         my $nch = chr $self->{nc};  
         if ($nch eq $ch or $nch eq $CH) {  
           !!!cp (24);  
           ## Stay in the state.  
           $self->{s_kwd} .= $nch;  
           !!!next-input-character;  
           redo A;  
         } else {  
           !!!cp (25);  
           $self->{state} = DATA_STATE;  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         }  
       } else { # after "<{tag-name}"  
         unless ($is_space->{$self->{nc}} or  
                 {  
                  0x003E => 1, # >  
                  0x002F => 1, # /  
                  -1 => 1, # EOF  
                 }->{$self->{nc}}) {  
           !!!cp (26);  
           ## Reconsume.  
           $self->{state} = DATA_STATE;  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '</' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (27);  
           $self->{ct}  
               = {type => END_TAG_TOKEN,  
                  tag_name => $self->{last_stag_name},  
                  line => $self->{line_prev},  
                  column => $self->{column_prev} - 1 - length $self->{s_kwd}};  
           $self->{state} = TAG_NAME_STATE;  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == TAG_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (34);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (35);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This should never be reached.  
           #  !!! cp (36);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (37);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (38);  
         $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);  
           # start tag or end tag  
         ## Stay in this state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (39);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           #if ($self->{ct}->{attributes}) {  
           #  ## NOTE: This state should never be reached.  
           #  !!! cp (40);  
           #  !!! parse-error (type => 'end tag attribute');  
           #} else {  
             !!!cp (41);  
           #}  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (42);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (44);  
         $self->{ct}->{tag_name} .= chr $self->{nc};  
           # start tag or end tag  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (45);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (46);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (47);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (48);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (49);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (50);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (52);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (53);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp (54);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (55);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (56);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {  
       my $before_leave = sub {  
         if (exists $self->{ct}->{attributes} # start tag or end tag  
             ->{$self->{ca}->{name}}) { # MUST  
           !!!cp (57);  
           !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});  
           ## Discard $self->{ca} # MUST  
         } else {  
           !!!cp (58);  
           $self->{ct}->{attributes}->{$self->{ca}->{name}}  
             = $self->{ca};  
         }  
       }; # $before_leave  
   
       if ($is_space->{$self->{nc}}) {  
         !!!cp (59);  
         $before_leave->();  
         $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (60);  
         $before_leave->();  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (61);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp (62);  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!parse-error (type => 'end tag attribute');  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (63);  
         $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (64);  
         $before_leave->();  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         $before_leave->();  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (66);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (67);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (68);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (69);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (70);  
         }  
         $self->{ca}->{name} .= chr ($self->{nc});  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (71);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003D) { # =  
         !!!cp (72);  
         $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (73);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (74);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (75);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x005A) { # A..Z  
         !!!cp (76);  
         $self->{ca}  
             = {name => chr ($self->{nc} + 0x0020),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (77);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (79);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (80);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (81);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         # reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x0022 or # "  
             $self->{nc} == 0x0027) { # '  
           !!!cp (78);  
           !!!parse-error (type => 'bad attribute name');  
         } else {  
           !!!cp (82);  
         }  
         $self->{ca}  
             = {name => chr ($self->{nc}),  
                value => '',  
                line => $self->{line}, column => $self->{column}};  
         $self->{state} = ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;          
       }  
     } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (83);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (84);  
         $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (85);  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         ## reconsume  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (86);  
         $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!parse-error (type => 'empty unquoted attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (87);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (88);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (89);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (90);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (91);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (92);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ($self->{nc} == 0x003D) { # =  
           !!!cp (93);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (94);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (95);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (96);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{prev_state} = $self->{state};  
         $self->{entity_add} = 0x0022; # "  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (97);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (98);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (99);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (100);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (101);  
         $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (102);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = 0x0027; # '  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed attribute value');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (103);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (104);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (105);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         !!!cp (106);  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q['&],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (107);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0026) { # &  
         !!!cp (108);  
         ## NOTE: In the spec, the tokenizer is switched to the  
         ## "entity in attribute value state".  In this implementation, the  
         ## tokenizer is switched to the |ENTITY_STATE|, which is an  
         ## implementation of the "consume a character reference" algorithm.  
         $self->{entity_add} = -1;  
         $self->{prev_state} = $self->{state};  
         $self->{state} = ENTITY_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (109);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (110);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (111);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (112);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (113);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (114);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } else {  
         if ({  
              0x0022 => 1, # "  
              0x0027 => 1, # '  
              0x003D => 1, # =  
             }->{$self->{nc}}) {  
           !!!cp (115);  
           !!!parse-error (type => 'bad attribute value');  
         } else {  
           !!!cp (116);  
         }  
         $self->{ca}->{value} .= chr ($self->{nc});  
         $self->{read_until}->($self->{ca}->{value},  
                               q["'=& >],  
                               length $self->{ca}->{value});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (118);  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (119);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp (120);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (121);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == 0x002F) { # /  
         !!!cp (122);  
         $self->{state} = SELF_CLOSING_START_TAG_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (122.3);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (122.1);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (122.2);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.1');  
         !!!parse-error (type => 'no space between attributes');  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         if ($self->{ct}->{type} == END_TAG_TOKEN) {  
           !!!cp ('124.2');  
           !!!parse-error (type => 'nestc', token => $self->{ct});  
           ## TODO: Different type than slash in start tag  
           $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST  
           if ($self->{ct}->{attributes}) {  
             !!!cp ('124.4');  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             !!!cp ('124.5');  
           }  
           ## TODO: Test |<title></title/>|  
         } else {  
           !!!cp ('124.3');  
           $self->{self_closing} = 1;  
         }  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # start tag or end tag  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!parse-error (type => 'unclosed tag');  
         if ($self->{ct}->{type} == START_TAG_TOKEN) {  
           !!!cp (124.7);  
           $self->{last_stag_name} = $self->{ct}->{tag_name};  
         } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {  
           if ($self->{ct}->{attributes}) {  
             !!!cp (124.5);  
             !!!parse-error (type => 'end tag attribute');  
           } else {  
             ## NOTE: This state should never be reached.  
             !!!cp (124.6);  
           }  
         } else {  
           die "$0: $self->{ct}->{type}: Unknown token type";  
         }  
         $self->{state} = DATA_STATE;  
         ## Reconsume.  
         !!!emit ($self->{ct}); # start tag or end tag  
         redo A;  
       } else {  
         !!!cp ('124.4');  
         !!!parse-error (type => 'nestc');  
         ## TODO: This error type is wrong.  
         $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_COMMENT_STATE) {  
       ## (only happen if PCDATA state)  
   
       ## NOTE: Unlike spec's "bogus comment state", this implementation  
       ## consumes characters one-by-one basis.  
         
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (124);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (125);  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
         redo A;  
       } else {  
         !!!cp (126);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[>],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {  
       ## (only happen if PCDATA state)  
         
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (133);  
         $self->{state} = MD_HYPHEN_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0044 or # D  
                $self->{nc} == 0x0064) { # d  
         ## ASCII case-insensitive.  
         !!!cp (130);  
         $self->{state} = MD_DOCTYPE_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and  
                $self->{open_elements}->[-1]->[1] & FOREIGN_EL and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.4);                  
         $self->{state} = MD_CDATA_STATE;  
         $self->{s_kwd} = '[';  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (136);  
       }  
   
       !!!parse-error (type => 'bogus comment',  
                       line => $self->{line_prev},  
                       column => $self->{column_prev} - 1);  
       ## Reconsume.  
       $self->{state} = BOGUS_COMMENT_STATE;  
       $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                 line => $self->{line_prev},  
                                 column => $self->{column_prev} - 1,  
                                };  
       redo A;  
     } elsif ($self->{state} == MD_HYPHEN_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (127);  
         $self->{ct} = {type => COMMENT_TOKEN, data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         $self->{state} = COMMENT_START_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (128);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => '-',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 2,  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_DOCTYPE_STATE) {  
       ## ASCII case-insensitive.  
       if ($self->{nc} == [  
             undef,  
             0x004F, # O  
             0x0043, # C  
             0x0054, # T  
             0x0059, # Y  
             0x0050, # P  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x006F, # o  
             0x0063, # c  
             0x0074, # t  
             0x0079, # y  
             0x0070, # p  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (131);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 6 and  
                ($self->{nc} == 0x0045 or # E  
                 $self->{nc} == 0x0065)) { # e  
         !!!cp (129);  
         $self->{state} = DOCTYPE_STATE;  
         $self->{ct} = {type => DOCTYPE_TOKEN,  
                                   quirks => 1,  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7,  
                                  };  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (132);          
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == MD_CDATA_STATE) {  
       if ($self->{nc} == {  
             '[' => 0x0043, # C  
             '[C' => 0x0044, # D  
             '[CD' => 0x0041, # A  
             '[CDA' => 0x0054, # T  
             '[CDAT' => 0x0041, # A  
           }->{$self->{s_kwd}}) {  
         !!!cp (135.1);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{s_kwd} eq '[CDATA' and  
                $self->{nc} == 0x005B) { # [  
         !!!cp (135.2);  
         $self->{ct} = {type => CHARACTER_TOKEN,  
                                   data => '',  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 7};  
         $self->{state} = CDATA_SECTION_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (135.3);  
         !!!parse-error (type => 'bogus comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1 - length $self->{s_kwd});  
         $self->{state} = BOGUS_COMMENT_STATE;  
         ## Reconsume.  
         $self->{ct} = {type => COMMENT_TOKEN,  
                                   data => $self->{s_kwd},  
                                   line => $self->{line_prev},  
                                   column => $self->{column_prev} - 1 - length $self->{s_kwd},  
                                  };  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (137);  
         $self->{state} = COMMENT_START_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (138);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (139);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (140);  
         $self->{ct}->{data} # comment  
             .= chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_START_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (141);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (142);  
         !!!parse-error (type => 'bogus comment');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (143);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (144);  
         $self->{ct}->{data} # comment  
             .= '-' . chr ($self->{nc});  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (145);  
         $self->{state} = COMMENT_END_DASH_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (146);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (147);  
         $self->{ct}->{data} .= chr ($self->{nc}); # comment  
         $self->{read_until}->($self->{ct}->{data},  
                               q[-],  
                               length $self->{ct}->{data});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_DASH_STATE) {  
       if ($self->{nc} == 0x002D) { # -  
         !!!cp (148);  
         $self->{state} = COMMENT_END_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (149);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (150);  
         $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == COMMENT_END_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (151);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } elsif ($self->{nc} == 0x002D) { # -  
         !!!cp (152);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '-'; # comment  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (153);  
         !!!parse-error (type => 'unclosed comment');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # comment  
   
         redo A;  
       } else {  
         !!!cp (154);  
         !!!parse-error (type => 'dash in comment',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev});  
         $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment  
         $self->{state} = COMMENT_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (155);  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (156);  
         !!!parse-error (type => 'no space before DOCTYPE name');  
         $self->{state} = BEFORE_DOCTYPE_NAME_STATE;  
         ## reconsume  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (157);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (158);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (159);  
         !!!parse-error (type => 'no DOCTYPE name');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE (quirks)  
   
         redo A;  
       } else {  
         !!!cp (160);  
         $self->{ct}->{name} = chr $self->{nc};  
         delete $self->{ct}->{quirks};  
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_NAME_STATE) {  
 ## ISSUE: Redundant "First," in the spec.  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (161);  
         $self->{state} = AFTER_DOCTYPE_NAME_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (162);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (163);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (164);  
         $self->{ct}->{name}  
           .= chr ($self->{nc}); # DOCTYPE  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (165);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (166);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (167);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == 0x0050 or # P  
                $self->{nc} == 0x0070) { # p  
         $self->{state} = PUBLIC_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0053 or # S  
                $self->{nc} == 0x0073) { # s  
         $self->{state} = SYSTEM_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (180);  
         !!!parse-error (type => 'string after DOCTYPE name');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == PUBLIC_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0055, # U  
             0x0042, # B  
             0x004C, # L  
             0x0049, # I  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0075, # u  
             0x0062, # b  
             0x006C, # l  
             0x0069, # i  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (175);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x0043 or # C  
                 $self->{nc} == 0x0063)) { # c  
         !!!cp (168);  
         $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (169);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == SYSTEM_STATE) {  
       ## ASCII case-insensitive  
       if ($self->{nc} == [  
             undef,  
             0x0059, # Y  
             0x0053, # S  
             0x0054, # T  
             0x0045, # E  
           ]->[length $self->{s_kwd}] or  
           $self->{nc} == [  
             undef,  
             0x0079, # y  
             0x0073, # s  
             0x0074, # t  
             0x0065, # e  
           ]->[length $self->{s_kwd}]) {  
         !!!cp (170);  
         ## Stay in the state.  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif ((length $self->{s_kwd}) == 5 and  
                ($self->{nc} == 0x004D or # M  
                 $self->{nc} == 0x006D)) { # m  
         !!!cp (171);  
         $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (172);  
         !!!parse-error (type => 'string after DOCTYPE name',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} + 1 - length $self->{s_kwd});  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (181);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0022) { # "  
         !!!cp (182);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x0027) { # '  
         !!!cp (183);  
         $self->{ct}->{pubid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} eq 0x003E) { # >  
         !!!cp (184);  
         !!!parse-error (type => 'no PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (185);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (186);  
         !!!parse-error (type => 'string after PUBLIC');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (187);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (188);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (189);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (190);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q[">],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (191);  
         $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (192);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (193);  
         !!!parse-error (type => 'unclosed PUBLIC literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (194);  
         $self->{ct}->{pubid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{pubid}, q['>],  
                               length $self->{ct}->{pubid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (195);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (196);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (197);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (198);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (199);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (200);  
         !!!parse-error (type => 'string after PUBLIC literal');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (201);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0022) { # "  
         !!!cp (202);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x0027) { # '  
         !!!cp (203);  
         $self->{ct}->{sysid} = ''; # DOCTYPE  
         $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (204);  
         !!!parse-error (type => 'no SYSTEM literal');  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (205);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (206);  
         !!!parse-error (type => 'string after SYSTEM');  
         $self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0022) { # "  
         !!!cp (207);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (208);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (209);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (210);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q[">],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {  
       if ($self->{nc} == 0x0027) { # '  
         !!!cp (211);  
         $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (212);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (213);  
         !!!parse-error (type => 'unclosed SYSTEM literal');  
   
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (214);  
         $self->{ct}->{sysid} # DOCTYPE  
             .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{sysid}, q['>],  
                               length $self->{ct}->{sysid});  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {  
       if ($is_space->{$self->{nc}}) {  
         !!!cp (215);  
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003E) { # >  
         !!!cp (216);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (217);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         $self->{ct}->{quirks} = 1;  
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (218);  
         !!!parse-error (type => 'string after SYSTEM literal');  
         #$self->{ct}->{quirks} = 1;  
   
         $self->{state} = BOGUS_DOCTYPE_STATE;  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         !!!cp (219);  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } elsif ($self->{nc} == -1) {  
         !!!cp (220);  
         !!!parse-error (type => 'unclosed DOCTYPE');  
         $self->{state} = DATA_STATE;  
         ## reconsume  
   
         !!!emit ($self->{ct}); # DOCTYPE  
   
         redo A;  
       } else {  
         !!!cp (221);  
         my $s = '';  
         $self->{read_until}->($s, q[>], 0);  
   
         ## Stay in the state  
         !!!next-input-character;  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_STATE) {  
       ## NOTE: "CDATA section state" in the state is jointly implemented  
       ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,  
       ## and |CDATA_SECTION_MSE2_STATE|.  
         
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.1);  
         $self->{state} = CDATA_SECTION_MSE1_STATE;  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == -1) {  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.2);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.3);  
           ## No token to emit. $self->{ct} is discarded.  
         }          
         redo A;  
       } else {  
         !!!cp (221.4);  
         $self->{ct}->{data} .= chr $self->{nc};  
         $self->{read_until}->($self->{ct}->{data},  
                               q<]>,  
                               length $self->{ct}->{data});  
   
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       }  
   
       ## ISSUE: "text tokens" in spec.  
     } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {  
       if ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.5);  
         $self->{state} = CDATA_SECTION_MSE2_STATE;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.6);  
         $self->{ct}->{data} .= ']';  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {  
       if ($self->{nc} == 0x003E) { # >  
         $self->{state} = DATA_STATE;  
         !!!next-input-character;  
         if (length $self->{ct}->{data}) { # character  
           !!!cp (221.7);  
           !!!emit ($self->{ct}); # character  
         } else {  
           !!!cp (221.8);  
           ## No token to emit. $self->{ct} is discarded.  
         }  
         redo A;  
       } elsif ($self->{nc} == 0x005D) { # ]  
         !!!cp (221.9); # character  
         $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (221.11);  
         $self->{ct}->{data} .= ']]'; # character  
         $self->{state} = CDATA_SECTION_STATE;  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_STATE) {  
       if ($is_space->{$self->{nc}} or  
           {  
             0x003C => 1, 0x0026 => 1, -1 => 1, # <, &  
             $self->{entity_add} => 1,  
           }->{$self->{nc}}) {  
         !!!cp (1001);  
         ## Don't consume  
         ## No error  
         ## Return nothing.  
         #  
       } elsif ($self->{nc} == 0x0023) { # #  
         !!!cp (999);  
         $self->{state} = ENTITY_HASH_STATE;  
         $self->{s_kwd} = '#';  
         !!!next-input-character;  
         redo A;  
       } elsif ((0x0041 <= $self->{nc} and  
                 $self->{nc} <= 0x005A) or # A..Z  
                (0x0061 <= $self->{nc} and  
                 $self->{nc} <= 0x007A)) { # a..z  
         !!!cp (998);  
         require Whatpm::_NamedEntityList;  
         $self->{state} = ENTITY_NAME_STATE;  
         $self->{s_kwd} = chr $self->{nc};  
         $self->{entity__value} = $self->{s_kwd};  
         $self->{entity__match} = 0;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!cp (1027);  
         !!!parse-error (type => 'bare ero');  
         ## Return nothing.  
         #  
       }  
   
       ## NOTE: No character is consumed by the "consume a character  
       ## reference" algorithm.  In other word, there is an "&" character  
       ## that does not introduce a character reference, which would be  
       ## appended to the parent element or the attribute value in later  
       ## process of the tokenizer.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (997);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => '&',  
                   line => $self->{line_prev},  
                   column => $self->{column_prev},  
                  });  
         redo A;  
       } else {  
         !!!cp (996);  
         $self->{ca}->{value} .= '&';  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_HASH_STATE) {  
       if ($self->{nc} == 0x0078 or # x  
           $self->{nc} == 0x0058) { # X  
         !!!cp (995);  
         $self->{state} = HEXREF_X_STATE;  
         $self->{s_kwd} .= chr $self->{nc};  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0030 <= $self->{nc} and  
                $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (994);  
         $self->{state} = NCR_NUM_STATE;  
         $self->{s_kwd} = $self->{nc} - 0x0030;  
         !!!next-input-character;  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare nero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 1);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" is appended to the parent element or the attribute  
         ## value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1019);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&#',  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - 1,  
                    });  
           redo A;  
         } else {  
           !!!cp (993);  
           $self->{ca}->{value} .= '&#';  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == NCR_NUM_STATE) {  
       if (0x0030 <= $self->{nc} and  
           $self->{nc} <= 0x0039) { # 0..9  
         !!!cp (1012);  
         $self->{s_kwd} *= 10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
           
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1013);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1014);  
         !!!parse-error (type => 'no refc');  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1015);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1016);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (992);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (991);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == HEXREF_X_STATE) {  
       if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or  
           (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or  
           (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {  
         # 0..9, A..F, a..f  
         !!!cp (990);  
         $self->{state} = HEXREF_HEX_STATE;  
         $self->{s_kwd} = 0;  
         ## Reconsume.  
         redo A;  
       } else {  
         !!!parse-error (type => 'bare hcro',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - 2);  
   
         ## NOTE: According to the spec algorithm, nothing is returned,  
         ## and then "&#" followed by "X" or "x" is appended to the parent  
         ## element or the attribute value in the later processing.  
   
         if ($self->{prev_state} == DATA_STATE) {  
           !!!cp (1005);  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           !!!emit ({type => CHARACTER_TOKEN,  
                     data => '&' . $self->{s_kwd},  
                     line => $self->{line_prev},  
                     column => $self->{column_prev} - length $self->{s_kwd},  
                    });  
           redo A;  
         } else {  
           !!!cp (989);  
           $self->{ca}->{value} .= '&' . $self->{s_kwd};  
           $self->{state} = $self->{prev_state};  
           ## Reconsume.  
           redo A;  
         }  
       }  
     } elsif ($self->{state} == HEXREF_HEX_STATE) {  
       if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {  
         # 0..9  
         !!!cp (1002);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0030;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0061 <= $self->{nc} and  
                $self->{nc} <= 0x0066) { # a..f  
         !!!cp (1003);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0060 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif (0x0041 <= $self->{nc} and  
                $self->{nc} <= 0x0046) { # A..F  
         !!!cp (1004);  
         $self->{s_kwd} *= 0x10;  
         $self->{s_kwd} += $self->{nc} - 0x0040 + 9;  
         ## Stay in the state.  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{nc} == 0x003B) { # ;  
         !!!cp (1006);  
         !!!next-input-character;  
         #  
       } else {  
         !!!cp (1007);  
         !!!parse-error (type => 'no refc',  
                         line => $self->{line},  
                         column => $self->{column});  
         ## Reconsume.  
         #  
       }  
   
       my $code = $self->{s_kwd};  
       my $l = $self->{line_prev};  
       my $c = $self->{column_prev};  
       if ($charref_map->{$code}) {  
         !!!cp (1008);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U+%04X', $code),  
                         line => $l, column => $c);  
         $code = $charref_map->{$code};  
       } elsif ($code > 0x10FFFF) {  
         !!!cp (1009);  
         !!!parse-error (type => 'invalid character reference',  
                         text => (sprintf 'U-%08X', $code),  
                         line => $l, column => $c);  
         $code = 0xFFFD;  
       }  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (988);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN, data => chr $code,  
                   line => $l, column => $c,  
                  });  
         redo A;  
       } else {  
         !!!cp (987);  
         $self->{ca}->{value} .= chr $code;  
         $self->{ca}->{has_reference} = 1;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } elsif ($self->{state} == ENTITY_NAME_STATE) {  
       if (length $self->{s_kwd} < 30 and  
           ## NOTE: Some number greater than the maximum length of entity name  
           ((0x0041 <= $self->{nc} and # a  
             $self->{nc} <= 0x005A) or # x  
            (0x0061 <= $self->{nc} and # a  
             $self->{nc} <= 0x007A) or # z  
            (0x0030 <= $self->{nc} and # 0  
             $self->{nc} <= 0x0039) or # 9  
            $self->{nc} == 0x003B)) { # ;  
         our $EntityChar;  
         $self->{s_kwd} .= chr $self->{nc};  
         if (defined $EntityChar->{$self->{s_kwd}}) {  
           if ($self->{nc} == 0x003B) { # ;  
             !!!cp (1020);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = 1;  
             !!!next-input-character;  
             #  
           } else {  
             !!!cp (1021);  
             $self->{entity__value} = $EntityChar->{$self->{s_kwd}};  
             $self->{entity__match} = -1;  
             ## Stay in the state.  
             !!!next-input-character;  
             redo A;  
           }  
         } else {  
           !!!cp (1022);  
           $self->{entity__value} .= chr $self->{nc};  
           $self->{entity__match} *= 2;  
           ## Stay in the state.  
           !!!next-input-character;  
           redo A;  
         }  
       }  
   
       my $data;  
       my $has_ref;  
       if ($self->{entity__match} > 0) {  
         !!!cp (1023);  
         $data = $self->{entity__value};  
         $has_ref = 1;  
         #  
       } elsif ($self->{entity__match} < 0) {  
         !!!parse-error (type => 'no refc');  
         if ($self->{prev_state} != DATA_STATE and # in attribute  
             $self->{entity__match} < -1) {  
           !!!cp (1024);  
           $data = '&' . $self->{s_kwd};  
           #  
         } else {  
           !!!cp (1025);  
           $data = $self->{entity__value};  
           $has_ref = 1;  
           #  
         }  
       } else {  
         !!!cp (1026);  
         !!!parse-error (type => 'bare ero',  
                         line => $self->{line_prev},  
                         column => $self->{column_prev} - length $self->{s_kwd});  
         $data = '&' . $self->{s_kwd};  
         #  
       }  
     
       ## NOTE: In these cases, when a character reference is found,  
       ## it is consumed and a character token is returned, or, otherwise,  
       ## nothing is consumed and returned, according to the spec algorithm.  
       ## In this implementation, anything that has been examined by the  
       ## tokenizer is appended to the parent element or the attribute value  
       ## as string, either literal string when no character reference or  
       ## entity-replaced string otherwise, in this stage, since any characters  
       ## that would not be consumed are appended in the data state or in an  
       ## appropriate attribute value state anyway.  
   
       if ($self->{prev_state} == DATA_STATE) {  
         !!!cp (986);  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         !!!emit ({type => CHARACTER_TOKEN,  
                   data => $data,  
                   line => $self->{line_prev},  
                   column => $self->{column_prev} + 1 - length $self->{s_kwd},  
                  });  
         redo A;  
       } else {  
         !!!cp (985);  
         $self->{ca}->{value} .= $data;  
         $self->{ca}->{has_reference} = 1 if $has_ref;  
         $self->{state} = $self->{prev_state};  
         ## Reconsume.  
         redo A;  
       }  
     } else {  
       die "$0: $self->{state}: Unknown state";  
     }  
   } # A    
   
   die "$0: _get_next_token: unexpected case";  
 } # _get_next_token  
   
858  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
859    my $self = shift;    my $self = shift;
860    ## NOTE: $self->{document} MUST be specified before this method is called    ## NOTE: $self->{document} MUST be specified before this method is called
# Line 3469  sub _construct_tree ($) { Line 883  sub _construct_tree ($) {
883    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
884    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
885    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
886        
887    !!!next-token;    !!!next-token;
888    
889    undef $self->{form_element};    undef $self->{form_element};
890    undef $self->{head_element};    undef $self->{head_element};
891      undef $self->{head_element_inserted};
892    $self->{open_elements} = [];    $self->{open_elements} = [];
893    undef $self->{inner_html_node};    undef $self->{inner_html_node};
894      undef $self->{ignore_newline};
895    
896    ## NOTE: The "initial" insertion mode.    ## NOTE: The "initial" insertion mode.
897    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
# Line 3499  sub _tree_construction_initial ($) { Line 911  sub _tree_construction_initial ($) {
911    
912    INITIAL: {    INITIAL: {
913      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
914        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not
915        ## error, switch to a conformance checking mode for another        ## HTML5" error, switch to a conformance checking mode for
916        ## language.        ## another language.  (We don't support such mode switchings; it
917          ## is nonsense to do anything different from what browsers do.)
918        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
919        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
920        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive        my $doctype = $self->{document}->create_document_type_definition
921        if (not defined $token->{name} or # <!DOCTYPE>            ($doctype_name);
922            defined $token->{sysid}) {  
923          $doctype_name =~ tr/A-Z/a-z/; # ASCII case-insensitive
924          if ($doctype_name ne 'html') {
925          !!!cp ('t1');          !!!cp ('t1');
926          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
927        } elsif ($doctype_name ne 'HTML') {        } elsif (defined $token->{pubid}) {
928          !!!cp ('t2');          !!!cp ('t2');
929            ## XXX Obsolete permitted DOCTYPEs
930          !!!parse-error (type => 'not HTML5', token => $token);          !!!parse-error (type => 'not HTML5', token => $token);
931        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{sysid}) {
932          if ($token->{pubid} eq 'XSLT-compat') {          if ($token->{sysid} eq 'about:legacy-compat') {
933            !!!cp ('t1.2');            !!!cp ('t1.2'); ## <!DOCTYPE HTML SYSTEM "about:legacy-compat">
934            !!!parse-error (type => 'XSLT-compat', token => $token,            !!!parse-error (type => 'XSLT-compat', token => $token,
935                            level => $self->{level}->{should});                            level => $self->{level}->{should});
936          } else {          } else {
937            !!!parse-error (type => 'not HTML5', token => $token);            !!!parse-error (type => 'not HTML5', token => $token);
938          }          }
939        } else {        } else { ## <!DOCTYPE HTML>
940          !!!cp ('t3');          !!!cp ('t3');
941          #          #
942        }        }
943                
       my $doctype = $self->{document}->create_document_type_definition  
         ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?  
944        ## NOTE: Default value for both |public_id| and |system_id| attributes        ## NOTE: Default value for both |public_id| and |system_id| attributes
945        ## are empty strings, so that we don't set any value in missing cases.        ## are empty strings, so that we don't set any value in missing cases.
946        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
947        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
948    
949        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
950        ## ISSUE: internalSubset = null??        ## In Firefox3, |internalSubset| attribute is set to the empty
951          ## string, while |null| is an allowed value for the attribute
952          ## according to DOM3 Core.
953        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
954                
955        if ($token->{quirks} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'html') {
956          !!!cp ('t4');          !!!cp ('t4');
957          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
958        } elsif (defined $token->{pubid}) {        } elsif (defined $token->{pubid}) {
# Line 3785  sub _tree_construction_root_element ($) Line 1202  sub _tree_construction_root_element ($)
1202      ## NOTE: Reprocess the token.      ## NOTE: Reprocess the token.
1203      !!!ack-later;      !!!ack-later;
1204      return; ## Go to the "before head" insertion mode.      return; ## Go to the "before head" insertion mode.
   
     ## ISSUE: There is an issue in the spec  
1205    } # B    } # B
1206    
1207    die "$0: _tree_construction_root_element: This should never be reached";    die "$0: _tree_construction_root_element: This should never be reached";
# Line 3822  sub _reset_insertion_mode ($) { Line 1237  sub _reset_insertion_mode ($) {
1237          ## SVG elements.  Currently the HTML syntax supports only MathML and          ## SVG elements.  Currently the HTML syntax supports only MathML and
1238          ## SVG elements as foreigners.          ## SVG elements as foreigners.
1239          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
1240        } elsif ($node->[1] & TABLE_CELL_EL) {        } elsif ($node->[1] == TABLE_CELL_EL) {
1241          if ($last) {          if ($last) {
1242            !!!cp ('t28.2');            !!!cp ('t28.2');
1243            #            #
# Line 3851  sub _reset_insertion_mode ($) { Line 1266  sub _reset_insertion_mode ($) {
1266        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
1267                
1268        ## Step 15        ## Step 15
1269        if ($node->[1] & HTML_EL) {        if ($node->[1] == HTML_EL) {
1270          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
1271            !!!cp ('t29');            !!!cp ('t29');
1272            $self->{insertion_mode} = BEFORE_HEAD_IM;            $self->{insertion_mode} = BEFORE_HEAD_IM;
# Line 3983  sub _tree_construction_main ($) { Line 1398  sub _tree_construction_main ($) {
1398    
1399      ## Step 1      ## Step 1
1400      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
1401      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);  
1402    
1403      ## Step 2      ## Step 2
     $insert->($el);  
   
     ## Step 3  
1404      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
1405      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
1406    
1407      ## Step 4      ## Step 3, 4
1408      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!nack ('t40.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing  
       !!!cp ('t40');  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       !!!cp ('t41');  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
1409    
1410      ## Step 6      !!!nack ('t40.1');
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
   
     ## Step 7  
     if ($token->{type} == END_TAG_TOKEN and  
         $token->{tag_name} eq $start_tag_name) {  
       !!!cp ('t42');  
       ## Ignore the token  
     } else {  
       ## NOTE: An end-of-file token.  
       if ($content_model_flag == CDATA_CONTENT_MODEL) {  
         !!!cp ('t43');  
         !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
         !!!cp ('t44');  
         !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
       } else {  
         die "$0: $content_model_flag in parse_rcdata";  
       }  
     }  
1411      !!!next-token;      !!!next-token;
1412    }; # $parse_rcdata    }; # $parse_rcdata
1413    
1414    my $script_start_tag = sub () {    my $script_start_tag = sub () {
1415        ## Step 1
1416      my $script_el;      my $script_el;
1417      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
1418    
1419        ## Step 2
1420      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
1421    
1422        ## Step 3
1423        ## TODO: Mark as "already executed", if ...
1424    
1425        ## Step 4 (HTML5 revision 2702)
1426        $insert->($script_el);
1427        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
1428    
1429        ## Step 5
1430      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
1431      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!nack ('t45.1');  
     !!!next-token;  
     while ($token->{type} == CHARACTER_TOKEN) {  
       !!!cp ('t45');  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       !!!cp ('t46');  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
1432    
1433      if ($token->{type} == END_TAG_TOKEN and      ## Step 6-7
1434          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       !!!cp ('t47');  
       ## Ignore the token  
     } else {  
       !!!cp ('t48');  
       !!!parse-error (type => 'in CDATA:#eof', token => $token);  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       !!!cp ('t49');  
       ## TODO: mark as "already executed"  
     } else {  
       !!!cp ('t50');  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
1435    
1436        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
1437      !!!next-token;      !!!next-token;
1438    }; # $script_start_tag    }; # $script_start_tag
1439    
1440    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.    ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
1441    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.    ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag (OBSOLETE; unused).
1442      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
1443    my $open_tables = [[$self->{open_elements}->[0]->[0]]];    my $open_tables = [[$self->{open_elements}->[0]->[0]]];
1444    
1445    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
# Line 4171  sub _tree_construction_main ($) { Line 1524  sub _tree_construction_main ($) {
1524            !!!cp ('t59');            !!!cp ('t59');
1525            $furthest_block = $node;            $furthest_block = $node;
1526            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
1527              ## NOTE: The topmost (eldest) node.
1528          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
1529            !!!cp ('t60');            !!!cp ('t60');
1530            last OE;            last OE;
# Line 4257  sub _tree_construction_main ($) { Line 1611  sub _tree_construction_main ($) {
1611          my $foster_parent_element;          my $foster_parent_element;
1612          my $next_sibling;          my $next_sibling;
1613          OE: for (reverse 0..$#{$self->{open_elements}}) {          OE: for (reverse 0..$#{$self->{open_elements}}) {
1614            if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {            if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1615                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1616                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1617                                 !!!cp ('t65.1');                                 !!!cp ('t65.1');
# Line 4317  sub _tree_construction_main ($) { Line 1671  sub _tree_construction_main ($) {
1671            $i = $_;            $i = $_;
1672          }          }
1673        } # OE        } # OE
1674        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
1675                
1676        ## Step 14        ## Step 14
1677        redo FET;        redo FET;
# Line 4335  sub _tree_construction_main ($) { Line 1689  sub _tree_construction_main ($) {
1689        my $foster_parent_element;        my $foster_parent_element;
1690        my $next_sibling;        my $next_sibling;
1691        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
1692          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {          if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
1693                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
1694                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
1695                                 !!!cp ('t70');                                 !!!cp ('t70');
# Line 4360  sub _tree_construction_main ($) { Line 1714  sub _tree_construction_main ($) {
1714      }      }
1715    }; # $insert_to_foster    }; # $insert_to_foster
1716    
1717      ## NOTE: Insert a character (MUST): When a character is inserted, if
1718      ## the last node that was inserted by the parser is a Text node and
1719      ## the character has to be inserted after that node, then the
1720      ## character is appended to the Text node.  However, if any other
1721      ## node is inserted by the parser, then a new Text node is created
1722      ## and the character is appended as that Text node.  If I'm not
1723      ## wrong, for a parser with scripting disabled, there are only two
1724      ## cases where this occurs.  One is the case where an element node
1725      ## is inserted to the |head| element.  This is covered by using the
1726      ## |$self->{head_element_inserted}| flag.  Another is the case where
1727      ## an element or comment is inserted into the |table| subtree while
1728      ## foster parenting happens.  This is covered by using the [2] flag
1729      ## of the |$open_tables| structure.  All other cases are handled
1730      ## simply by calling |manakai_append_text| method.
1731    
1732      ## TODO: |<body><script>document.write("a<br>");
1733      ## document.body.removeChild (document.body.lastChild);
1734      ## document.write ("b")</script>|
1735    
1736    B: while (1) {    B: while (1) {
1737      if ($token->{type} == DOCTYPE_TOKEN) {      if ($token->{type} == DOCTYPE_TOKEN) {
1738        !!!cp ('t73');        !!!cp ('t73');
# Line 4407  sub _tree_construction_main ($) { Line 1780  sub _tree_construction_main ($) {
1780        } else {        } else {
1781          !!!cp ('t87');          !!!cp ('t87');
1782          $self->{open_elements}->[-1]->[0]->append_child ($comment);          $self->{open_elements}->[-1]->[0]->append_child ($comment);
1783            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
1784        }        }
1785        !!!next-token;        !!!next-token;
1786        next B;        next B;
1787        } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
1788          if ($token->{type} == CHARACTER_TOKEN) {
1789            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
1790            delete $self->{ignore_newline};
1791    
1792            if (length $token->{data}) {
1793              !!!cp ('t43');
1794              $self->{open_elements}->[-1]->[0]->manakai_append_text
1795                  ($token->{data});
1796            } else {
1797              !!!cp ('t43.1');
1798            }
1799            !!!next-token;
1800            next B;
1801          } elsif ($token->{type} == END_TAG_TOKEN) {
1802            delete $self->{ignore_newline};
1803    
1804            if ($token->{tag_name} eq 'script') {
1805              !!!cp ('t50');
1806              
1807              ## Para 1-2
1808              my $script = pop @{$self->{open_elements}};
1809              
1810              ## Para 3
1811              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1812    
1813              ## Para 4
1814              ## TODO: $old_insertion_point = $current_insertion_point;
1815              ## TODO: $current_insertion_point = just before $self->{nc};
1816    
1817              ## Para 5
1818              ## TODO: Run the $script->[0].
1819    
1820              ## Para 6
1821              ## TODO: $current_insertion_point = $old_insertion_point;
1822    
1823              ## Para 7
1824              ## TODO: if ($pending_external_script) {
1825                ## TODO: ...
1826              ## TODO: }
1827    
1828              !!!next-token;
1829              next B;
1830            } else {
1831              !!!cp ('t42');
1832    
1833              pop @{$self->{open_elements}};
1834    
1835              $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1836              !!!next-token;
1837              next B;
1838            }
1839          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1840            delete $self->{ignore_newline};
1841    
1842            !!!cp ('t44');
1843            !!!parse-error (type => 'not closed',
1844                            text => $self->{open_elements}->[-1]->[0]
1845                                ->manakai_local_name,
1846                            token => $token);
1847    
1848            #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
1849            #  ## TODO: Mark as "already executed"
1850            #}
1851    
1852            pop @{$self->{open_elements}};
1853    
1854            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
1855            ## Reprocess.
1856            next B;
1857          } else {
1858            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
1859          }
1860      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
1861        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1862          !!!cp ('t87.1');          !!!cp ('t87.1');
# Line 4421  sub _tree_construction_main ($) { Line 1868  sub _tree_construction_main ($) {
1868               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
1869              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
1870              ($token->{tag_name} eq 'svg' and              ($token->{tag_name} eq 'svg' and
1871               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
1872            ## NOTE: "using the rules for secondary insertion mode"then"continue"            ## NOTE: "using the rules for secondary insertion mode"then"continue"
1873            !!!cp ('t87.2');            !!!cp ('t87.2');
1874            #            #
1875          } elsif ({          } elsif ({
1876                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
1877                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
1878                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,                    em => 1, embed => 1, h1 => 1, h2 => 1, h3 => 1,
1879                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
1880                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
1881                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
1882                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
1883                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
1884                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}} or
1885                     ($token->{tag_name} eq 'font' and
1886                      ($token->{attributes}->{color} or
1887                       $token->{attributes}->{face} or
1888                       $token->{attributes}->{size}))) {
1889            !!!cp ('t87.2');            !!!cp ('t87.2');
1890            !!!parse-error (type => 'not closed',            !!!parse-error (type => 'not closed',
1891                            text => $self->{open_elements}->[-1]->[0]                            text => $self->{open_elements}->[-1]->[0]
# Line 4510  sub _tree_construction_main ($) { Line 1961  sub _tree_construction_main ($) {
1961          }          }
1962        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
1963          ## NOTE: "using the rules for secondary insertion mode" then "continue"          ## NOTE: "using the rules for secondary insertion mode" then "continue"
1964          !!!cp ('t87.5');          if ($token->{tag_name} eq 'script') {
1965          #            !!!cp ('t87.41');
1966              #
1967              ## XXXscript: Execute script here.
1968            } else {
1969              !!!cp ('t87.5');
1970              #
1971            }
1972        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
1973          !!!cp ('t87.6');          !!!cp ('t87.6');
1974          !!!parse-error (type => 'not closed',          !!!parse-error (type => 'not closed',
# Line 4522  sub _tree_construction_main ($) { Line 1979  sub _tree_construction_main ($) {
1979          pop @{$self->{open_elements}}          pop @{$self->{open_elements}}
1980              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
1981    
1982            ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
1983    
1984          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
1985          ## Reprocess.          ## Reprocess.
1986          next B;          next B;
# Line 4534  sub _tree_construction_main ($) { Line 1993  sub _tree_construction_main ($) {
1993        if ($token->{type} == CHARACTER_TOKEN) {        if ($token->{type} == CHARACTER_TOKEN) {
1994          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
1995            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
1996              !!!cp ('t88.2');              if ($self->{head_element_inserted}) {
1997              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                !!!cp ('t88.3');
1998              #                $self->{open_elements}->[-1]->[0]->append_child
1999                    ($self->{document}->create_text_node ($1));
2000                  delete $self->{head_element_inserted};
2001                  ## NOTE: |</head> <link> |
2002                  #
2003                } else {
2004                  !!!cp ('t88.2');
2005                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
2006                  ## NOTE: |</head> &#x20;|
2007                  #
2008                }
2009            } else {            } else {
2010              !!!cp ('t88.1');              !!!cp ('t88.1');
2011              ## Ignore the token.              ## Ignore the token.
# Line 4632  sub _tree_construction_main ($) { Line 2101  sub _tree_construction_main ($) {
2101            !!!cp ('t97');            !!!cp ('t97');
2102          }          }
2103    
2104              if ($token->{tag_name} eq 'base') {          if ($token->{tag_name} eq 'base') {
2105                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2106                  !!!cp ('t98');              !!!cp ('t98');
2107                  ## As if </noscript>              ## As if </noscript>
2108                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2109                  !!!parse-error (type => 'in noscript', text => 'base',              !!!parse-error (type => 'in noscript', text => 'base',
2110                                  token => $token);                              token => $token);
2111                            
2112                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2113                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2114                } else {            } else {
2115                  !!!cp ('t99');              !!!cp ('t99');
2116                }            }
2117    
2118                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2119                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2120                  !!!cp ('t100');              !!!cp ('t100');
2121                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2122                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2123                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2124                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2125                } else {              $self->{head_element_inserted} = 1;
2126                  !!!cp ('t101');            } else {
2127                }              !!!cp ('t101');
2128                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2129                pop @{$self->{open_elements}};            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2130                pop @{$self->{open_elements}} # <head>            pop @{$self->{open_elements}};
2131                    if $self->{insertion_mode} == AFTER_HEAD_IM;            pop @{$self->{open_elements}} # <head>
2132                !!!nack ('t101.1');                if $self->{insertion_mode} == AFTER_HEAD_IM;
2133                !!!next-token;            !!!nack ('t101.1');
2134                next B;            !!!next-token;
2135              next B;
2136          } elsif ($token->{tag_name} eq 'link') {          } elsif ($token->{tag_name} eq 'link') {
2137            ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2138            if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
# Line 4671  sub _tree_construction_main ($) { Line 2141  sub _tree_construction_main ($) {
2141                              text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2142              push @{$self->{open_elements}},              push @{$self->{open_elements}},
2143                  [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2144                $self->{head_element_inserted} = 1;
2145            } else {            } else {
2146              !!!cp ('t103');              !!!cp ('t103');
2147            }            }
# Line 4703  sub _tree_construction_main ($) { Line 2174  sub _tree_construction_main ($) {
2174              !!!cp ('t103.3');              !!!cp ('t103.3');
2175              #              #
2176            }            }
2177              } elsif ($token->{tag_name} eq 'meta') {          } elsif ($token->{tag_name} eq 'meta') {
2178                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2179                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2180                  !!!cp ('t104');              !!!cp ('t104');
2181                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2182                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2183                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2184                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2185                } else {              $self->{head_element_inserted} = 1;
2186                  !!!cp ('t105');            } else {
2187                }              !!!cp ('t105');
2188                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            }
2189                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
2190              my $meta_el = pop @{$self->{open_elements}};
2191    
2192                unless ($self->{confident}) {                unless ($self->{confident}) {
2193                  if ($token->{attributes}->{charset}) {                  if ($token->{attributes}->{charset}) {
# Line 4773  sub _tree_construction_main ($) { Line 2245  sub _tree_construction_main ($) {
2245                !!!ack ('t110.1');                !!!ack ('t110.1');
2246                !!!next-token;                !!!next-token;
2247                next B;                next B;
2248              } elsif ($token->{tag_name} eq 'title') {          } elsif ($token->{tag_name} eq 'title') {
2249                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2250                  !!!cp ('t111');              !!!cp ('t111');
2251                  ## As if </noscript>              ## As if </noscript>
2252                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2253                  !!!parse-error (type => 'in noscript', text => 'title',              !!!parse-error (type => 'in noscript', text => 'title',
2254                                  token => $token);                              token => $token);
2255                            
2256                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2257                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2258                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2259                  !!!cp ('t112');              !!!cp ('t112');
2260                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2261                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2262                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2263                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2264                } else {              $self->{head_element_inserted} = 1;
2265                  !!!cp ('t113');            } else {
2266                }              !!!cp ('t113');
2267              }
2268    
2269                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2270                my $parent = defined $self->{head_element} ? $self->{head_element}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
2271                    : $self->{open_elements}->[-1]->[0];  
2272                $parse_rcdata->(RCDATA_CONTENT_MODEL);            ## NOTE: At this point the stack of open elements contain
2273                pop @{$self->{open_elements}} # <head>            ## the |head| element (index == -2) and the |script| element
2274                    if $self->{insertion_mode} == AFTER_HEAD_IM;            ## (index == -1).  In the "after head" insertion mode the
2275                next B;            ## |head| element is inserted only for the purpose of
2276              } elsif ($token->{tag_name} eq 'style' or            ## providing the context for the |script| element, and
2277                       $token->{tag_name} eq 'noframes') {            ## therefore we can now and have to remove the element from
2278                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and            ## the stack.
2279                ## insertion mode IN_HEAD_IM)            splice @{$self->{open_elements}}, -2, 1, () # <head>
2280                ## NOTE: There is a "as if in head" code clone.                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2281                if ($self->{insertion_mode} == AFTER_HEAD_IM) {            next B;
2282                  !!!cp ('t114');          } elsif ($token->{tag_name} eq 'style' or
2283                  !!!parse-error (type => 'after head',                   $token->{tag_name} eq 'noframes') {
2284                                  text => $token->{tag_name}, token => $token);            ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
2285                  push @{$self->{open_elements}},            ## insertion mode IN_HEAD_IM)
2286                      [$self->{head_element}, $el_category->{head}];            ## NOTE: There is a "as if in head" code clone.
2287                } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
2288                  !!!cp ('t115');              !!!cp ('t114');
2289                }              !!!parse-error (type => 'after head',
2290                $parse_rcdata->(CDATA_CONTENT_MODEL);                              text => $token->{tag_name}, token => $token);
2291                pop @{$self->{open_elements}} # <head>              push @{$self->{open_elements}},
2292                    if $self->{insertion_mode} == AFTER_HEAD_IM;                  [$self->{head_element}, $el_category->{head}];
2293                next B;              $self->{head_element_inserted} = 1;
2294              } elsif ($token->{tag_name} eq 'noscript') {            } else {
2295                !!!cp ('t115');
2296              }
2297              $parse_rcdata->(CDATA_CONTENT_MODEL);
2298              ## ISSUE: A spec bug [Bug 6038]
2299              splice @{$self->{open_elements}}, -2, 1, () # <head>
2300                  if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2301              next B;
2302            } elsif ($token->{tag_name} eq 'noscript') {
2303                if ($self->{insertion_mode} == IN_HEAD_IM) {                if ($self->{insertion_mode} == IN_HEAD_IM) {
2304                  !!!cp ('t116');                  !!!cp ('t116');
2305                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
# Line 4839  sub _tree_construction_main ($) { Line 2320  sub _tree_construction_main ($) {
2320                  !!!cp ('t118');                  !!!cp ('t118');
2321                  #                  #
2322                }                }
2323              } elsif ($token->{tag_name} eq 'script') {          } elsif ($token->{tag_name} eq 'script') {
2324                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2325                  !!!cp ('t119');              !!!cp ('t119');
2326                  ## As if </noscript>              ## As if </noscript>
2327                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2328                  !!!parse-error (type => 'in noscript', text => 'script',              !!!parse-error (type => 'in noscript', text => 'script',
2329                                  token => $token);                              token => $token);
2330                            
2331                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2332                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2333                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2334                  !!!cp ('t120');              !!!cp ('t120');
2335                  !!!parse-error (type => 'after head',              !!!parse-error (type => 'after head',
2336                                  text => $token->{tag_name}, token => $token);                              text => $token->{tag_name}, token => $token);
2337                  push @{$self->{open_elements}},              push @{$self->{open_elements}},
2338                      [$self->{head_element}, $el_category->{head}];                  [$self->{head_element}, $el_category->{head}];
2339                } else {              $self->{head_element_inserted} = 1;
2340                  !!!cp ('t121');            } else {
2341                }              !!!cp ('t121');
2342              }
2343    
2344                ## NOTE: There is a "as if in head" code clone.            ## NOTE: There is a "as if in head" code clone.
2345                $script_start_tag->();            $script_start_tag->();
2346                pop @{$self->{open_elements}} # <head>            ## ISSUE: A spec bug  [Bug 6038]
2347                    if $self->{insertion_mode} == AFTER_HEAD_IM;            splice @{$self->{open_elements}}, -2, 1 # <head>
2348                next B;                if ($self->{insertion_mode} & IM_MASK) == AFTER_HEAD_IM;
2349              } elsif ($token->{tag_name} eq 'body' or            next B;
2350                       $token->{tag_name} eq 'frameset') {          } elsif ($token->{tag_name} eq 'body' or
2351                     $token->{tag_name} eq 'frameset') {
2352                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2353                  !!!cp ('t122');                  !!!cp ('t122');
2354                  ## As if </noscript>                  ## As if </noscript>
# Line 5000  sub _tree_construction_main ($) { Line 2483  sub _tree_construction_main ($) {
2483              } elsif ({              } elsif ({
2484                        body => 1, html => 1,                        body => 1, html => 1,
2485                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
2486                if ($self->{insertion_mode} == BEFORE_HEAD_IM or                ## TODO: This branch is entirely redundant.
2487                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
2488                    $self->{insertion_mode} == IN_HEAD_IM or                    $self->{insertion_mode} == IN_HEAD_IM or
2489                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2490                  !!!cp ('t140');                  !!!cp ('t140');
# Line 5026  sub _tree_construction_main ($) { Line 2510  sub _tree_construction_main ($) {
2510                ## Ignore the token                ## Ignore the token
2511                !!!next-token;                !!!next-token;
2512                next B;                next B;
2513              } elsif ($token->{tag_name} eq 'br') {          } elsif ($token->{tag_name} eq 'br') {
2514                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2515                  !!!cp ('t142.2');              !!!cp ('t142.2');
2516                  ## (before head) as if <head>, (in head) as if </head>              ## (before head) as if <head>, (in head) as if </head>
2517                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
2518                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
2519                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2520        
2521                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2522                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
2523                  !!!cp ('t143.2');              !!!cp ('t143.2');
2524                  ## As if </head>              ## As if </head>
2525                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2526                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2527        
2528                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2529                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
2530                  !!!cp ('t143.3');              !!!cp ('t143.3');
2531                  ## ISSUE: Two parse errors for <head><noscript></br>              ## NOTE: Two parse errors for <head><noscript></br>
2532                  !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
2533                                  text => 'br', token => $token);                              text => 'br', token => $token);
2534                  ## As if </noscript>              ## As if </noscript>
2535                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2536                  $self->{insertion_mode} = IN_HEAD_IM;              $self->{insertion_mode} = IN_HEAD_IM;
2537    
2538                  ## Reprocess in the "in head" insertion mode...              ## Reprocess in the "in head" insertion mode...
2539                  ## As if </head>              ## As if </head>
2540                  pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
2541                  $self->{insertion_mode} = AFTER_HEAD_IM;              $self->{insertion_mode} = AFTER_HEAD_IM;
2542    
2543                  ## Reprocess in the "after head" insertion mode...              ## Reprocess in the "after head" insertion mode...
2544                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
2545                  !!!cp ('t143.4');              !!!cp ('t143.4');
2546                  #              #
2547                } else {            } else {
2548                  die "$0: $self->{insertion_mode}: Unknown insertion mode";              die "$0: $self->{insertion_mode}: Unknown insertion mode";
2549                }            }
2550    
2551                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.            #
2552                !!!parse-error (type => 'unmatched end tag',          } else { ## Other end tags
                               text => 'br', token => $token);  
               ## Ignore the token  
               !!!next-token;  
               next B;  
             } else {  
2553                !!!cp ('t145');                !!!cp ('t145');
2554                !!!parse-error (type => 'unmatched end tag',                !!!parse-error (type => 'unmatched end tag',
2555                                text => $token->{tag_name}, token => $token);                                text => $token->{tag_name}, token => $token);
# Line 5114  sub _tree_construction_main ($) { Line 2593  sub _tree_construction_main ($) {
2593              !!!insert-element ('body',, $token);              !!!insert-element ('body',, $token);
2594              $self->{insertion_mode} = IN_BODY_IM;              $self->{insertion_mode} = IN_BODY_IM;
2595              ## reprocess              ## reprocess
2596              next B;          next B;
2597        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
2598          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
2599            !!!cp ('t149.1');            !!!cp ('t149.1');
# Line 5172  sub _tree_construction_main ($) { Line 2651  sub _tree_construction_main ($) {
2651        } else {        } else {
2652          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
2653        }        }
   
           ## ISSUE: An issue in the spec.  
2654      } elsif ($self->{insertion_mode} & BODY_IMS) {      } elsif ($self->{insertion_mode} & BODY_IMS) {
2655            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
2656              !!!cp ('t150');              !!!cp ('t150');
# Line 5189  sub _tree_construction_main ($) { Line 2666  sub _tree_construction_main ($) {
2666                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
2667                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
2668                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
2669                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2670                  ## have an element in table scope                  ## have an element in table scope
2671                  for (reverse 0..$#{$self->{open_elements}}) {                  for (reverse 0..$#{$self->{open_elements}}) {
2672                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
2673                    if ($node->[1] & TABLE_CELL_EL) {                    if ($node->[1] == TABLE_CELL_EL) {
2674                      !!!cp ('t151');                      !!!cp ('t151');
2675    
2676                      ## Close the cell                      ## Close the cell
# Line 5217  sub _tree_construction_main ($) { Line 2694  sub _tree_construction_main ($) {
2694                  !!!nack ('t153.1');                  !!!nack ('t153.1');
2695                  !!!next-token;                  !!!next-token;
2696                  next B;                  next B;
2697                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2698                  !!!parse-error (type => 'not closed', text => 'caption',                  !!!parse-error (type => 'not closed', text => 'caption',
2699                                  token => $token);                                  token => $token);
2700                                    
# Line 5227  sub _tree_construction_main ($) { Line 2704  sub _tree_construction_main ($) {
2704                  INSCOPE: {                  INSCOPE: {
2705                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2706                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2707                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2708                        !!!cp ('t155');                        !!!cp ('t155');
2709                        $i = $_;                        $i = $_;
2710                        last INSCOPE;                        last INSCOPE;
# Line 5253  sub _tree_construction_main ($) { Line 2730  sub _tree_construction_main ($) {
2730                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2731                  }                  }
2732    
2733                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2734                    !!!cp ('t159');                    !!!cp ('t159');
2735                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2736                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5282  sub _tree_construction_main ($) { Line 2759  sub _tree_construction_main ($) {
2759              }              }
2760            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
2761              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
2762                if ($self->{insertion_mode} == IN_CELL_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2763                  ## have an element in table scope                  ## have an element in table scope
2764                  my $i;                  my $i;
2765                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 5332  sub _tree_construction_main ($) { Line 2809  sub _tree_construction_main ($) {
2809                                    
2810                  !!!next-token;                  !!!next-token;
2811                  next B;                  next B;
2812                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2813                  !!!cp ('t169');                  !!!cp ('t169');
2814                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2815                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5344  sub _tree_construction_main ($) { Line 2821  sub _tree_construction_main ($) {
2821                  #                  #
2822                }                }
2823              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
2824                if ($self->{insertion_mode} == IN_CAPTION_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2825                  ## have a table element in table scope                  ## have a table element in table scope
2826                  my $i;                  my $i;
2827                  INSCOPE: {                  INSCOPE: {
2828                    for (reverse 0..$#{$self->{open_elements}}) {                    for (reverse 0..$#{$self->{open_elements}}) {
2829                      my $node = $self->{open_elements}->[$_];                      my $node = $self->{open_elements}->[$_];
2830                      if ($node->[1] & CAPTION_EL) {                      if ($node->[1] == CAPTION_EL) {
2831                        !!!cp ('t171');                        !!!cp ('t171');
2832                        $i = $_;                        $i = $_;
2833                        last INSCOPE;                        last INSCOPE;
# Line 5375  sub _tree_construction_main ($) { Line 2852  sub _tree_construction_main ($) {
2852                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
2853                  }                  }
2854                                    
2855                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2856                    !!!cp ('t175');                    !!!cp ('t175');
2857                    !!!parse-error (type => 'not closed',                    !!!parse-error (type => 'not closed',
2858                                    text => $self->{open_elements}->[-1]->[0]                                    text => $self->{open_elements}->[-1]->[0]
# Line 5393  sub _tree_construction_main ($) { Line 2870  sub _tree_construction_main ($) {
2870                                    
2871                  !!!next-token;                  !!!next-token;
2872                  next B;                  next B;
2873                } elsif ($self->{insertion_mode} == IN_CELL_IM) {                } elsif (($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2874                  !!!cp ('t177');                  !!!cp ('t177');
2875                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2876                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
# Line 5408  sub _tree_construction_main ($) { Line 2885  sub _tree_construction_main ($) {
2885                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
2886                        thead => 1, tr => 1,                        thead => 1, tr => 1,
2887                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
2888                       $self->{insertion_mode} == IN_CELL_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CELL_IM) {
2889                ## have an element in table scope                ## have an element in table scope
2890                my $i;                my $i;
2891                my $tn;                my $tn;
# Line 5425  sub _tree_construction_main ($) { Line 2902  sub _tree_construction_main ($) {
2902                                line => $token->{line},                                line => $token->{line},
2903                                column => $token->{column}};                                column => $token->{column}};
2904                      next B;                      next B;
2905                    } elsif ($node->[1] & TABLE_CELL_EL) {                    } elsif ($node->[1] == TABLE_CELL_EL) {
2906                      !!!cp ('t180');                      !!!cp ('t180');
2907                      $tn = $node->[0]->manakai_local_name;                      $tn = $node->[0]->manakai_local_name;
2908                      ## NOTE: There is exactly one |td| or |th| element                      ## NOTE: There is exactly one |td| or |th| element
# Line 5445  sub _tree_construction_main ($) { Line 2922  sub _tree_construction_main ($) {
2922                  next B;                  next B;
2923                } # INSCOPE                } # INSCOPE
2924              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
2925                       $self->{insertion_mode} == IN_CAPTION_IM) {                       ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2926                !!!parse-error (type => 'not closed', text => 'caption',                !!!parse-error (type => 'not closed', text => 'caption',
2927                                token => $token);                                token => $token);
2928    
# Line 5454  sub _tree_construction_main ($) { Line 2931  sub _tree_construction_main ($) {
2931                my $i;                my $i;
2932                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
2933                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
2934                  if ($node->[1] & CAPTION_EL) {                  if ($node->[1] == CAPTION_EL) {
2935                    !!!cp ('t184');                    !!!cp ('t184');
2936                    $i = $_;                    $i = $_;
2937                    last INSCOPE;                    last INSCOPE;
# Line 5465  sub _tree_construction_main ($) { Line 2942  sub _tree_construction_main ($) {
2942                } # INSCOPE                } # INSCOPE
2943                unless (defined $i) {                unless (defined $i) {
2944                  !!!cp ('t186');                  !!!cp ('t186');
2945            ## TODO: Wrong error type?
2946                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
2947                                  text => 'caption', token => $token);                                  text => 'caption', token => $token);
2948                  ## Ignore the token                  ## Ignore the token
# Line 5478  sub _tree_construction_main ($) { Line 2956  sub _tree_construction_main ($) {
2956                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
2957                }                }
2958    
2959                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
2960                  !!!cp ('t188');                  !!!cp ('t188');
2961                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
2962                                  text => $self->{open_elements}->[-1]->[0]                                  text => $self->{open_elements}->[-1]->[0]
# Line 5510  sub _tree_construction_main ($) { Line 2988  sub _tree_construction_main ($) {
2988                  !!!cp ('t191');                  !!!cp ('t191');
2989                  #                  #
2990                }                }
2991              } elsif ({          } elsif ({
2992                        tbody => 1, tfoot => 1,                    tbody => 1, tfoot => 1,
2993                        thead => 1, tr => 1,                    thead => 1, tr => 1,
2994                       }->{$token->{tag_name}} and                   }->{$token->{tag_name}} and
2995                       $self->{insertion_mode} == IN_CAPTION_IM) {                   ($self->{insertion_mode} & IM_MASK) == IN_CAPTION_IM) {
2996                !!!cp ('t192');            !!!cp ('t192');
2997                !!!parse-error (type => 'unmatched end tag',            !!!parse-error (type => 'unmatched end tag',
2998                                text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
2999                ## Ignore the token            ## Ignore the token
3000                !!!next-token;            !!!next-token;
3001                next B;            next B;
3002              } else {          } else {
3003                !!!cp ('t193');            !!!cp ('t193');
3004                #            #
3005              }          }
3006        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3007          for my $entry (@{$self->{open_elements}}) {          for my $entry (@{$self->{open_elements}}) {
3008            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
# Line 5543  sub _tree_construction_main ($) { Line 3021  sub _tree_construction_main ($) {
3021        $insert = $insert_to_current;        $insert = $insert_to_current;
3022        #        #
3023      } elsif ($self->{insertion_mode} & TABLE_IMS) {      } elsif ($self->{insertion_mode} & TABLE_IMS) {
3024        if ($token->{type} == CHARACTER_TOKEN) {        C: {
3025          if (not $open_tables->[-1]->[1] and # tainted          my $s;
3026              $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {          if ($token->{type} == CHARACTER_TOKEN) {
3027            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            !!!cp ('t194');
3028                            $self->{pending_chars} ||= [];
3029            unless (length $token->{data}) {            push @{$self->{pending_chars}}, $token;
3030              !!!cp ('t194');            !!!next-token;
3031              !!!next-token;            next B;
3032              next B;          } else {
3033              if ($self->{pending_chars}) {
3034                $s = join '', map { $_->{data} } @{$self->{pending_chars}};
3035                delete $self->{pending_chars};
3036                if ($s =~ /[^\x09\x0A\x0C\x0D\x20]/) {
3037                  !!!cp ('t195');
3038                  #
3039                } else {
3040                  !!!cp ('t195.1');
3041                  #$self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
3042                  $self->{open_elements}->[-1]->[0]->append_child
3043                      ($self->{document}->create_text_node ($s));
3044                  last C;
3045                }
3046            } else {            } else {
3047              !!!cp ('t195');              !!!cp ('t195.2');
3048                last C;
3049            }            }
3050          }          }
3051    
3052            ## Foster parenting
3053          !!!parse-error (type => 'in table:#text', token => $token);          !!!parse-error (type => 'in table:#text', token => $token);
3054    
3055              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
3056              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
3057                            
3058              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
3059                # MUST            # MUST
3060                my $foster_parent_element;            my $foster_parent_element;
3061                my $next_sibling;            my $next_sibling;
3062                my $prev_sibling;            #my $prev_sibling;
3063                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
3064                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
3065                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3066                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
3067                      !!!cp ('t196');                  $foster_parent_element = $parent;
3068                      $foster_parent_element = $parent;                  !!!cp ('t196');
3069                      $next_sibling = $self->{open_elements}->[$_]->[0];                  $next_sibling = $self->{open_elements}->[$_]->[0];
3070                      $prev_sibling = $next_sibling->previous_sibling;            #      $prev_sibling = $next_sibling->previous_sibling;
3071                    } else {                  #
                     !!!cp ('t197');  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 !!!cp ('t198');  
                 $prev_sibling->manakai_append_text ($token->{data});  
3072                } else {                } else {
3073                  !!!cp ('t199');                  !!!cp ('t197');
3074                  $foster_parent_element->insert_before                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
3075                    ($self->{document}->create_text_node ($token->{data}),            #      $prev_sibling = $foster_parent_element->last_child;
3076                     $next_sibling);                  #
3077                }                }
3078                  last OE;
3079                }
3080              } # OE
3081              $foster_parent_element = $self->{open_elements}->[0]->[0] #and
3082              #$prev_sibling = $foster_parent_element->last_child
3083                  unless defined $foster_parent_element;
3084              #undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
3085              #if (defined $prev_sibling and
3086              #    $prev_sibling->node_type == 3) {
3087              #  !!! cp ('t198');
3088              #  $prev_sibling->manakai_append_text ($s);
3089              #} else {
3090                !!!cp ('t199');
3091                $foster_parent_element->insert_before
3092                    ($self->{document}->create_text_node ($s), $next_sibling);
3093              #}
3094            $open_tables->[-1]->[1] = 1; # tainted            $open_tables->[-1]->[1] = 1; # tainted
3095              $open_tables->[-1]->[2] = 1; # ~node inserted
3096          } else {          } else {
3097              ## NOTE: Fragment case or in a foster parent'ed element
3098              ## (e.g. |<table><span>a|).  In fragment case, whether the
3099              ## character is appended to existing node or a new node is
3100              ## created is irrelevant, since the foster parent'ed nodes
3101              ## are discarded and fragment parsing does not invoke any
3102              ## script.
3103            !!!cp ('t200');            !!!cp ('t200');
3104            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text ($s);
3105          }          }
3106                      } # C
3107          !!!next-token;  
3108          next B;        if ($token->{type} == START_TAG_TOKEN) {
       } elsif ($token->{type} == START_TAG_TOKEN) {  
3109          if ({          if ({
3110               tr => ($self->{insertion_mode} != IN_ROW_IM),               tr => (($self->{insertion_mode} & IM_MASK) != IN_ROW_IM),
3111               th => 1, td => 1,               th => 1, td => 1,
3112              }->{$token->{tag_name}}) {              }->{$token->{tag_name}}) {
3113            if ($self->{insertion_mode} == IN_TABLE_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_IM) {
3114              ## Clear back to table context              ## Clear back to table context
3115              while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3116                              & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
# Line 5625  sub _tree_construction_main ($) { Line 3123  sub _tree_construction_main ($) {
3123              ## reprocess in the "in table body" insertion mode...              ## reprocess in the "in table body" insertion mode...
3124            }            }
3125                        
3126            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {            if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3127              unless ($token->{tag_name} eq 'tr') {              unless ($token->{tag_name} eq 'tr') {
3128                !!!cp ('t202');                !!!cp ('t202');
3129                !!!parse-error (type => 'missing start tag:tr', token => $token);                !!!parse-error (type => 'missing start tag:tr', token => $token);
# Line 5639  sub _tree_construction_main ($) { Line 3137  sub _tree_construction_main ($) {
3137                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3138              }              }
3139                                    
3140                  $self->{insertion_mode} = IN_ROW_IM;              $self->{insertion_mode} = IN_ROW_IM;
3141                  if ($token->{tag_name} eq 'tr') {              if ($token->{tag_name} eq 'tr') {
3142                    !!!cp ('t204');                !!!cp ('t204');
3143                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3144                    !!!nack ('t204');                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3145                    !!!next-token;                !!!nack ('t204');
3146                    next B;                !!!next-token;
3147                  } else {                next B;
3148                    !!!cp ('t205');              } else {
3149                    !!!insert-element ('tr',, $token);                !!!cp ('t205');
3150                    ## reprocess in the "in row" insertion mode                !!!insert-element ('tr',, $token);
3151                  }                ## reprocess in the "in row" insertion mode
3152                } else {              }
3153                  !!!cp ('t206');            } else {
3154                }              !!!cp ('t206');
3155              }
3156    
3157                ## Clear back to table row context                ## Clear back to table row context
3158                while (not ($self->{open_elements}->[-1]->[1]                while (not ($self->{open_elements}->[-1]->[1]
# Line 5662  sub _tree_construction_main ($) { Line 3161  sub _tree_construction_main ($) {
3161                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3162                }                }
3163                                
3164                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3165                $self->{insertion_mode} = IN_CELL_IM;            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3166              $self->{insertion_mode} = IN_CELL_IM;
3167    
3168                push @$active_formatting_elements, ['#marker', ''];            push @$active_formatting_elements, ['#marker', ''];
3169                                
3170                !!!nack ('t207.1');            !!!nack ('t207.1');
3171              !!!next-token;
3172              next B;
3173            } elsif ({
3174                      caption => 1, col => 1, colgroup => 1,
3175                      tbody => 1, tfoot => 1, thead => 1,
3176                      tr => 1, # $self->{insertion_mode} == IN_ROW_IM
3177                     }->{$token->{tag_name}}) {
3178              if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3179                ## As if </tr>
3180                ## have an element in table scope
3181                my $i;
3182                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3183                  my $node = $self->{open_elements}->[$_];
3184                  if ($node->[1] == TABLE_ROW_EL) {
3185                    !!!cp ('t208');
3186                    $i = $_;
3187                    last INSCOPE;
3188                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
3189                    !!!cp ('t209');
3190                    last INSCOPE;
3191                  }
3192                } # INSCOPE
3193                unless (defined $i) {
3194                  !!!cp ('t210');
3195                  ## TODO: This type is wrong.
3196                  !!!parse-error (type => 'unmacthed end tag',
3197                                  text => $token->{tag_name}, token => $token);
3198                  ## Ignore the token
3199                  !!!nack ('t210.1');
3200                !!!next-token;                !!!next-token;
3201                next B;                next B;
3202              } elsif ({              }
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1,  
                       tr => 1, # $self->{insertion_mode} == IN_ROW_IM  
                      }->{$token->{tag_name}}) {  
               if ($self->{insertion_mode} == IN_ROW_IM) {  
                 ## As if </tr>  
                 ## have an element in table scope  
                 my $i;  
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                   my $node = $self->{open_elements}->[$_];  
                   if ($node->[1] & TABLE_ROW_EL) {  
                     !!!cp ('t208');  
                     $i = $_;  
                     last INSCOPE;  
                   } elsif ($node->[1] & TABLE_SCOPING_EL) {  
                     !!!cp ('t209');  
                     last INSCOPE;  
                   }  
                 } # INSCOPE  
                 unless (defined $i) {  
                   !!!cp ('t210');  
 ## TODO: This type is wrong.  
                   !!!parse-error (type => 'unmacthed end tag',  
                                   text => $token->{tag_name}, token => $token);  
                   ## Ignore the token  
                   !!!nack ('t210.1');  
                   !!!next-token;  
                   next B;  
                 }  
3203                                    
3204                  ## Clear back to table row context                  ## Clear back to table row context
3205                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
# Line 5722  sub _tree_construction_main ($) { Line 3222  sub _tree_construction_main ($) {
3222                  }                  }
3223                }                }
3224    
3225                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3226                  ## have an element in table scope                  ## have an element in table scope
3227                  my $i;                  my $i;
3228                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3229                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3230                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3231                      !!!cp ('t214');                      !!!cp ('t214');
3232                      $i = $_;                      $i = $_;
3233                      last INSCOPE;                      last INSCOPE;
# Line 5769  sub _tree_construction_main ($) { Line 3269  sub _tree_construction_main ($) {
3269                  !!!cp ('t218');                  !!!cp ('t218');
3270                }                }
3271    
3272                if ($token->{tag_name} eq 'col') {            if ($token->{tag_name} eq 'col') {
3273                  ## Clear back to table context              ## Clear back to table context
3274                  while (not ($self->{open_elements}->[-1]->[1]              while (not ($self->{open_elements}->[-1]->[1]
3275                                  & TABLE_SCOPING_EL)) {                              & TABLE_SCOPING_EL)) {
3276                    !!!cp ('t219');                !!!cp ('t219');
3277                    ## ISSUE: Can this state be reached?                ## ISSUE: Can this state be reached?
3278                    pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
3279                  }              }
3280                                
3281                  !!!insert-element ('colgroup',, $token);              !!!insert-element ('colgroup',, $token);
3282                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;              $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
3283                  ## reprocess              ## reprocess
3284                  !!!ack-later;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3285                  next B;              !!!ack-later;
3286                } elsif ({              next B;
3287                          caption => 1,            } elsif ({
3288                          colgroup => 1,                      caption => 1,
3289                          tbody => 1, tfoot => 1, thead => 1,                      colgroup => 1,
3290                         }->{$token->{tag_name}}) {                      tbody => 1, tfoot => 1, thead => 1,
3291                  ## Clear back to table context                     }->{$token->{tag_name}}) {
3292                ## Clear back to table context
3293                  while (not ($self->{open_elements}->[-1]->[1]                  while (not ($self->{open_elements}->[-1]->[1]
3294                                  & TABLE_SCOPING_EL)) {                                  & TABLE_SCOPING_EL)) {
3295                    !!!cp ('t220');                    !!!cp ('t220');
# Line 5796  sub _tree_construction_main ($) { Line 3297  sub _tree_construction_main ($) {
3297                    pop @{$self->{open_elements}};                    pop @{$self->{open_elements}};
3298                  }                  }
3299                                    
3300                  push @$active_formatting_elements, ['#marker', '']              push @$active_formatting_elements, ['#marker', '']
3301                      if $token->{tag_name} eq 'caption';                  if $token->{tag_name} eq 'caption';
3302                                    
3303                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3304                  $self->{insertion_mode} = {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3305                                             caption => IN_CAPTION_IM,              $self->{insertion_mode} = {
3306                                             colgroup => IN_COLUMN_GROUP_IM,                                         caption => IN_CAPTION_IM,
3307                                             tbody => IN_TABLE_BODY_IM,                                         colgroup => IN_COLUMN_GROUP_IM,
3308                                             tfoot => IN_TABLE_BODY_IM,                                         tbody => IN_TABLE_BODY_IM,
3309                                             thead => IN_TABLE_BODY_IM,                                         tfoot => IN_TABLE_BODY_IM,
3310                                            }->{$token->{tag_name}};                                         thead => IN_TABLE_BODY_IM,
3311                  !!!next-token;                                        }->{$token->{tag_name}};
3312                  !!!nack ('t220.1');              !!!next-token;
3313                  next B;              !!!nack ('t220.1');
3314                } else {              next B;
3315                  die "$0: in table: <>: $token->{tag_name}";            } else {
3316                }              die "$0: in table: <>: $token->{tag_name}";
3317              }
3318              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3319                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
3320                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
# Line 5824  sub _tree_construction_main ($) { Line 3326  sub _tree_construction_main ($) {
3326                my $i;                my $i;
3327                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3328                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3329                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3330                    !!!cp ('t221');                    !!!cp ('t221');
3331                    $i = $_;                    $i = $_;
3332                    last INSCOPE;                    last INSCOPE;
# Line 5851  sub _tree_construction_main ($) { Line 3353  sub _tree_construction_main ($) {
3353                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
3354                }                }
3355    
3356                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
3357                  !!!cp ('t225');                  !!!cp ('t225');
3358                  ## NOTE: |<table><tr><table>|                  ## NOTE: |<table><tr><table>|
3359                  !!!parse-error (type => 'not closed',                  !!!parse-error (type => 'not closed',
# Line 5875  sub _tree_construction_main ($) { Line 3377  sub _tree_construction_main ($) {
3377              !!!cp ('t227.8');              !!!cp ('t227.8');
3378              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3379              $parse_rcdata->(CDATA_CONTENT_MODEL);              $parse_rcdata->(CDATA_CONTENT_MODEL);
3380                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3381              next B;              next B;
3382            } else {            } else {
3383              !!!cp ('t227.7');              !!!cp ('t227.7');
# Line 5885  sub _tree_construction_main ($) { Line 3388  sub _tree_construction_main ($) {
3388              !!!cp ('t227.6');              !!!cp ('t227.6');
3389              ## NOTE: This is a "as if in head" code clone.              ## NOTE: This is a "as if in head" code clone.
3390              $script_start_tag->();              $script_start_tag->();
3391                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3392              next B;              next B;
3393            } else {            } else {
3394              !!!cp ('t227.5');              !!!cp ('t227.5');
# Line 5900  sub _tree_construction_main ($) { Line 3404  sub _tree_construction_main ($) {
3404                                  text => $token->{tag_name}, token => $token);                                  text => $token->{tag_name}, token => $token);
3405    
3406                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
3407                    $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
3408    
3409                  ## TODO: form element pointer                  ## TODO: form element pointer
3410    
# Line 5931  sub _tree_construction_main ($) { Line 3436  sub _tree_construction_main ($) {
3436          $insert = $insert_to_foster;          $insert = $insert_to_foster;
3437          #          #
3438        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3439              if ($token->{tag_name} eq 'tr' and          if ($token->{tag_name} eq 'tr' and
3440                  $self->{insertion_mode} == IN_ROW_IM) {              ($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3441                ## have an element in table scope            ## have an element in table scope
3442                my $i;                my $i;
3443                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3444                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3445                  if ($node->[1] & TABLE_ROW_EL) {                  if ($node->[1] == TABLE_ROW_EL) {
3446                    !!!cp ('t228');                    !!!cp ('t228');
3447                    $i = $_;                    $i = $_;
3448                    last INSCOPE;                    last INSCOPE;
# Line 5972  sub _tree_construction_main ($) { Line 3477  sub _tree_construction_main ($) {
3477                !!!nack ('t231.1');                !!!nack ('t231.1');
3478                next B;                next B;
3479              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
3480                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3481                  ## As if </tr>                  ## As if </tr>
3482                  ## have an element in table scope                  ## have an element in table scope
3483                  my $i;                  my $i;
3484                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3485                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3486                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3487                      !!!cp ('t233');                      !!!cp ('t233');
3488                      $i = $_;                      $i = $_;
3489                      last INSCOPE;                      last INSCOPE;
# Line 6011  sub _tree_construction_main ($) { Line 3516  sub _tree_construction_main ($) {
3516                  ## reprocess in the "in table body" insertion mode...                  ## reprocess in the "in table body" insertion mode...
3517                }                }
3518    
3519                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_TABLE_BODY_IM) {
3520                  ## have an element in table scope                  ## have an element in table scope
3521                  my $i;                  my $i;
3522                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3523                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3524                    if ($node->[1] & TABLE_ROW_GROUP_EL) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
3525                      !!!cp ('t237');                      !!!cp ('t237');
3526                      $i = $_;                      $i = $_;
3527                      last INSCOPE;                      last INSCOPE;
# Line 6063  sub _tree_construction_main ($) { Line 3568  sub _tree_construction_main ($) {
3568                my $i;                my $i;
3569                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3570                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
3571                  if ($node->[1] & TABLE_EL) {                  if ($node->[1] == TABLE_EL) {
3572                    !!!cp ('t241');                    !!!cp ('t241');
3573                    $i = $_;                    $i = $_;
3574                    last INSCOPE;                    last INSCOPE;
# Line 6093  sub _tree_construction_main ($) { Line 3598  sub _tree_construction_main ($) {
3598                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
3599                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
3600                       $self->{insertion_mode} & ROW_IMS) {                       $self->{insertion_mode} & ROW_IMS) {
3601                if ($self->{insertion_mode} == IN_ROW_IM) {                if (($self->{insertion_mode} & IM_MASK) == IN_ROW_IM) {
3602                  ## have an element in table scope                  ## have an element in table scope
3603                  my $i;                  my $i;
3604                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
# Line 6122  sub _tree_construction_main ($) { Line 3627  sub _tree_construction_main ($) {
3627                  my $i;                  my $i;
3628                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3629                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
3630                    if ($node->[1] & TABLE_ROW_EL) {                    if ($node->[1] == TABLE_ROW_EL) {
3631                      !!!cp ('t250');                      !!!cp ('t250');
3632                      $i = $_;                      $i = $_;
3633                      last INSCOPE;                      last INSCOPE;
# Line 6212  sub _tree_construction_main ($) { Line 3717  sub _tree_construction_main ($) {
3717            #            #
3718          }          }
3719        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3720          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
3721                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
3722            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
3723            !!!cp ('t259.1');            !!!cp ('t259.1');
# Line 6227  sub _tree_construction_main ($) { Line 3732  sub _tree_construction_main ($) {
3732        } else {        } else {
3733          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
3734        }        }
3735      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {      } elsif (($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
3736            if ($token->{type} == CHARACTER_TOKEN) {            if ($token->{type} == CHARACTER_TOKEN) {
3737              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3738                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
# Line 6254  sub _tree_construction_main ($) { Line 3759  sub _tree_construction_main ($) {
3759              }              }
3760            } elsif ($token->{type} == END_TAG_TOKEN) {            } elsif ($token->{type} == END_TAG_TOKEN) {
3761              if ($token->{tag_name} eq 'colgroup') {              if ($token->{tag_name} eq 'colgroup') {
3762                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3763                  !!!cp ('t264');                  !!!cp ('t264');
3764                  !!!parse-error (type => 'unmatched end tag',                  !!!parse-error (type => 'unmatched end tag',
3765                                  text => 'colgroup', token => $token);                                  text => 'colgroup', token => $token);
# Line 6280  sub _tree_construction_main ($) { Line 3785  sub _tree_construction_main ($) {
3785                #                #
3786              }              }
3787        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
3788          if ($self->{open_elements}->[-1]->[1] & HTML_EL and          if ($self->{open_elements}->[-1]->[1] == HTML_EL and
3789              @{$self->{open_elements}} == 1) { # redundant, maybe              @{$self->{open_elements}} == 1) { # redundant, maybe
3790            !!!cp ('t270.2');            !!!cp ('t270.2');
3791            ## Stop parsing.            ## Stop parsing.
# Line 6298  sub _tree_construction_main ($) { Line 3803  sub _tree_construction_main ($) {
3803        }        }
3804    
3805            ## As if </colgroup>            ## As if </colgroup>
3806            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {            if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
3807              !!!cp ('t269');              !!!cp ('t269');
3808  ## TODO: Wrong error type?  ## TODO: Wrong error type?
3809              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6323  sub _tree_construction_main ($) { Line 3828  sub _tree_construction_main ($) {
3828          next B;          next B;
3829        } elsif ($token->{type} == START_TAG_TOKEN) {        } elsif ($token->{type} == START_TAG_TOKEN) {
3830          if ($token->{tag_name} eq 'option') {          if ($token->{tag_name} eq 'option') {
3831            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3832              !!!cp ('t272');              !!!cp ('t272');
3833              ## As if </option>              ## As if </option>
3834              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6336  sub _tree_construction_main ($) { Line 3841  sub _tree_construction_main ($) {
3841            !!!next-token;            !!!next-token;
3842            next B;            next B;
3843          } elsif ($token->{tag_name} eq 'optgroup') {          } elsif ($token->{tag_name} eq 'optgroup') {
3844            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3845              !!!cp ('t274');              !!!cp ('t274');
3846              ## As if </option>              ## As if </option>
3847              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6344  sub _tree_construction_main ($) { Line 3849  sub _tree_construction_main ($) {
3849              !!!cp ('t275');              !!!cp ('t275');
3850            }            }
3851    
3852            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3853              !!!cp ('t276');              !!!cp ('t276');
3854              ## As if </optgroup>              ## As if </optgroup>
3855              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
# Line 6357  sub _tree_construction_main ($) { Line 3862  sub _tree_construction_main ($) {
3862            !!!next-token;            !!!next-token;
3863            next B;            next B;
3864          } elsif ({          } elsif ({
3865                     select => 1, input => 1, textarea => 1,                     select => 1, input => 1, textarea => 1, keygen => 1,
3866                   }->{$token->{tag_name}} or                   }->{$token->{tag_name}} or
3867                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and                   (($self->{insertion_mode} & IM_MASK)
3868                          == IN_SELECT_IN_TABLE_IM and
3869                    {                    {
3870                     caption => 1, table => 1,                     caption => 1, table => 1,
3871                     tbody => 1, tfoot => 1, thead => 1,                     tbody => 1, tfoot => 1, thead => 1,
3872                     tr => 1, td => 1, th => 1,                     tr => 1, td => 1, th => 1,
3873                    }->{$token->{tag_name}})) {                    }->{$token->{tag_name}})) {
3874            ## TODO: The type below is not good - <select> is replaced by </select>  
3875            !!!parse-error (type => 'not closed', text => 'select',            ## 1. Parse error.
3876                            token => $token);            if ($token->{tag_name} eq 'select') {
3877            ## NOTE: As if the token were </select> (<select> case) or                !!!parse-error (type => 'select in select', ## XXX: documentation
3878            ## as if there were </select> (otherwise).                                token => $token);
3879            ## have an element in table scope            } else {
3880                !!!parse-error (type => 'not closed', text => 'select',
3881                                token => $token);
3882              }
3883    
3884              ## 2./<select>-1. Unless "have an element in table scope" (select):
3885            my $i;            my $i;
3886            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3887              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3888              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3889                !!!cp ('t278');                !!!cp ('t278');
3890                $i = $_;                $i = $_;
3891                last INSCOPE;                last INSCOPE;
# Line 6385  sub _tree_construction_main ($) { Line 3896  sub _tree_construction_main ($) {
3896            } # INSCOPE            } # INSCOPE
3897            unless (defined $i) {            unless (defined $i) {
3898              !!!cp ('t280');              !!!cp ('t280');
3899              !!!parse-error (type => 'unmatched end tag',              if ($token->{tag_name} eq 'select') {
3900                              text => 'select', token => $token);                ## NOTE: This error would be raised when
3901              ## Ignore the token                ## |select.innerHTML = '<select>'| is executed; in this
3902                  ## case two errors, "select in select" and "unmatched
3903                  ## end tags" are reported to the user, the latter might
3904                  ## be confusing but this is what the spec requires.
3905                  !!!parse-error (type => 'unmatched end tag',
3906                                  text => 'select',
3907                                  token => $token);
3908                }
3909                ## Ignore the token.
3910              !!!nack ('t280.1');              !!!nack ('t280.1');
3911              !!!next-token;              !!!next-token;
3912              next B;              next B;
3913            }            }
3914    
3915              ## 3. Otherwise, as if there were <select>:
3916                                
3917            !!!cp ('t281');            !!!cp ('t281');
3918            splice @{$self->{open_elements}}, $i;            splice @{$self->{open_elements}}, $i;
# Line 6408  sub _tree_construction_main ($) { Line 3929  sub _tree_construction_main ($) {
3929              ## Reprocess the token.              ## Reprocess the token.
3930              next B;              next B;
3931            }            }
3932            } elsif ($token->{tag_name} eq 'script') {
3933              !!!cp ('t281.3');
3934              ## NOTE: This is an "as if in head" code clone
3935              $script_start_tag->();
3936              next B;
3937          } else {          } else {
3938            !!!cp ('t282');            !!!cp ('t282');
3939            !!!parse-error (type => 'in select',            !!!parse-error (type => 'in select',
# Line 6419  sub _tree_construction_main ($) { Line 3945  sub _tree_construction_main ($) {
3945          }          }
3946        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
3947          if ($token->{tag_name} eq 'optgroup') {          if ($token->{tag_name} eq 'optgroup') {
3948            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
3949                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
3950              !!!cp ('t283');              !!!cp ('t283');
3951              ## As if </option>              ## As if </option>
3952              splice @{$self->{open_elements}}, -2;              splice @{$self->{open_elements}}, -2;
3953            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
3954              !!!cp ('t284');              !!!cp ('t284');
3955              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3956            } else {            } else {
# Line 6437  sub _tree_construction_main ($) { Line 3963  sub _tree_construction_main ($) {
3963            !!!next-token;            !!!next-token;
3964            next B;            next B;
3965          } elsif ($token->{tag_name} eq 'option') {          } elsif ($token->{tag_name} eq 'option') {
3966            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
3967              !!!cp ('t286');              !!!cp ('t286');
3968              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
3969            } else {            } else {
# Line 6454  sub _tree_construction_main ($) { Line 3980  sub _tree_construction_main ($) {
3980            my $i;            my $i;
3981            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
3982              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
3983              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
3984                !!!cp ('t288');                !!!cp ('t288');
3985                $i = $_;                $i = $_;
3986                last INSCOPE;                last INSCOPE;
# Line 6481  sub _tree_construction_main ($) { Line 4007  sub _tree_construction_main ($) {
4007            !!!nack ('t291.1');            !!!nack ('t291.1');
4008            !!!next-token;            !!!next-token;
4009            next B;            next B;
4010          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and          } elsif (($self->{insertion_mode} & IM_MASK)
4011                         == IN_SELECT_IN_TABLE_IM and
4012                   {                   {
4013                    caption => 1, table => 1, tbody => 1,                    caption => 1, table => 1, tbody => 1,
4014                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
# Line 6516  sub _tree_construction_main ($) { Line 4043  sub _tree_construction_main ($) {
4043            undef $i;            undef $i;
4044            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4045              my $node = $self->{open_elements}->[$_];              my $node = $self->{open_elements}->[$_];
4046              if ($node->[1] & SELECT_EL) {              if ($node->[1] == SELECT_EL) {
4047                !!!cp ('t295');                !!!cp ('t295');
4048                $i = $_;                $i = $_;
4049                last INSCOPE;                last INSCOPE;
# Line 6555  sub _tree_construction_main ($) { Line 4082  sub _tree_construction_main ($) {
4082            next B;            next B;
4083          }          }
4084        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4085          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4086                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4087            !!!cp ('t299.1');            !!!cp ('t299.1');
4088            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6742  sub _tree_construction_main ($) { Line 4269  sub _tree_construction_main ($) {
4269        } elsif ($token->{type} == END_TAG_TOKEN) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4270          if ($token->{tag_name} eq 'frameset' and          if ($token->{tag_name} eq 'frameset' and
4271              $self->{insertion_mode} == IN_FRAMESET_IM) {              $self->{insertion_mode} == IN_FRAMESET_IM) {
4272            if ($self->{open_elements}->[-1]->[1] & HTML_EL and            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
4273                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
4274              !!!cp ('t325');              !!!cp ('t325');
4275              !!!parse-error (type => 'unmatched end tag',              !!!parse-error (type => 'unmatched end tag',
# Line 6756  sub _tree_construction_main ($) { Line 4283  sub _tree_construction_main ($) {
4283            }            }
4284    
4285            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
4286                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
4287              !!!cp ('t327');              !!!cp ('t327');
4288              $self->{insertion_mode} = AFTER_FRAMESET_IM;              $self->{insertion_mode} = AFTER_FRAMESET_IM;
4289            } else {            } else {
# Line 6788  sub _tree_construction_main ($) { Line 4315  sub _tree_construction_main ($) {
4315            next B;            next B;
4316          }          }
4317        } elsif ($token->{type} == END_OF_FILE_TOKEN) {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4318          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
4319                  @{$self->{open_elements}} == 1) { # redundant, maybe                  @{$self->{open_elements}} == 1) { # redundant, maybe
4320            !!!cp ('t331.1');            !!!cp ('t331.1');
4321            !!!parse-error (type => 'in body:#eof', token => $token);            !!!parse-error (type => 'in body:#eof', token => $token);
# Line 6801  sub _tree_construction_main ($) { Line 4328  sub _tree_construction_main ($) {
4328        } else {        } else {
4329          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
4330        }        }
   
       ## ISSUE: An issue in spec here  
4331      } else {      } else {
4332        die "$0: $self->{insertion_mode}: Unknown insertion mode";        die "$0: $self->{insertion_mode}: Unknown insertion mode";
4333      }      }
# Line 6893  sub _tree_construction_main ($) { Line 4418  sub _tree_construction_main ($) {
4418          !!!parse-error (type => 'in body', text => 'body', token => $token);          !!!parse-error (type => 'in body', text => 'body', token => $token);
4419                                
4420          if (@{$self->{open_elements}} == 1 or          if (@{$self->{open_elements}} == 1 or
4421              not ($self->{open_elements}->[1]->[1] & BODY_EL)) {              not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
4422            !!!cp ('t342');            !!!cp ('t342');
4423            ## Ignore the token            ## Ignore the token
4424          } else {          } else {
# Line 6928  sub _tree_construction_main ($) { Line 4453  sub _tree_construction_main ($) {
4453                  table => 1,                  table => 1,
4454                  hr => 1,                  hr => 1,
4455                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
4456    
4457            ## 1. When there is an opening |form| element:
4458          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {          if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
4459            !!!cp ('t350');            !!!cp ('t350');
4460            !!!parse-error (type => 'in form:form', token => $token);            !!!parse-error (type => 'in form:form', token => $token);
# Line 6937  sub _tree_construction_main ($) { Line 4464  sub _tree_construction_main ($) {
4464            next B;            next B;
4465          }          }
4466    
4467          ## has a p element in scope          ## 2. Close the |p| element, if any.
4468          INSCOPE: for (reverse @{$self->{open_elements}}) {          if ($token->{tag_name} ne 'table' or # The Hixie Quirk
4469            if ($_->[1] & P_EL) {              $self->{document}->manakai_compat_mode ne 'quirks') {
4470              !!!cp ('t344');            ## has a p element in scope
4471              !!!back-token; # <form>            INSCOPE: for (reverse @{$self->{open_elements}}) {
4472              $token = {type => END_TAG_TOKEN, tag_name => 'p',              if ($_->[1] == P_EL) {
4473                        line => $token->{line}, column => $token->{column}};                !!!cp ('t344');
4474              next B;                !!!back-token; # <form>
4475            } elsif ($_->[1] & SCOPING_EL) {                $token = {type => END_TAG_TOKEN, tag_name => 'p',
4476              !!!cp ('t345');                          line => $token->{line}, column => $token->{column}};
4477              last INSCOPE;                next B;
4478                } elsif ($_->[1] & SCOPING_EL) {
4479                  !!!cp ('t345');
4480                  last INSCOPE;
4481                }
4482              } # INSCOPE
4483            }
4484    
4485            ## 3. Close the opening <hn> element, if any.
4486            if ({h1 => 1, h2 => 1, h3 => 1,
4487                 h4 => 1, h5 => 1, h6 => 1}->{$token->{tag_name}}) {
4488              if ($self->{open_elements}->[-1]->[1] == HEADING_EL) {
4489                !!!parse-error (type => 'not closed',
4490                                text => $self->{open_elements}->[-1]->[0]->manakai_local_name,
4491                                token => $token);
4492                pop @{$self->{open_elements}};
4493            }            }
4494          } # INSCOPE          }
4495              
4496            ## 4. Insertion.
4497          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4498          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {          if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
4499            !!!nack ('t346.1');            !!!nack ('t346.1');
# Line 6994  sub _tree_construction_main ($) { Line 4537  sub _tree_construction_main ($) {
4537        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{tag_name} eq 'li') {
4538          ## NOTE: As normal, but imply </li> when there's another <li> ...          ## NOTE: As normal, but imply </li> when there's another <li> ...
4539    
4540          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)::
4541            ## Interpreted as <li><foo/></li><li/> (non-conforming)            ## Interpreted as <li><foo/></li><li/> (non-conforming):
4542            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
4543            ## 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),
4544            ## 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),
4545            ## object (Fx)            ## object (Fx)
4546            ## Generate non-tree (non-conforming)            ## Generate non-tree (non-conforming):
4547            ## basefont (IE7 (where basefont is non-void)), center (IE),            ## basefont (IE7 (where basefont is non-void)), center (IE),
4548            ## form (IE), hn (IE)            ## form (IE), hn (IE)
4549          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)::
4550            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)            ## Interpreted as <li><foo><li/></foo></li> (non-conforming):
4551            ## div (Fx, S)            ## div (Fx, S)
4552    
4553          my $non_optional;          my $non_optional;
# Line 7012  sub _tree_construction_main ($) { Line 4555  sub _tree_construction_main ($) {
4555    
4556          ## 1.          ## 1.
4557          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4558            if ($node->[1] & LI_EL) {            if ($node->[1] == LI_EL) {
4559              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4560              {              {
4561                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7043  sub _tree_construction_main ($) { Line 4586  sub _tree_construction_main ($) {
4586                     ($node->[1] & SPECIAL_EL or                     ($node->[1] & SPECIAL_EL or
4587                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4588                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4589                       (not $node->[1] & ADDRESS_DIV_P_EL)
4590                     (not $node->[1] & ADDRESS_EL) &                    ) {
                    (not $node->[1] & DIV_EL) &  
                    (not $node->[1] & P_EL)) {  
4591              ## 3.              ## 3.
4592              !!!cp ('t357');              !!!cp ('t357');
4593              last; ## goto 5.              last; ## goto 5.
# Line 7065  sub _tree_construction_main ($) { Line 4606  sub _tree_construction_main ($) {
4606    
4607          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4608          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4609            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4610              !!!cp ('t353');              !!!cp ('t353');
4611    
4612                ## NOTE: |<p><li>|, for example.
4613    
4614              !!!back-token; # <x>              !!!back-token; # <x>
4615              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
4616                        line => $token->{line}, column => $token->{column}};                        line => $token->{line}, column => $token->{column}};
# Line 7091  sub _tree_construction_main ($) { Line 4635  sub _tree_construction_main ($) {
4635    
4636          ## 1.          ## 1.
4637          for my $node (reverse @{$self->{open_elements}}) {          for my $node (reverse @{$self->{open_elements}}) {
4638            if ($node->[1] & DT_EL or $node->[1] & DD_EL) {            if ($node->[1] == DTDD_EL) {
4639              ## 2. (a) As if </li>              ## 2. (a) As if </li>
4640              {              {
4641                ## If no </li> - not applied                ## If no </li> - not applied
# Line 7123  sub _tree_construction_main ($) { Line 4667  sub _tree_construction_main ($) {
4667                      $node->[1] & SCOPING_EL) and                      $node->[1] & SCOPING_EL) and
4668                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.                     ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
4669    
4670                     (not $node->[1] & ADDRESS_EL) &                     (not $node->[1] & ADDRESS_DIV_P_EL)
4671                     (not $node->[1] & DIV_EL) &                    ) {
                    (not $node->[1] & P_EL)) {  
4672              ## 3.              ## 3.
4673              !!!cp ('t357.1');              !!!cp ('t357.1');
4674              last; ## goto 5.              last; ## goto 5.
# Line 7144  sub _tree_construction_main ($) { Line 4687  sub _tree_construction_main ($) {
4687    
4688          ## 5. (a) has a |p| element in scope          ## 5. (a) has a |p| element in scope
4689          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4690            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4691              !!!cp ('t353.1');              !!!cp ('t353.1');
4692              !!!back-token; # <x>              !!!back-token; # <x>
4693              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7166  sub _tree_construction_main ($) { Line 4709  sub _tree_construction_main ($) {
4709    
4710          ## has a p element in scope          ## has a p element in scope
4711          INSCOPE: for (reverse @{$self->{open_elements}}) {          INSCOPE: for (reverse @{$self->{open_elements}}) {
4712            if ($_->[1] & P_EL) {            if ($_->[1] == P_EL) {
4713              !!!cp ('t367');              !!!cp ('t367');
4714              !!!back-token; # <plaintext>              !!!back-token; # <plaintext>
4715              $token = {type => END_TAG_TOKEN, tag_name => 'p',              $token = {type => END_TAG_TOKEN, tag_name => 'p',
# Line 7188  sub _tree_construction_main ($) { Line 4731  sub _tree_construction_main ($) {
4731        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{tag_name} eq 'a') {
4732          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          AFE: for my $i (reverse 0..$#$active_formatting_elements) {
4733            my $node = $active_formatting_elements->[$i];            my $node = $active_formatting_elements->[$i];
4734            if ($node->[1] & A_EL) {            if ($node->[1] == A_EL) {
4735              !!!cp ('t371');              !!!cp ('t371');
4736              !!!parse-error (type => 'in a:a', token => $token);              !!!parse-error (type => 'in a:a', token => $token);
4737                            
# Line 7232  sub _tree_construction_main ($) { Line 4775  sub _tree_construction_main ($) {
4775          ## has a |nobr| element in scope          ## has a |nobr| element in scope
4776          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4777            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4778            if ($node->[1] & NOBR_EL) {            if ($node->[1] == NOBR_EL) {
4779              !!!cp ('t376');              !!!cp ('t376');
4780              !!!parse-error (type => 'in nobr:nobr', token => $token);              !!!parse-error (type => 'in nobr:nobr', token => $token);
4781              !!!back-token; # <nobr>              !!!back-token; # <nobr>
# Line 7255  sub _tree_construction_main ($) { Line 4798  sub _tree_construction_main ($) {
4798          ## has a button element in scope          ## has a button element in scope
4799          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4800            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4801            if ($node->[1] & BUTTON_EL) {            if ($node->[1] == BUTTON_EL) {
4802              !!!cp ('t378');              !!!cp ('t378');
4803              !!!parse-error (type => 'in button:button', token => $token);              !!!parse-error (type => 'in button:button', token => $token);
4804              !!!back-token; # <button>              !!!back-token; # <button>
# Line 7320  sub _tree_construction_main ($) { Line 4863  sub _tree_construction_main ($) {
4863                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4864                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4865                           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}},  
4866                          {type => START_TAG_TOKEN, tag_name => 'label',                          {type => START_TAG_TOKEN, tag_name => 'label',
4867                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4868                         );                         );
# Line 7344  sub _tree_construction_main ($) { Line 4885  sub _tree_construction_main ($) {
4885                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD                          #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
4886                          {type => END_TAG_TOKEN, tag_name => 'label',                          {type => END_TAG_TOKEN, tag_name => 'label',
4887                           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}},  
4888                          {type => START_TAG_TOKEN, tag_name => 'hr',                          {type => START_TAG_TOKEN, tag_name => 'hr',
4889                           line => $token->{line}, column => $token->{column}},                           line => $token->{line}, column => $token->{column}},
4890                          {type => END_TAG_TOKEN, tag_name => 'form',                          {type => END_TAG_TOKEN, tag_name => 'form',
# Line 7355  sub _tree_construction_main ($) { Line 4894  sub _tree_construction_main ($) {
4894            next B;            next B;
4895          }          }
4896        } elsif ($token->{tag_name} eq 'textarea') {        } elsif ($token->{tag_name} eq 'textarea') {
4897          my $tag_name = $token->{tag_name};          ## 1. Insert
4898          my $el;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
         !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);  
4899                    
4900            ## Step 2 # XXX
4901          ## TODO: $self->{form_element} if defined          ## TODO: $self->{form_element} if defined
4902    
4903            ## 2. Drop U+000A LINE FEED
4904            $self->{ignore_newline} = 1;
4905    
4906            ## 3. RCDATA
4907          $self->{content_model} = RCDATA_CONTENT_MODEL;          $self->{content_model} = RCDATA_CONTENT_MODEL;
4908          delete $self->{escape}; # MUST          delete $self->{escape}; # MUST
4909            
4910          $insert->($el);          ## 4., 6. Insertion mode
4911                    $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
4912          my $text = '';  
4913            ## XXX: 5. frameset-ok flag
4914    
4915          !!!nack ('t392.1');          !!!nack ('t392.1');
4916          !!!next-token;          !!!next-token;
4917          if ($token->{type} == CHARACTER_TOKEN) {          next B;
4918            $token->{data} =~ s/^\x0A//;        } elsif ($token->{tag_name} eq 'optgroup' or
4919            unless (length $token->{data}) {                 $token->{tag_name} eq 'option') {
4920              !!!cp ('t392');          ## has an |option| element in scope
4921              !!!next-token;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4922            } else {            my $node = $self->{open_elements}->[$_];
4923              !!!cp ('t393');            if ($node->[1] == OPTION_EL) {
4924                !!!cp ('t397.1');
4925                ## NOTE: As if </option>
4926                !!!back-token; # <option> or <optgroup>
4927                $token = {type => END_TAG_TOKEN, tag_name => 'option',
4928                          line => $token->{line}, column => $token->{column}};
4929                next B;
4930              } elsif ($node->[1] & SCOPING_EL) {
4931                !!!cp ('t397.2');
4932                last INSCOPE;
4933            }            }
4934          } else {          } # INSCOPE
4935            !!!cp ('t394');  
4936          }          $reconstruct_active_formatting_elements->($insert_to_current);
4937          while ($token->{type} == CHARACTER_TOKEN) {  
4938            !!!cp ('t395');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4939            $text .= $token->{data};  
4940            !!!next-token;          !!!nack ('t397.3');
         }  
         if (length $text) {  
           !!!cp ('t396');  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} == END_TAG_TOKEN and  
             $token->{tag_name} eq $tag_name) {  
           !!!cp ('t397');  
           ## Ignore the token  
         } else {  
           !!!cp ('t398');  
           !!!parse-error (type => 'in RCDATA:#eof', token => $token);  
         }  
4941          !!!next-token;          !!!next-token;
4942          next B;          redo B;
4943        } elsif ($token->{tag_name} eq 'rt' or        } elsif ($token->{tag_name} eq 'rt' or
4944                 $token->{tag_name} eq 'rp') {                 $token->{tag_name} eq 'rp') {
4945          ## has a |ruby| element in scope          ## has a |ruby| element in scope
4946          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4947            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
4948            if ($node->[1] & RUBY_EL) {            if ($node->[1] == RUBY_EL) {
4949              !!!cp ('t398.1');              !!!cp ('t398.1');
4950              ## generate implied end tags              ## generate implied end tags
4951              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
4952                !!!cp ('t398.2');                !!!cp ('t398.2');
4953                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4954              }              }
4955              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
4956                !!!cp ('t398.3');                !!!cp ('t398.3');
4957                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
4958                                text => $self->{open_elements}->[-1]->[0]                                text => $self->{open_elements}->[-1]->[0]
4959                                    ->manakai_local_name,                                    ->manakai_local_name,
4960                                token => $token);                                token => $token);
4961                pop @{$self->{open_elements}}                pop @{$self->{open_elements}}
4962                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
4963              }              }
4964              last INSCOPE;              last INSCOPE;
4965            } elsif ($node->[1] & SCOPING_EL) {            } elsif ($node->[1] & SCOPING_EL) {
# Line 7428  sub _tree_construction_main ($) { Line 4967  sub _tree_construction_main ($) {
4967              last INSCOPE;              last INSCOPE;
4968            }            }
4969          } # INSCOPE          } # INSCOPE
4970              
4971            ## TODO: <non-ruby><rt> is not allowed.
4972    
4973          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
4974    
# Line 7448  sub _tree_construction_main ($) { Line 4989  sub _tree_construction_main ($) {
4989                    
4990          if ($self->{self_closing}) {          if ($self->{self_closing}) {
4991            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4992            !!!ack ('t398.1');            !!!ack ('t398.6');
4993          } else {          } else {
4994            !!!cp ('t398.2');            !!!cp ('t398.7');
4995            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;            $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
4996            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion            ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
4997            ## mode, "in body" (not "in foreign content") secondary insertion            ## mode, "in body" (not "in foreign content") secondary insertion
# Line 7461  sub _tree_construction_main ($) { Line 5002  sub _tree_construction_main ($) {
5002          next B;          next B;
5003        } elsif ({        } elsif ({
5004                  caption => 1, col => 1, colgroup => 1, frame => 1,                  caption => 1, col => 1, colgroup => 1, frame => 1,
5005                  frameset => 1, head => 1, option => 1, optgroup => 1,                  frameset => 1, head => 1,
5006                  tbody => 1, td => 1, tfoot => 1, th => 1,                  tbody => 1, td => 1, tfoot => 1, th => 1,
5007                  thead => 1, tr => 1,                  thead => 1, tr => 1,
5008                 }->{$token->{tag_name}}) {                 }->{$token->{tag_name}}) {
# Line 7472  sub _tree_construction_main ($) { Line 5013  sub _tree_construction_main ($) {
5013          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.          !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
5014          !!!next-token;          !!!next-token;
5015          next B;          next B;
5016                  } elsif ($token->{tag_name} eq 'param' or
5017          ## ISSUE: An issue on HTML5 new elements in the spec.                 $token->{tag_name} eq 'source') {
5018            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
5019            pop @{$self->{open_elements}};
5020    
5021            !!!ack ('t398.5');
5022            !!!next-token;
5023            redo B;
5024        } else {        } else {
5025          if ($token->{tag_name} eq 'image') {          if ($token->{tag_name} eq 'image') {
5026            !!!cp ('t384');            !!!cp ('t384');
# Line 7509  sub _tree_construction_main ($) { Line 5056  sub _tree_construction_main ($) {
5056            !!!ack ('t388.2');            !!!ack ('t388.2');
5057          } elsif ({          } elsif ({
5058                    area => 1, basefont => 1, bgsound => 1, br => 1,                    area => 1, basefont => 1, bgsound => 1, br => 1,
5059                    embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,                    embed => 1, img => 1, spacer => 1, wbr => 1,
                   #image => 1,  
5060                   }->{$token->{tag_name}}) {                   }->{$token->{tag_name}}) {
5061            !!!cp ('t388.1');            !!!cp ('t388.1');
5062            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
# Line 7520  sub _tree_construction_main ($) { Line 5066  sub _tree_construction_main ($) {
5066                    
5067            if ($self->{insertion_mode} & TABLE_IMS or            if ($self->{insertion_mode} & TABLE_IMS or
5068                $self->{insertion_mode} & BODY_TABLE_IMS or                $self->{insertion_mode} & BODY_TABLE_IMS or
5069                $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {                ($self->{insertion_mode} & IM_MASK) == IN_COLUMN_GROUP_IM) {
5070              !!!cp ('t400.1');              !!!cp ('t400.1');
5071              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;              $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
5072            } else {            } else {
# Line 7537  sub _tree_construction_main ($) { Line 5083  sub _tree_construction_main ($) {
5083        }        }
5084      } elsif ($token->{type} == END_TAG_TOKEN) {      } elsif ($token->{type} == END_TAG_TOKEN) {
5085        if ($token->{tag_name} eq 'body') {        if ($token->{tag_name} eq 'body') {
5086          ## has a |body| element in scope  
5087            ## 1. If not "have an element in scope":
5088            ## "has a |body| element in scope"
5089          my $i;          my $i;
5090          INSCOPE: {          INSCOPE: {
5091            for (reverse @{$self->{open_elements}}) {            for (reverse @{$self->{open_elements}}) {
5092              if ($_->[1] & BODY_EL) {              if ($_->[1] == BODY_EL) {
5093                !!!cp ('t405');                !!!cp ('t405');
5094                $i = $_;                $i = $_;
5095                last INSCOPE;                last INSCOPE;
# Line 7551  sub _tree_construction_main ($) { Line 5099  sub _tree_construction_main ($) {
5099              }              }
5100            }            }
5101    
5102            !!!parse-error (type => 'start tag not allowed',            ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
5103    
5104              !!!parse-error (type => 'unmatched end tag',
5105                            text => $token->{tag_name}, token => $token);                            text => $token->{tag_name}, token => $token);
5106            ## NOTE: Ignore the token.            ## NOTE: Ignore the token.
5107            !!!next-token;            !!!next-token;
5108            next B;            next B;
5109          } # INSCOPE          } # INSCOPE
5110    
5111            ## 2. If unclosed elements:
5112          for (@{$self->{open_elements}}) {          for (@{$self->{open_elements}}) {
5113            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {            unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL ||
5114                      $_->[1] == OPTGROUP_EL ||
5115                      $_->[1] == OPTION_EL ||
5116                      $_->[1] == RUBY_COMPONENT_EL) {
5117              !!!cp ('t403');              !!!cp ('t403');
5118              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5119                              text => $_->[0]->manakai_local_name,                              text => $_->[0]->manakai_local_name,
# Line 7570  sub _tree_construction_main ($) { Line 5124  sub _tree_construction_main ($) {
5124            }            }
5125          }          }
5126    
5127            ## 3. Switch the insertion mode.
5128          $self->{insertion_mode} = AFTER_BODY_IM;          $self->{insertion_mode} = AFTER_BODY_IM;
5129          !!!next-token;          !!!next-token;
5130          next B;          next B;
# Line 7577  sub _tree_construction_main ($) { Line 5132  sub _tree_construction_main ($) {
5132          ## TODO: Update this code.  It seems that the code below is not          ## TODO: Update this code.  It seems that the code below is not
5133          ## up-to-date, though it has same effect as speced.          ## up-to-date, though it has same effect as speced.
5134          if (@{$self->{open_elements}} > 1 and          if (@{$self->{open_elements}} > 1 and
5135              $self->{open_elements}->[1]->[1] & BODY_EL) {              $self->{open_elements}->[1]->[1] == BODY_EL) {
5136            ## ISSUE: There is an issue in the spec.            unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
           unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {  
5137              !!!cp ('t406');              !!!cp ('t406');
5138              !!!parse-error (type => 'not closed',              !!!parse-error (type => 'not closed',
5139                              text => $self->{open_elements}->[1]->[0]                              text => $self->{open_elements}->[1]->[0]
# Line 7686  sub _tree_construction_main ($) { Line 5240  sub _tree_construction_main ($) {
5240          my $i;          my $i;
5241          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5242            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5243            if ($node->[1] & FORM_EL) {            if ($node->[1] == FORM_EL) {
5244              !!!cp ('t418');              !!!cp ('t418');
5245              $i = $_;              $i = $_;
5246              last INSCOPE;              last INSCOPE;
# Line 7734  sub _tree_construction_main ($) { Line 5288  sub _tree_construction_main ($) {
5288          my $i;          my $i;
5289          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5290            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5291            if ($node->[1] & HEADING_EL) {            if ($node->[1] == HEADING_EL) {
5292              !!!cp ('t423');              !!!cp ('t423');
5293              $i = $_;              $i = $_;
5294              last INSCOPE;              last INSCOPE;
# Line 7780  sub _tree_construction_main ($) { Line 5334  sub _tree_construction_main ($) {
5334          my $i;          my $i;
5335          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5336            my $node = $self->{open_elements}->[$_];            my $node = $self->{open_elements}->[$_];
5337            if ($node->[1] & P_EL) {            if ($node->[1] == P_EL) {
5338              !!!cp ('t410.1');              !!!cp ('t410.1');
5339              $i = $_;              $i = $_;
5340              last INSCOPE;              last INSCOPE;
# Line 7853  sub _tree_construction_main ($) { Line 5407  sub _tree_construction_main ($) {
5407          ## Ignore the token.          ## Ignore the token.
5408          !!!next-token;          !!!next-token;
5409          next B;          next B;
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!cp ('t429');  
         !!!parse-error (type => 'unmatched end tag',  
                         text => $token->{tag_name}, token => $token);  
         ## Ignore the token  
         !!!next-token;  
         next B;  
5410        } else {        } else {
5411          if ($token->{tag_name} eq 'sarcasm') {          if ($token->{tag_name} eq 'sarcasm') {
5412            sleep 0.001; # take a deep breath            sleep 0.001; # take a deep breath
# Line 7882  sub _tree_construction_main ($) { Line 5418  sub _tree_construction_main ($) {
5418    
5419          ## Step 2          ## Step 2
5420          S2: {          S2: {
5421            if ($node->[0]->manakai_local_name eq $token->{tag_name}) {            my $node_tag_name = $node->[0]->manakai_local_name;
5422              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
5423              if ($node_tag_name eq $token->{tag_name}) {
5424              ## Step 1              ## Step 1
5425              ## generate implied end tags              ## generate implied end tags
5426              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
# Line 7895  sub _tree_construction_main ($) { Line 5433  sub _tree_construction_main ($) {
5433              }              }
5434                    
5435              ## Step 2              ## Step 2
5436              if ($self->{open_elements}->[-1]->[0]->manakai_local_name              my $current_tag_name
5437                      ne $token->{tag_name}) {                  = $self->{open_elements}->[-1]->[0]->manakai_local_name;
5438                $current_tag_name =~ tr/A-Z/a-z/;
5439                if ($current_tag_name ne $token->{tag_name}) {
5440                !!!cp ('t431');                !!!cp ('t431');
5441                ## NOTE: <x><y></x>                ## NOTE: <x><y></x>
5442                !!!parse-error (type => 'not closed',                !!!parse-error (type => 'not closed',
# Line 7971  sub _tree_construction_main ($) { Line 5511  sub _tree_construction_main ($) {
5511    ## TODO: script stuffs    ## TODO: script stuffs
5512  } # _tree_construct_main  } # _tree_construct_main
5513    
5514    ## XXX: How this method is organized is somewhat out of date, although
5515    ## it still does what the current spec documents.
5516  sub set_inner_html ($$$$;$) {  sub set_inner_html ($$$$;$) {
5517    my $class = shift;    my $class = shift;
5518    my $node = shift;    my $node = shift; # /context/
5519    #my $s = \$_[0];    #my $s = \$_[0];
5520    my $onerror = $_[1];    my $onerror = $_[1];
5521    my $get_wrapper = $_[2] || sub ($) { return $_[0] };    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
# Line 7981  sub set_inner_html ($$$$;$) { Line 5523  sub set_inner_html ($$$$;$) {
5523    ## ISSUE: Should {confident} be true?    ## ISSUE: Should {confident} be true?
5524    
5525    my $nt = $node->node_type;    my $nt = $node->node_type;
5526    if ($nt == 9) {    if ($nt == 9) { # Document (invoke the algorithm with no /context/ element)
5527      # MUST      # MUST
5528            
5529      ## Step 1 # MUST      ## Step 1 # MUST
# Line 7996  sub set_inner_html ($$$$;$) { Line 5538  sub set_inner_html ($$$$;$) {
5538    
5539      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
5540      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
5541    } elsif ($nt == 1) {    } elsif ($nt == 1) { # Element (invoke the algorithm with /context/ element)
5542      ## TODO: If non-html element      ## TODO: If non-html element
5543    
5544      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
5545    
5546  ## TODO: Support for $get_wrapper  ## TODO: Support for $get_wrapper
5547    
5548      ## Step 1 # MUST      ## F1. Create an HTML document.
5549      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
5550      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
5551      $doc->manakai_is_html (1);      $doc->manakai_is_html (1);
5552    
5553        ## F2. Propagate quirkness flag
5554        my $node_doc = $node->owner_document;
5555        $doc->manakai_compat_mode ($node_doc->manakai_compat_mode);
5556    
5557        ## F3. Create an HTML parser
5558      my $p = $class->new;      my $p = $class->new;
5559      $p->{document} = $doc;      $p->{document} = $doc;
5560    
# Line 8134  sub set_inner_html ($$$$;$) { Line 5682  sub set_inner_html ($$$$;$) {
5682      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
5683      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
5684    
5685      ## Step 2      ## F4. If /context/ is not undef...
5686    
5687        ## F4.1. content model flag
5688      my $node_ln = $node->manakai_local_name;      my $node_ln = $node->manakai_local_name;
5689      $p->{content_model} = {      $p->{content_model} = {
5690        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
# Line 8150  sub set_inner_html ($$$$;$) { Line 5700  sub set_inner_html ($$$$;$) {
5700      }->{$node_ln};      }->{$node_ln};
5701      $p->{content_model} = PCDATA_CONTENT_MODEL      $p->{content_model} = PCDATA_CONTENT_MODEL
5702          unless defined $p->{content_model};          unless defined $p->{content_model};
         ## ISSUE: What is "the name of the element"? local name?  
5703    
5704      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
5705        ## TODO: Foreign element OK?        ## TODO: Foreign element OK?
5706    
5707      ## Step 3      ## F4.2. Root |html| element
5708      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
5709        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
5710    
5711      ## Step 4 # MUST      ## F4.3.
5712      $doc->append_child ($root);      $doc->append_child ($root);
5713    
5714      ## Step 5 # MUST      ## F4.4.
5715      push @{$p->{open_elements}}, [$root, $el_category->{html}];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
5716    
5717      undef $p->{head_element};      undef $p->{head_element};
5718        undef $p->{head_element_inserted};
5719    
5720      ## Step 6 # MUST      ## F4.5.
5721      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
5722    
5723      ## Step 7 # MUST      ## F4.6.
5724      my $anode = $node;      my $anode = $node;
5725      AN: while (defined $anode) {      AN: while (defined $anode) {
5726        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
# Line 8185  sub set_inner_html ($$$$;$) { Line 5735  sub set_inner_html ($$$$;$) {
5735        }        }
5736        $anode = $anode->parent_node;        $anode = $anode->parent_node;
5737      } # AN      } # AN
5738        
5739      ## Step 9 # MUST      ## F.6. Start the parser.
5740      {      {
5741        my $self = $p;        my $self = $p;
5742        !!!next-token;        !!!next-token;
5743      }      }
5744      $p->_tree_construction_main;      $p->_tree_construction_main;
5745    
5746      ## Step 10 # MUST      ## F.7.
5747      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
5748      for (@cn) {      for (@cn) {
5749        $node->remove_child ($_);        $node->remove_child ($_);

Legend:
Removed from v.1.197  
changed lines
  Added in v.1.229

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24