/[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.7 by wakaba, Wed May 30 12:24:50 2007 UTC revision 1.198 by wakaba, Sat Oct 4 08:58:02 2008 UTC
# Line 1  Line 1 
1  package Whatpm::HTML;  package Whatpm::HTML;
2  use strict;  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);
5    
6  ## This is an early version of an HTML parser.  ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8  my $permitted_slash_tag_name = {  ## modules.
9    base => 1,  ## Parse errors for control characters that are not allowed in HTML5
10    link => 1,  ## documents, for surrogate code points, and for noncharacter code
11    meta => 1,  ## points, as well as U+FFFD substitions for characters whose code points
12    hr => 1,  ## is higher than U+10FFFF may be detected by combining the parser with
13    br => 1,  ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    img=> 1,  ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    embed => 1,  ## WebHACC::Language::HTML module in the WebHACC package).
16    param => 1,  
17    area => 1,  ## ISSUE:
18    col => 1,  ## var doc = implementation.createDocument (null, null, null);
19    input => 1,  ## doc.write ('');
20    ## alert (doc.compatMode);
21    
22    require IO::Handle;
23    
24    my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26    my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    
31    sub A_EL () { 0b1 }
32    sub ADDRESS_EL () { 0b10 }
33    sub BODY_EL () { 0b100 }
34    sub BUTTON_EL () { 0b1000 }
35    sub CAPTION_EL () { 0b10000 }
36    sub DD_EL () { 0b100000 }
37    sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      BODY_EL |
90      HTML_EL |
91      TABLE_CELL_EL |
92      TABLE_ROW_EL |
93      TABLE_ROW_GROUP_EL
94    }
95    
96    sub SCOPING_EL () {
97      BUTTON_EL |
98      CAPTION_EL |
99      HTML_EL |
100      TABLE_EL |
101      TABLE_CELL_EL |
102      MISC_SCOPING_EL
103    }
104    
105    sub TABLE_SCOPING_EL () {
106      HTML_EL |
107      TABLE_EL
108    }
109    
110    sub TABLE_ROWS_SCOPING_EL () {
111      HTML_EL |
112      TABLE_ROW_GROUP_EL
113    }
114    
115    sub TABLE_ROW_SCOPING_EL () {
116      HTML_EL |
117      TABLE_ROW_EL
118    }
119    
120    sub SPECIAL_EL () {
121      ADDRESS_EL |
122      BODY_EL |
123      DIV_EL |
124    
125      DD_EL |
126      DT_EL |
127      LI_EL |
128      P_EL |
129    
130      FORM_EL |
131      FRAMESET_EL |
132      HEADING_EL |
133      OPTION_EL |
134      OPTGROUP_EL |
135      SELECT_EL |
136      TABLE_ROW_EL |
137      TABLE_ROW_GROUP_EL |
138      MISC_SPECIAL_EL
139    }
140    
141    my $el_category = {
142      a => A_EL | FORMATTING_EL,
143      address => ADDRESS_EL,
144      applet => MISC_SCOPING_EL,
145      area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148      b => FORMATTING_EL,
149      base => MISC_SPECIAL_EL,
150      basefont => MISC_SPECIAL_EL,
151      bgsound => MISC_SPECIAL_EL,
152      big => FORMATTING_EL,
153      blockquote => MISC_SPECIAL_EL,
154      body => BODY_EL,
155      br => MISC_SPECIAL_EL,
156      button => BUTTON_EL,
157      caption => CAPTION_EL,
158      center => MISC_SPECIAL_EL,
159      col => MISC_SPECIAL_EL,
160      colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163      dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166      dir => MISC_SPECIAL_EL,
167      div => DIV_EL,
168      dl => MISC_SPECIAL_EL,
169      dt => DT_EL,
170      em => FORMATTING_EL,
171      embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173      fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175      font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177      form => FORM_EL,
178      frame => MISC_SPECIAL_EL,
179      frameset => FRAMESET_EL,
180      h1 => HEADING_EL,
181      h2 => HEADING_EL,
182      h3 => HEADING_EL,
183      h4 => HEADING_EL,
184      h5 => HEADING_EL,
185      h6 => HEADING_EL,
186      head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188      hr => MISC_SPECIAL_EL,
189      html => HTML_EL,
190      i => FORMATTING_EL,
191      iframe => MISC_SPECIAL_EL,
192      img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194      input => MISC_SPECIAL_EL,
195      isindex => MISC_SPECIAL_EL,
196      li => LI_EL,
197      link => MISC_SPECIAL_EL,
198      listing => MISC_SPECIAL_EL,
199      marquee => MISC_SCOPING_EL,
200      menu => MISC_SPECIAL_EL,
201      meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203      nobr => NOBR_EL | FORMATTING_EL,
204      noembed => MISC_SPECIAL_EL,
205      noframes => MISC_SPECIAL_EL,
206      noscript => MISC_SPECIAL_EL,
207      object => MISC_SCOPING_EL,
208      ol => MISC_SPECIAL_EL,
209      optgroup => OPTGROUP_EL,
210      option => OPTION_EL,
211      p => P_EL,
212      param => MISC_SPECIAL_EL,
213      plaintext => MISC_SPECIAL_EL,
214      pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218      s => FORMATTING_EL,
219      script => MISC_SPECIAL_EL,
220      select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222      small => FORMATTING_EL,
223      spacer => MISC_SPECIAL_EL,
224      strike => FORMATTING_EL,
225      strong => FORMATTING_EL,
226      style => MISC_SPECIAL_EL,
227      table => TABLE_EL,
228      tbody => TABLE_ROW_GROUP_EL,
229      td => TABLE_CELL_EL,
230      textarea => MISC_SPECIAL_EL,
231      tfoot => TABLE_ROW_GROUP_EL,
232      th => TABLE_CELL_EL,
233      thead => TABLE_ROW_GROUP_EL,
234      title => MISC_SPECIAL_EL,
235      tr => TABLE_ROW_EL,
236      tt => FORMATTING_EL,
237      u => FORMATTING_EL,
238      ul => MISC_SPECIAL_EL,
239      wbr => MISC_SPECIAL_EL,
240  };  };
241    
242  my $entity_char = {  my $el_category_f = {
243    AElig => "\x{00C6}",    $MML_NS => {
244    Aacute => "\x{00C1}",      'annotation-xml' => MML_AXML_EL,
245    Acirc => "\x{00C2}",      mi => FOREIGN_FLOW_CONTENT_EL,
246    Agrave => "\x{00C0}",      mo => FOREIGN_FLOW_CONTENT_EL,
247    Alpha => "\x{0391}",      mn => FOREIGN_FLOW_CONTENT_EL,
248    Aring => "\x{00C5}",      ms => FOREIGN_FLOW_CONTENT_EL,
249    Atilde => "\x{00C3}",      mtext => FOREIGN_FLOW_CONTENT_EL,
250    Auml => "\x{00C4}",    },
251    Beta => "\x{0392}",    $SVG_NS => {
252    Ccedil => "\x{00C7}",      foreignObject => FOREIGN_FLOW_CONTENT_EL,
253    Chi => "\x{03A7}",      desc => FOREIGN_FLOW_CONTENT_EL,
254    Dagger => "\x{2021}",      title => FOREIGN_FLOW_CONTENT_EL,
255    Delta => "\x{0394}",    },
256    ETH => "\x{00D0}",    ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
   Eacute => "\x{00C9}",  
   Ecirc => "\x{00CA}",  
   Egrave => "\x{00C8}",  
   Epsilon => "\x{0395}",  
   Eta => "\x{0397}",  
   Euml => "\x{00CB}",  
   Gamma => "\x{0393}",  
   Iacute => "\x{00CD}",  
   Icirc => "\x{00CE}",  
   Igrave => "\x{00CC}",  
   Iota => "\x{0399}",  
   Iuml => "\x{00CF}",  
   Kappa => "\x{039A}",  
   Lambda => "\x{039B}",  
   Mu => "\x{039C}",  
   Ntilde => "\x{00D1}",  
   Nu => "\x{039D}",  
   OElig => "\x{0152}",  
   Oacute => "\x{00D3}",  
   Ocirc => "\x{00D4}",  
   Ograve => "\x{00D2}",  
   Omega => "\x{03A9}",  
   Omicron => "\x{039F}",  
   Oslash => "\x{00D8}",  
   Otilde => "\x{00D5}",  
   Ouml => "\x{00D6}",  
   Phi => "\x{03A6}",  
   Pi => "\x{03A0}",  
   Prime => "\x{2033}",  
   Psi => "\x{03A8}",  
   Rho => "\x{03A1}",  
   Scaron => "\x{0160}",  
   Sigma => "\x{03A3}",  
   THORN => "\x{00DE}",  
   Tau => "\x{03A4}",  
   Theta => "\x{0398}",  
   Uacute => "\x{00DA}",  
   Ucirc => "\x{00DB}",  
   Ugrave => "\x{00D9}",  
   Upsilon => "\x{03A5}",  
   Uuml => "\x{00DC}",  
   Xi => "\x{039E}",  
   Yacute => "\x{00DD}",  
   Yuml => "\x{0178}",  
   Zeta => "\x{0396}",  
   aacute => "\x{00E1}",  
   acirc => "\x{00E2}",  
   acute => "\x{00B4}",  
   aelig => "\x{00E6}",  
   agrave => "\x{00E0}",  
   alefsym => "\x{2135}",  
   alpha => "\x{03B1}",  
   amp => "\x{0026}",  
   AMP => "\x{0026}",  
   and => "\x{2227}",  
   ang => "\x{2220}",  
   apos => "\x{0027}",  
   aring => "\x{00E5}",  
   asymp => "\x{2248}",  
   atilde => "\x{00E3}",  
   auml => "\x{00E4}",  
   bdquo => "\x{201E}",  
   beta => "\x{03B2}",  
   brvbar => "\x{00A6}",  
   bull => "\x{2022}",  
   cap => "\x{2229}",  
   ccedil => "\x{00E7}",  
   cedil => "\x{00B8}",  
   cent => "\x{00A2}",  
   chi => "\x{03C7}",  
   circ => "\x{02C6}",  
   clubs => "\x{2663}",  
   cong => "\x{2245}",  
   copy => "\x{00A9}",  
   COPY => "\x{00A9}",  
   crarr => "\x{21B5}",  
   cup => "\x{222A}",  
   curren => "\x{00A4}",  
   dArr => "\x{21D3}",  
   dagger => "\x{2020}",  
   darr => "\x{2193}",  
   deg => "\x{00B0}",  
   delta => "\x{03B4}",  
   diams => "\x{2666}",  
   divide => "\x{00F7}",  
   eacute => "\x{00E9}",  
   ecirc => "\x{00EA}",  
   egrave => "\x{00E8}",  
   empty => "\x{2205}",  
   emsp => "\x{2003}",  
   ensp => "\x{2002}",  
   epsilon => "\x{03B5}",  
   equiv => "\x{2261}",  
   eta => "\x{03B7}",  
   eth => "\x{00F0}",  
   euml => "\x{00EB}",  
   euro => "\x{20AC}",  
   exist => "\x{2203}",  
   fnof => "\x{0192}",  
   forall => "\x{2200}",  
   frac12 => "\x{00BD}",  
   frac14 => "\x{00BC}",  
   frac34 => "\x{00BE}",  
   frasl => "\x{2044}",  
   gamma => "\x{03B3}",  
   ge => "\x{2265}",  
   gt => "\x{003E}",  
   GT => "\x{003E}",  
   hArr => "\x{21D4}",  
   harr => "\x{2194}",  
   hearts => "\x{2665}",  
   hellip => "\x{2026}",  
   iacute => "\x{00ED}",  
   icirc => "\x{00EE}",  
   iexcl => "\x{00A1}",  
   igrave => "\x{00EC}",  
   image => "\x{2111}",  
   infin => "\x{221E}",  
   int => "\x{222B}",  
   iota => "\x{03B9}",  
   iquest => "\x{00BF}",  
   isin => "\x{2208}",  
   iuml => "\x{00EF}",  
   kappa => "\x{03BA}",  
   lArr => "\x{21D0}",  
   lambda => "\x{03BB}",  
   lang => "\x{2329}",  
   laquo => "\x{00AB}",  
   larr => "\x{2190}",  
   lceil => "\x{2308}",  
   ldquo => "\x{201C}",  
   le => "\x{2264}",  
   lfloor => "\x{230A}",  
   lowast => "\x{2217}",  
   loz => "\x{25CA}",  
   lrm => "\x{200E}",  
   lsaquo => "\x{2039}",  
   lsquo => "\x{2018}",  
   lt => "\x{003C}",  
   LT => "\x{003C}",  
   macr => "\x{00AF}",  
   mdash => "\x{2014}",  
   micro => "\x{00B5}",  
   middot => "\x{00B7}",  
   minus => "\x{2212}",  
   mu => "\x{03BC}",  
   nabla => "\x{2207}",  
   nbsp => "\x{00A0}",  
   ndash => "\x{2013}",  
   ne => "\x{2260}",  
   ni => "\x{220B}",  
   not => "\x{00AC}",  
   notin => "\x{2209}",  
   nsub => "\x{2284}",  
   ntilde => "\x{00F1}",  
   nu => "\x{03BD}",  
   oacute => "\x{00F3}",  
   ocirc => "\x{00F4}",  
   oelig => "\x{0153}",  
   ograve => "\x{00F2}",  
   oline => "\x{203E}",  
   omega => "\x{03C9}",  
   omicron => "\x{03BF}",  
   oplus => "\x{2295}",  
   or => "\x{2228}",  
   ordf => "\x{00AA}",  
   ordm => "\x{00BA}",  
   oslash => "\x{00F8}",  
   otilde => "\x{00F5}",  
   otimes => "\x{2297}",  
   ouml => "\x{00F6}",  
   para => "\x{00B6}",  
   part => "\x{2202}",  
   permil => "\x{2030}",  
   perp => "\x{22A5}",  
   phi => "\x{03C6}",  
   pi => "\x{03C0}",  
   piv => "\x{03D6}",  
   plusmn => "\x{00B1}",  
   pound => "\x{00A3}",  
   prime => "\x{2032}",  
   prod => "\x{220F}",  
   prop => "\x{221D}",  
   psi => "\x{03C8}",  
   quot => "\x{0022}",  
   QUOT => "\x{0022}",  
   rArr => "\x{21D2}",  
   radic => "\x{221A}",  
   rang => "\x{232A}",  
   raquo => "\x{00BB}",  
   rarr => "\x{2192}",  
   rceil => "\x{2309}",  
   rdquo => "\x{201D}",  
   real => "\x{211C}",  
   reg => "\x{00AE}",  
   REG => "\x{00AE}",  
   rfloor => "\x{230B}",  
   rho => "\x{03C1}",  
   rlm => "\x{200F}",  
   rsaquo => "\x{203A}",  
   rsquo => "\x{2019}",  
   sbquo => "\x{201A}",  
   scaron => "\x{0161}",  
   sdot => "\x{22C5}",  
   sect => "\x{00A7}",  
   shy => "\x{00AD}",  
   sigma => "\x{03C3}",  
   sigmaf => "\x{03C2}",  
   sim => "\x{223C}",  
   spades => "\x{2660}",  
   sub => "\x{2282}",  
   sube => "\x{2286}",  
   sum => "\x{2211}",  
   sup => "\x{2283}",  
   sup1 => "\x{00B9}",  
   sup2 => "\x{00B2}",  
   sup3 => "\x{00B3}",  
   supe => "\x{2287}",  
   szlig => "\x{00DF}",  
   tau => "\x{03C4}",  
   there4 => "\x{2234}",  
   theta => "\x{03B8}",  
   thetasym => "\x{03D1}",  
   thinsp => "\x{2009}",  
   thorn => "\x{00FE}",  
   tilde => "\x{02DC}",  
   times => "\x{00D7}",  
   trade => "\x{2122}",  
   uArr => "\x{21D1}",  
   uacute => "\x{00FA}",  
   uarr => "\x{2191}",  
   ucirc => "\x{00FB}",  
   ugrave => "\x{00F9}",  
   uml => "\x{00A8}",  
   upsih => "\x{03D2}",  
   upsilon => "\x{03C5}",  
   uuml => "\x{00FC}",  
   weierp => "\x{2118}",  
   xi => "\x{03BE}",  
   yacute => "\x{00FD}",  
   yen => "\x{00A5}",  
   yuml => "\x{00FF}",  
   zeta => "\x{03B6}",  
   zwj => "\x{200D}",  
   zwnj => "\x{200C}",  
 }; # $entity_char  
   
 ## <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8562>  
 my $c1_entity_char = {  
      128, 8364,  
      129, 65533,  
      130, 8218,  
      131, 402,  
      132, 8222,  
      133, 8230,  
      134, 8224,  
      135, 8225,  
      136, 710,  
      137, 8240,  
      138, 352,  
      139, 8249,  
      140, 338,  
      141, 65533,  
      142, 381,  
      143, 65533,  
      144, 65533,  
      145, 8216,  
      146, 8217,  
      147, 8220,  
      148, 8221,  
      149, 8226,  
      150, 8211,  
      151, 8212,  
      152, 732,  
      153, 8482,  
      154, 353,  
      155, 8250,  
      156, 339,  
      157, 65533,  
      158, 382,  
      159, 376,  
 }; # $c1_entity_char  
   
 my $special_category = {  
   address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  
   blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
   dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  
   form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,  
   h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  
   img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
   menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  
   ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  
   pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
   textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  
257  };  };
258  my $scoping_category = {  
259    button => 1, caption => 1, html => 1, marquee => 1, object => 1,  my $svg_attr_name = {
260    table => 1, td => 1, th => 1,    attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323  my $formatting_category = {  
324    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  my $foreign_attr_xname = {
325    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,    'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337  };  };
 # $phrasing_category: all other elements  
338    
339  sub parse_string ($$$;$) {  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    my $self = shift->new;  
341    my $s = \$_[0];  my $charref_map = {
342      0x0D => 0x000A,
343      0x80 => 0x20AC,
344      0x81 => 0xFFFD,
345      0x82 => 0x201A,
346      0x83 => 0x0192,
347      0x84 => 0x201E,
348      0x85 => 0x2026,
349      0x86 => 0x2020,
350      0x87 => 0x2021,
351      0x88 => 0x02C6,
352      0x89 => 0x2030,
353      0x8A => 0x0160,
354      0x8B => 0x2039,
355      0x8C => 0x0152,
356      0x8D => 0xFFFD,
357      0x8E => 0x017D,
358      0x8F => 0xFFFD,
359      0x90 => 0xFFFD,
360      0x91 => 0x2018,
361      0x92 => 0x2019,
362      0x93 => 0x201C,
363      0x94 => 0x201D,
364      0x95 => 0x2022,
365      0x96 => 0x2013,
366      0x97 => 0x2014,
367      0x98 => 0x02DC,
368      0x99 => 0x2122,
369      0x9A => 0x0161,
370      0x9B => 0x203A,
371      0x9C => 0x0153,
372      0x9D => 0xFFFD,
373      0x9E => 0x017E,
374      0x9F => 0x0178,
375    }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385    ## TODO: Invoke the reset algorithm when a resettable element is
386    ## created (cf. HTML5 revision 2259).
387    
388    sub parse_byte_string ($$$$;$) {
389      my $self = shift;
390      my $charset_name = shift;
391      open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392      return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    } # parse_byte_string
394    
395    sub parse_byte_stream ($$$$;$$) {
396      # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397      my $self = ref $_[0] ? shift : shift->new;
398      my $charset_name = shift;
399      my $byte_stream = $_[0];
400    
401      my $onerror = $_[2] || sub {
402        my (%opt) = @_;
403        warn "Parse error ($opt{type})\n";
404      };
405      $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      };
410    
411      ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444        }
445    
446        ## Step 2
447        my $byte_buffer = '';
448        for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460          $self->{confident} = 1;
461          last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476        }
477    
478        ## Step 4
479        ## TODO: <meta charset>
480    
481        ## Step 5
482        ## TODO: from history
483    
484        ## Step 6
485        require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599      }; # $self->{change_encoding}
600    
601      my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615      my $return;
616      try {
617        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618      } catch Whatpm::HTML::RestartParser with {
619        ## NOTE: Invoked after {change_encoding}.
620    
621        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639        $self->{confident} = 1;
640    
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645      };
646      return $return;
647    } # parse_byte_stream
648    
649    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
651    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
652    ## because the core part of our HTML parser expects a string of character,
653    ## not a string of bytes or code units or anything which might contain a BOM.
654    ## Therefore, any parser interface that accepts a string of bytes,
655    ## such as |parse_byte_string| in this module, must ensure that it does
656    ## strip the BOM and never strip any ZWNBSP.
657    
658    sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668    sub parse_char_stream ($$$;$$) {
669      my $self = ref $_[0] ? shift : shift->new;
670      my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672      @{$self->{document}->child_nodes} = ();
673    
674    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
675    
676    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
677    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
678    my $column = 0;        if defined $self->{input_encoding};
679    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
680    
681      $self->{line_prev} = $self->{line} = 1;
682      $self->{column_prev} = -1;
683      $self->{column} = 0;
684      $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686      $self->{next_input_character} = -1 and return if $i >= length $$s;  
687      $self->{next_input_character} = ord substr $$s, $i++, 1;      my $char = '';
688      $column++;      if (defined $self->{next_nc}) {
689              $char = $self->{next_nc};
690      if ($self->{next_input_character} == 0x000A) { # LF        delete $self->{next_nc};
691        $line++;        $self->{nc} = ord $char;
692        $column = 0;      } else {
693      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{char_buffer} = '';
694        if ($i >= length $$s) {        $self->{char_buffer_pos} = 0;
695          #  
696          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709        } else {        } else {
710          my $next_char = ord substr $$s, $i++, 1;          $self->{nc} = -1;
711          if ($next_char == 0x000A) { # LF          return;
           #  
         } else {  
           push @{$self->{char}}, $next_char;  
         }  
712        }        }
713        $self->{next_input_character} = 0x000A; # LF # MUST      }
714        $line++;  
715        $column = 0;      ($self->{line_prev}, $self->{column_prev})
716      } elsif ($self->{next_input_character} > 0x10FFFF) {          = ($self->{line}, $self->{column});
717        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST      $self->{column}++;
718      } elsif ($self->{next_input_character} == 0x0000) { # NULL      
719        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST      if ($self->{nc} == 0x000A) { # LF
720          !!!cp ('j1');
721          $self->{line}++;
722          $self->{column} = 0;
723        } elsif ($self->{nc} == 0x000D) { # CR
724          !!!cp ('j2');
725    ## TODO: support for abort/streaming
726          my $next = '';
727          if ($input->read ($next, 1) and $next ne "\x0A") {
728            $self->{next_nc} = $next;
729          }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735          !!!parse-error (type => 'NULL');
736          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    
740      $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771          }
772          return $count;
773        }
774      }; # $self->{read_until}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_input_character} = sub {      level => {must => 'm',
814      $self->{next_input_character} = -1;                should => 's',
815                  warn => 'w',
816                  info => 'i',
817                  uncertain => 'u'},
818      }, $class;
819      $self->{set_nc} = sub {
820        $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
824    };    };
825      $self->{change_encoding} = sub {
826        # if ($_[0] is a supported encoding) {
827        #   run "change the encoding" algorithm;
828        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
829        # }
830      };
831      $self->{application_cache_selection} = sub {
832        #
833      };
834    return $self;    return $self;
835  } # new  } # new
836    
837    sub CM_ENTITY () { 0b001 } # & markup in data
838    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
839    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
840    
841    sub PLAINTEXT_CONTENT_MODEL () { 0 }
842    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
843    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
844    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846    sub DATA_STATE () { 0 }
847    #sub ENTITY_DATA_STATE () { 1 }
848    sub TAG_OPEN_STATE () { 2 }
849    sub CLOSE_TAG_OPEN_STATE () { 3 }
850    sub TAG_NAME_STATE () { 4 }
851    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
852    sub ATTRIBUTE_NAME_STATE () { 6 }
853    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
854    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
855    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860    sub COMMENT_START_STATE () { 14 }
861    sub COMMENT_START_DASH_STATE () { 15 }
862    sub COMMENT_STATE () { 16 }
863    sub COMMENT_END_STATE () { 17 }
864    sub COMMENT_END_DASH_STATE () { 18 }
865    sub BOGUS_COMMENT_STATE () { 19 }
866    sub DOCTYPE_STATE () { 20 }
867    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
868    sub DOCTYPE_NAME_STATE () { 22 }
869    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
870    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
871    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
872    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
873    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
874    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
875    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
876    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
877    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878    sub BOGUS_DOCTYPE_STATE () { 32 }
879    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901    sub DOCTYPE_TOKEN () { 1 }
902    sub COMMENT_TOKEN () { 2 }
903    sub START_TAG_TOKEN () { 3 }
904    sub END_TAG_TOKEN () { 4 }
905    sub END_OF_FILE_TOKEN () { 5 }
906    sub CHARACTER_TOKEN () { 6 }
907    
908    sub AFTER_HTML_IMS () { 0b100 }
909    sub HEAD_IMS ()       { 0b1000 }
910    sub BODY_IMS ()       { 0b10000 }
911    sub BODY_TABLE_IMS () { 0b100000 }
912    sub TABLE_IMS ()      { 0b1000000 }
913    sub ROW_IMS ()        { 0b10000000 }
914    sub BODY_AFTER_IMS () { 0b100000000 }
915    sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922    ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
924    ## NOTE: "after after body" insertion mode.
925    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
926    
927    ## NOTE: "after after frameset" insertion mode.
928    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
929    
930    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
931    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
932    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
933    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
934    sub IN_BODY_IM () { BODY_IMS }
935    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
936    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
937    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
938    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
939    sub IN_TABLE_IM () { TABLE_IMS }
940    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945    sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
948    
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
952    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{s_kwd}; # state keyword - initialized when used
953    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    #$self->{entity__value}; # initialized when used
954    undef $self->{current_attribute};    #$self->{entity__match}; # initialized when used
955    undef $self->{last_emitted_start_tag_name};    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{last_attribute_value_state};    undef $self->{ct}; # current token
957    $self->{char} = [];    undef $self->{ca}; # current attribute
958    # $self->{next_input_character}    undef $self->{last_stag_name}; # last emitted start tag name
959      #$self->{prev_state}; # initialized when used
960      delete $self->{self_closing};
961      $self->{char_buffer} = '';
962      $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967      # $self->{escape}
968  } # _initialize_tokenizer  } # _initialize_tokenizer
969    
970  ## A token has:  ## A token has:
971  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
972  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE, start tag (tagname), end tag (tagname))  ##   ->{name} (DOCTYPE_TOKEN)
974      ## ISSUE: the spec need s/tagname/tag name/  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{error} == 1 or 0 (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{data} (comment, character)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978    ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979  ## Macros  ##        ->{name}
980  ##   Macros MUST be preceded by three EXCLAMATION MARKs.  ##        ->{value}
981  ##   emit ($token)  ##        ->{has_reference} == 1 or 0
982  ##     Emits the specified token.  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
983    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
984    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985    ##     while the token is pushed back to the stack.
986    
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
# Line 444  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996    ## (This requirement was dropped from HTML5 spec, unfortunately.)
997    
998    my $is_space = {
999      0x0009 => 1, # CHARACTER TABULATION (HT)
1000      0x000A => 1, # LINE FEED (LF)
1001      #0x000B => 0, # LINE TABULATION (VT)
1002      0x000C => 1, # FORM FEED (FF)
1003      #0x000D => 1, # CARRIAGE RETURN (CR)
1004      0x0020 => 1, # SPACE (SP)
1005    };
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010      if ($self->{self_closing}) {
1011        !!!parse-error (type => 'nestc', token => $self->{ct});
1012        ## NOTE: The |self_closing| flag is only set by start tag token.
1013        ## In addition, when a start tag token is emitted, it is always set to
1014        ## |ct|.
1015        delete $self->{self_closing};
1016      }
1017    
1018    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1019        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1020      return shift @{$self->{token}};      return shift @{$self->{token}};
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026          if ($self->{content_model_flag} eq 'PCDATA' or  
1027              $self->{content_model_flag} eq 'RCDATA') {        if ($self->{nc} == 0x0026) { # &
1028            $self->{state} = 'entity data';          !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069                not $self->{escape}) {
1070              !!!cp (1);
1071              ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081              !!!cp (2);
1082              #
1083            }
1084          } elsif ($self->{nc} == 0x002D) { # -
1085            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086              $self->{s_kwd} .= '-';
1087              
1088              if ($self->{s_kwd} eq '<!--') {
1089                !!!cp (3);
1090                $self->{escape} = 1; # unless $self->{escape};
1091                $self->{s_kwd} = '--';
1092                #
1093              } elsif ($self->{s_kwd} eq '---') {
1094                !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097              } else {
1098                !!!cp (5);
1099                #
1100              }
1101            }
1102            
1103            #
1104          } elsif ($self->{nc} == 0x0021) { # !
1105            if (length $self->{s_kwd}) {
1106              !!!cp (5.1);
1107              $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112            #            #
1113          }          }
1114        } elsif ($self->{next_input_character} == 0x003C) { # <          #
1115          if ($self->{content_model_flag} ne 'PLAINTEXT') {        } elsif ($self->{nc} == 0x003C) { # <
1116            $self->{state} = 'tag open';          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118                 not $self->{escape})) {
1119              !!!cp (6);
1120              $self->{state} = TAG_OPEN_STATE;
1121            !!!next-input-character;            !!!next-input-character;
1122            redo A;            redo A;
1123          } else {          } else {
1124              !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
1129          !!!emit ({type => 'end-of-file'});          if ($self->{escape} and
1130                ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131              if ($self->{s_kwd} eq '--') {
1132                !!!cp (8);
1133                delete $self->{escape};
1134              } else {
1135                !!!cp (9);
1136              }
1137            } else {
1138              !!!cp (10);
1139            }
1140            
1141            $self->{s_kwd} = '';
1142            #
1143          } elsif ($self->{nc} == -1) {
1144            !!!cp (11);
1145            $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149          } else {
1150            !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
   
       !!!emit ($token);  
   
       redo A;  
     } elsif ($self->{state} eq 'entity data') {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity;  
1154    
1155        $self->{state} = 'data';        # Anything else
1156        # next-input-character is already done        my $token = {type => CHARACTER_TOKEN,
1157                       data => chr $self->{nc},
1158                       line => $self->{line}, column => $self->{column},
1159                      };
1160          if ($self->{read_until}->($token->{data}, q[-!<>&],
1161                                    length $token->{data})) {
1162            $self->{s_kwd} = '';
1163          }
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          !!!emit ({type => 'character', data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167            !!!cp (13);
1168            $self->{state} = PCDATA_STATE;
1169        } else {        } else {
1170          !!!emit ($token);          !!!cp (14);
1171            ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178            $self->{content_model_flag} eq 'CDATA') {          if ($self->{nc} == 0x002F) { # /
1179          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            ## reconsume            !!!cp (16);
1189            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1190          }          }
1191        } elsif ($self->{content_model_flag} eq 'PCDATA') {  
1192          if ($self->{next_input_character} == 0x0021) { # !          ## reconsume
1193            $self->{state} = 'markup declaration open';          $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199          } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200            if ($self->{nc} == 0x0021) { # !
1201              !!!cp (17);
1202              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206            $self->{state} = 'close tag open';            !!!cp (18);
1207              $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            $self->{current_token}            !!!cp (19);
1213              = {type => 'start tag',            $self->{ct}
1214                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1215            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1216                   line => $self->{line_prev},
1217                   column => $self->{column_prev}};
1218              $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1224                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1225            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1226                                        line => $self->{line_prev},
1227                                        column => $self->{column_prev}};
1228              $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1233            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1234                              line => $self->{line_prev},
1235                              column => $self->{column_prev});
1236              $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!parse-error (type => 'pio');            !!!cp (22);
1247            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1248            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250              $self->{state} = BOGUS_COMMENT_STATE;
1251              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!parse-error (type => 'bare stago');            !!!cp (23);
1259            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1260                              line => $self->{line_prev},
1261                              column => $self->{column_prev});
1262              $self->{state} = DATA_STATE;
1263            ## reconsume            ## reconsume
1264    
1265            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
1272        } else {        } else {
1273          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1277            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1278          my @next_char;  
1279          TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            push @next_char, $self->{next_input_character};        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281            my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);          if (defined $self->{last_stag_name}) {
1282            my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {            $self->{s_kwd} = '';
1284              !!!next-input-character;            ## Reconsume.
             next TAGNAME;  
           } else {  
             !!!parse-error (type => 'unmatched end tag');  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = 'data';  
   
             !!!emit ({type => 'character', data => '</'});  
   
             redo A;  
           }  
         }  
         push @next_char, $self->{next_input_character};  
       
         unless ($self->{next_input_character} == 0x0009 or # HT  
                 $self->{next_input_character} == 0x000A or # LF  
                 $self->{next_input_character} == 0x000B or # VT  
                 $self->{next_input_character} == 0x000C or # FF  
                 $self->{next_input_character} == 0x0020 or # SP  
                 $self->{next_input_character} == 0x003E or # >  
                 $self->{next_input_character} == 0x002F or # /  
                 $self->{next_input_character} == 0x003C or # <  
                 $self->{next_input_character} == -1) {  
           !!!parse-error (type => 'unmatched end tag');  
           $self->{next_input_character} = shift @next_char; # reconsume  
           !!!back-next-input-character (@next_char);  
           $self->{state} = 'data';  
   
           !!!emit ({type => 'character', data => '</'});  
   
1285            redo A;            redo A;
1286          } else {          } else {
1287            $self->{next_input_character} = shift @next_char;            ## No start tag token has ever been emitted
1288            !!!back-next-input-character (@next_char);            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289            # and consume...            !!!cp (28);
1290              $self->{state} = DATA_STATE;
1291              ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295              redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1302                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1303          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1304          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1305          redo A;                 line => $l, column => $c};
1306        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1307                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1308          $self->{current_token} = {type => 'end tag',          redo A;
1309                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1310          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1311          !!!next-input-character;          !!!cp (30);
1312          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1313        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1314          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1315          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1316            !!!next-input-character;
1317            redo A;
1318          } elsif ($self->{nc} == 0x003E) { # >
1319            !!!cp (31);
1320            !!!parse-error (type => 'empty end tag',
1321                            line => $self->{line_prev}, ## "<" in "</>"
1322                            column => $self->{column_prev} - 1);
1323            $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1327            !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338            !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343        }                                    column => $self->{column_prev} - 1,
1344      } elsif ($self->{state} eq 'tag name') {                                   };
1345        if ($self->{next_input_character} == 0x0009 or # HT          ## NOTE: $self->{nc} is intentionally left as is.
1346            $self->{next_input_character} == 0x000A or # LF          ## Although the "anything else" case of the spec not explicitly
1347            $self->{next_input_character} == 0x000B or # VT          ## states that the next input character is to be reconsumed,
1348            $self->{next_input_character} == 0x000C or # FF          ## it will be included to the |data| of the comment token
1349            $self->{next_input_character} == 0x0020) { # SP          ## generated from the bogus end tag, as defined in the
1350          $self->{state} = 'before attribute name';          ## "bogus comment state" entry.
1351          !!!next-input-character;          redo A;
1352          redo A;        }
1353        } elsif ($self->{next_input_character} == 0x003E) { # >      } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          if ($self->{current_token}->{type} eq 'start tag') {        my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};        if (length $ch) {
1356          } elsif ($self->{current_token}->{type} eq 'end tag') {          my $CH = $ch;
1357            $self->{content_model_flag} = 'PCDATA'; # MUST          $ch =~ tr/a-z/A-Z/;
1358            if ($self->{current_token}->{attributes}) {          my $nch = chr $self->{nc};
1359              !!!parse-error (type => 'end tag attribute');          if ($nch eq $ch or $nch eq $CH) {
1360            }            !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365          } else {          } else {
1366            die "$0: $self->{current_token}->{type}: Unknown token type";            !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375          }          }
1376          $self->{state} = 'data';        } else { # after "<{tag-name}"
1377          !!!next-input-character;          unless ($is_space->{$self->{nc}} or
1378                    {
1379          !!!emit ($self->{current_token}); # start tag or end tag                   0x003E => 1, # >
1380          undef $self->{current_token};                   0x002F => 1, # /
1381                     -1 => 1, # EOF
1382          redo A;                  }->{$self->{nc}}) {
1383        } elsif (0x0041 <= $self->{next_input_character} and            !!!cp (26);
1384                 $self->{next_input_character} <= 0x005A) { # A..Z            ## Reconsume.
1385          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);            $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403          }
1404        } elsif ($self->{state} == TAG_NAME_STATE) {
1405          if ($is_space->{$self->{nc}}) {
1406            !!!cp (34);
1407            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408            !!!next-input-character;
1409            redo A;
1410          } elsif ($self->{nc} == 0x003E) { # >
1411            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412              !!!cp (35);
1413              $self->{last_stag_name} = $self->{ct}->{tag_name};
1414            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1415              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416              #if ($self->{ct}->{attributes}) {
1417              #  ## NOTE: This should never be reached.
1418              #  !!! cp (36);
1419              #  !!! parse-error (type => 'end tag attribute');
1420              #} else {
1421                !!!cp (37);
1422              #}
1423            } else {
1424              die "$0: $self->{ct}->{type}: Unknown token type";
1425            }
1426            $self->{state} = DATA_STATE;
1427            !!!next-input-character;
1428    
1429            !!!emit ($self->{ct}); # start tag or end tag
1430    
1431            redo A;
1432          } elsif (0x0041 <= $self->{nc} and
1433                   $self->{nc} <= 0x005A) { # A..Z
1434            !!!cp (38);
1435            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (39);
1444          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1446            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447              !!!parse-error (type => 'end tag attribute');            #if ($self->{ct}->{attributes}) {
1448            }            #  ## NOTE: This state should never be reached.
1449              #  !!! cp (40);
1450              #  !!! parse-error (type => 'end tag attribute');
1451              #} else {
1452                !!!cp (41);
1453              #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464            !!!cp (42);
1465            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1467          redo A;          redo A;
1468        } else {        } else {
1469          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1470            $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1478            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (46);
1485          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1487            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488              if ($self->{ct}->{attributes}) {
1489                !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491              } else {
1492                !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1506                                value => ''};          $self->{ca}
1507          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510            $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514            !!!cp (50);
1515            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1517          redo A;          redo A;
1518        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (52);
1522          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1524            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525              if ($self->{ct}->{attributes}) {
1526                !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1538    
1539          redo A;          redo A;
1540        } else {        } else {
1541          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1542                                value => ''};               0x0022 => 1, # "
1543          $self->{state} = 'attribute name';               0x0027 => 1, # '
1544                 0x003D => 1, # =
1545                }->{$self->{nc}}) {
1546              !!!cp (55);
1547              !!!parse-error (type => 'bad attribute name');
1548            } else {
1549              !!!cp (56);
1550            }
1551            $self->{ca}
1552                = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555            $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1564            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1565          } else {            ## Discard $self->{ca} # MUST
1566            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1567              = $self->{current_attribute};            !!!cp (58);
1568              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569                = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1574            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580            !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (61);
1589          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591            if ($self->{current_token}->{attributes}) {            !!!cp (62);
1592              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593              if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1608            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613            !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1617          redo A;          redo A;
1618        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (66);
1623          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1625            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626              if ($self->{ct}->{attributes}) {
1627                !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629              } else {
1630                ## NOTE: This state should never be reached.
1631                !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1644                $self->{nc} == 0x0027) { # '
1645              !!!cp (69);
1646              !!!parse-error (type => 'bad attribute name');
1647            } else {
1648              !!!cp (70);
1649            }
1650            $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1657            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662          $self->{state} = 'before attribute value';          !!!cp (72);
1663            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (73);
1669          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1671            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672              if ($self->{ct}->{attributes}) {
1673                !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675              } else {
1676                ## NOTE: This state should never be reached.
1677                !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1691                                value => ''};          $self->{ca}
1692          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695            $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699            !!!cp (77);
1700            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1702          redo A;          redo A;
1703        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (79);
1707          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1709            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710              if ($self->{ct}->{attributes}) {
1711                !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713              } else {
1714                ## NOTE: This state should never be reached.
1715                !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1728                                value => ''};              $self->{nc} == 0x0027) { # '
1729          $self->{state} = 'attribute name';            !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738            $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1744            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1750            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1755            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1760            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1765            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (87);
1767            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1769              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770              if ($self->{ct}->{attributes}) {
1771                !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773              } else {
1774                ## NOTE: This state should never be reached.
1775                !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (90);
1790          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1792            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793              if ($self->{ct}->{attributes}) {
1794                !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796              } else {
1797                ## NOTE: This state should never be reached.
1798                !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1811          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1812              !!!parse-error (type => 'bad attribute value');
1813            } else {
1814              !!!cp (94);
1815            }
1816            $self->{ca}->{value} .= chr ($self->{nc});
1817            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          $self->{state} = 'before attribute name';          !!!cp (95);
1824            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825          !!!next-input-character;          !!!next-input-character;
1826          redo A;          redo A;
1827        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1829          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1830            ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (97);
1842          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1844            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845              if ($self->{ct}->{attributes}) {
1846                !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848              } else {
1849                ## NOTE: This state should never be reached.
1850                !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1863            $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          $self->{state} = 'before attribute name';          !!!cp (101);
1875            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876            !!!next-input-character;
1877            redo A;
1878          } elsif ($self->{nc} == 0x0026) { # &
1879            !!!cp (102);
1880            ## NOTE: In the spec, the tokenizer is switched to the
1881            ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = 'attribute value (single-quoted)';  
         $self->{state} = 'entity in attribute value';  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (103);
1893          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1895            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896              if ($self->{ct}->{attributes}) {
1897                !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899              } else {
1900                ## NOTE: This state should never be reached.
1901                !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1914            $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1925            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1926            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1928            $self->{next_input_character} == 0x0020) { # SP          redo A;
1929          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1930          !!!next-input-character;          !!!cp (108);
1931          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1932        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1933          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1935          !!!next-input-character;          $self->{entity_add} = -1;
1936          redo A;          $self->{prev_state} = $self->{state};
1937        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1938          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1939            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          redo A;
1940          } elsif ($self->{current_token}->{type} eq 'end tag') {        } elsif ($self->{nc} == 0x003E) { # >
1941            $self->{content_model_flag} = 'PCDATA'; # MUST          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942            if ($self->{current_token}->{attributes}) {            !!!cp (109);
1943              $self->{last_stag_name} = $self->{ct}->{tag_name};
1944            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1945              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946              if ($self->{ct}->{attributes}) {
1947                !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949              } else {
1950                ## NOTE: This state should never be reached.
1951                !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (112);
1966          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1968            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969              if ($self->{ct}->{attributes}) {
1970                !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972              } else {
1973                ## NOTE: This state should never be reached.
1974                !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1983    
1984          redo A;          redo A;
1985        } else {        } else {
1986          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1987                 0x0022 => 1, # "
1988                 0x0027 => 1, # '
1989                 0x003D => 1, # =
1990                }->{$self->{nc}}) {
1991              !!!cp (115);
1992              !!!parse-error (type => 'bad attribute value');
1993            } else {
1994              !!!cp (116);
1995            }
1996            $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
2005      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        my $token = $self->_tokenize_attempt_to_consume_an_entity;        if ($is_space->{$self->{nc}}) {
2007            !!!cp (118);
2008            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009            !!!next-input-character;
2010            redo A;
2011          } elsif ($self->{nc} == 0x003E) { # >
2012            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013              !!!cp (119);
2014              $self->{last_stag_name} = $self->{ct}->{tag_name};
2015            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2016              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017              if ($self->{ct}->{attributes}) {
2018                !!!cp (120);
2019                !!!parse-error (type => 'end tag attribute');
2020              } else {
2021                ## NOTE: This state should never be reached.
2022                !!!cp (121);
2023              }
2024            } else {
2025              die "$0: $self->{ct}->{type}: Unknown token type";
2026            }
2027            $self->{state} = DATA_STATE;
2028            !!!next-input-character;
2029    
2030            !!!emit ($self->{ct}); # start tag or end tag
2031    
2032        unless (defined $token) {          redo A;
2033          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2034            !!!cp (122);
2035            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036            !!!next-input-character;
2037            redo A;
2038          } elsif ($self->{nc} == -1) {
2039            !!!parse-error (type => 'unclosed tag');
2040            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041              !!!cp (122.3);
2042              $self->{last_stag_name} = $self->{ct}->{tag_name};
2043            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044              if ($self->{ct}->{attributes}) {
2045                !!!cp (122.1);
2046                !!!parse-error (type => 'end tag attribute');
2047              } else {
2048                ## NOTE: This state should never be reached.
2049                !!!cp (122.2);
2050              }
2051            } else {
2052              die "$0: $self->{ct}->{type}: Unknown token type";
2053            }
2054            $self->{state} = DATA_STATE;
2055            ## Reconsume.
2056            !!!emit ($self->{ct}); # start tag or end tag
2057            redo A;
2058        } else {        } else {
2059          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2060          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2061            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2062            ## reconsume
2063            redo A;
2064        }        }
2065        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066          if ($self->{nc} == 0x003E) { # >
2067            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068              !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070              ## TODO: Different type than slash in start tag
2071              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072              if ($self->{ct}->{attributes}) {
2073                !!!cp ('124.4');
2074                !!!parse-error (type => 'end tag attribute');
2075              } else {
2076                !!!cp ('124.5');
2077              }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2085        # next-input-character is already done          !!!next-input-character;
       redo A;  
     } elsif ($self->{state} eq 'bogus comment') {  
       ## (only happen if PCDATA state)  
         
       my $token = {type => 'comment', data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = 'data';  
           !!!next-input-character;  
   
           !!!emit ($token);  
   
           redo A;  
         } elsif ($self->{next_input_character} == -1) {  
           $self->{state} = 'data';  
           ## reconsume  
2086    
2087            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;          redo A;
2090          } elsif ($self->{nc} == -1) {
2091            !!!parse-error (type => 'unclosed tag');
2092            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093              !!!cp (124.7);
2094              $self->{last_stag_name} = $self->{ct}->{tag_name};
2095            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096              if ($self->{ct}->{attributes}) {
2097                !!!cp (124.5);
2098                !!!parse-error (type => 'end tag attribute');
2099              } else {
2100                ## NOTE: This state should never be reached.
2101                !!!cp (124.6);
2102              }
2103          } else {          } else {
2104            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2105          }          }
2106        } # BC          $self->{state} = DATA_STATE;
2107      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2108            !!!emit ($self->{ct}); # start tag or end tag
2109            redo A;
2110          } else {
2111            !!!cp ('124.4');
2112            !!!parse-error (type => 'nestc');
2113            ## TODO: This error type is wrong.
2114            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2115            ## Reconsume.
2116            redo A;
2117          }
2118        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2120    
2121        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2123                
2124        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127          !!!next-input-character;          !!!next-input-character;
2128          push @next_char, $self->{next_input_character};  
2129          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2130            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2131            $self->{state} = 'comment';        } elsif ($self->{nc} == -1) {
2132            !!!next-input-character;          !!!cp (125);
2133            redo A;          $self->{state} = DATA_STATE;
2134          }          ## reconsume
2135        } elsif ($self->{next_input_character} == 0x0044 or # D  
2136                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2137            redo A;
2138          } else {
2139            !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            ## Stay in the state.
2146          !!!next-input-character;          !!!next-input-character;
2147          push @next_char, $self->{next_input_character};          redo A;
2148          if ($self->{next_input_character} == 0x004F or # O        }
2149              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150            !!!next-input-character;        ## (only happen if PCDATA state)
2151            push @next_char, $self->{next_input_character};        
2152            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2153                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2154              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2155              push @next_char, $self->{next_input_character};          !!!next-input-character;
2156              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2157                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2158                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2159                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2160                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2161                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2162                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2163                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2164                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2165                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2168                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2169                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2170                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2171                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2172                      !!!next-input-character;          redo A;
2173                      redo A;        } else {
2174                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment open');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180        $self->{state} = 'bogus comment';        ## Reconsume.
2181          $self->{state} = BOGUS_COMMENT_STATE;
2182          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190      } elsif ($self->{state} eq 'comment') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2192          $self->{state} = 'comment dash';                                    column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297        } elsif ($self->{state} == COMMENT_START_STATE) {
2298          if ($self->{nc} == 0x002D) { # -
2299            !!!cp (137);
2300            $self->{state} = COMMENT_START_DASH_STATE;
2301            !!!next-input-character;
2302            redo A;
2303          } elsif ($self->{nc} == 0x003E) { # >
2304            !!!cp (138);
2305            !!!parse-error (type => 'bogus comment');
2306            $self->{state} = DATA_STATE;
2307            !!!next-input-character;
2308    
2309            !!!emit ($self->{ct}); # comment
2310    
2311            redo A;
2312          } elsif ($self->{nc} == -1) {
2313            !!!cp (139);
2314            !!!parse-error (type => 'unclosed comment');
2315            $self->{state} = DATA_STATE;
2316            ## reconsume
2317    
2318            !!!emit ($self->{ct}); # comment
2319    
2320            redo A;
2321          } else {
2322            !!!cp (140);
2323            $self->{ct}->{data} # comment
2324                .= chr ($self->{nc});
2325            $self->{state} = COMMENT_STATE;
2326            !!!next-input-character;
2327            redo A;
2328          }
2329        } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330          if ($self->{nc} == 0x002D) { # -
2331            !!!cp (141);
2332            $self->{state} = COMMENT_END_STATE;
2333            !!!next-input-character;
2334            redo A;
2335          } elsif ($self->{nc} == 0x003E) { # >
2336            !!!cp (142);
2337            !!!parse-error (type => 'bogus comment');
2338            $self->{state} = DATA_STATE;
2339            !!!next-input-character;
2340    
2341            !!!emit ($self->{ct}); # comment
2342    
2343            redo A;
2344          } elsif ($self->{nc} == -1) {
2345            !!!cp (143);
2346            !!!parse-error (type => 'unclosed comment');
2347            $self->{state} = DATA_STATE;
2348            ## reconsume
2349    
2350            !!!emit ($self->{ct}); # comment
2351    
2352            redo A;
2353          } else {
2354            !!!cp (144);
2355            $self->{ct}->{data} # comment
2356                .= '-' . chr ($self->{nc});
2357            $self->{state} = COMMENT_STATE;
2358            !!!next-input-character;
2359            redo A;
2360          }
2361        } elsif ($self->{state} == COMMENT_STATE) {
2362          if ($self->{nc} == 0x002D) { # -
2363            !!!cp (145);
2364            $self->{state} = COMMENT_END_DASH_STATE;
2365          !!!next-input-character;          !!!next-input-character;
2366          redo A;          redo A;
2367        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2368            !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2378            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} eq 'comment dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389          $self->{state} = 'comment end';          !!!cp (148);
2390            $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2394            !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2404          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405            $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411          $self->{state} = 'data';          !!!cp (151);
2412            $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2420          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2421                            line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2428            !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2434    
2435          redo A;          redo A;
2436        } else {        } else {
2437          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2438          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2439          $self->{state} = 'comment';                          line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442            $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2448            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2449            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'before DOCTYPE name';  
2450          !!!next-input-character;          !!!next-input-character;
2451          redo A;          redo A;
2452        } else {        } else {
2453            !!!cp (156);
2454          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2455          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2456          ## reconsume          ## reconsume
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2461            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif (0x0061 <= $self->{next_input_character} and        } elsif ($self->{nc} == 0x003E) { # >
2466                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (158);
2467  ## ISSUE: "Set the token's name name to the" in the spec          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{current_token} = {type => 'DOCTYPE',          $self->{state} = DATA_STATE;
                           name => chr ($self->{next_input_character} - 0x0020),  
                           error => 1};  
         $self->{state} = 'DOCTYPE name';  
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471            !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == -1) {
2475            !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2478            ## reconsume
2479    
2480            !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482            redo A;
2483          } else {
2484            !!!cp (160);
2485            $self->{ct}->{name} = chr $self->{nc};
2486            delete $self->{ct}->{quirks};
2487            $self->{state} = DOCTYPE_NAME_STATE;
2488            !!!next-input-character;
2489            redo A;
2490          }
2491        } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492    ## ISSUE: Redundant "First," in the spec.
2493          if ($is_space->{$self->{nc}}) {
2494            !!!cp (161);
2495            $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496            !!!next-input-character;
2497            redo A;
2498          } elsif ($self->{nc} == 0x003E) { # >
2499            !!!cp (162);
2500            $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2507          !!!parse-error (type => 'no DOCTYPE name');          !!!cp (163);
2508          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
2509            $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          $self->{ct}->{quirks} = 1;
2513            !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          $self->{current_token} = {type => 'DOCTYPE',          !!!cp (164);
2518                            name => chr ($self->{next_input_character}),          $self->{ct}->{name}
2519                            error => 1};            .= chr ($self->{nc}); # DOCTYPE
2520  ## ISSUE: "Set the token's name name to the" in the spec          ## Stay in the state
         $self->{state} = 'DOCTYPE name';  
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2526            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
2527            $self->{next_input_character} == 0x000B or # VT          ## Stay in the state
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE  
         $self->{state} = 'after DOCTYPE name';  
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE          !!!cp (166);
2532          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536          undef $self->{current_token};  
2537            redo A;
2538          } elsif ($self->{nc} == -1) {
2539            !!!cp (167);
2540            !!!parse-error (type => 'unclosed DOCTYPE');
2541            $self->{state} = DATA_STATE;
2542            ## reconsume
2543    
2544            $self->{ct}->{quirks} = 1;
2545            !!!emit ($self->{ct}); # DOCTYPE
2546    
2547            redo A;
2548          } elsif ($self->{nc} == 0x0050 or # P
2549                   $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552            !!!next-input-character;
2553            redo A;
2554          } elsif ($self->{nc} == 0x0053 or # S
2555                   $self->{nc} == 0x0073) { # s
2556            $self->{state} = SYSTEM_STATE;
2557            $self->{s_kwd} = chr $self->{nc};
2558            !!!next-input-character;
2559            redo A;
2560          } else {
2561            !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566            !!!next-input-character;
2567            redo A;
2568          }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604            $self->{state} = BOGUS_DOCTYPE_STATE;
2605            ## Reconsume.
2606            redo A;
2607          }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643            $self->{state} = BOGUS_DOCTYPE_STATE;
2644            ## Reconsume.
2645          redo A;          redo A;
2646        } elsif (0x0061 <= $self->{next_input_character} and        }
2647                 $self->{next_input_character} <= 0x007A) { # a..z      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648          $self->{current_token}->{name} .= chr ($self->{next_input_character} - 0x0020); # DOCTYPE        if ($is_space->{$self->{nc}}) {
2649          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');          !!!cp (181);
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} eq 0x0022) { # "
2654            !!!cp (182);
2655            $self->{ct}->{pubid} = ''; # DOCTYPE
2656            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657            !!!next-input-character;
2658            redo A;
2659          } elsif ($self->{nc} eq 0x0027) { # '
2660            !!!cp (183);
2661            $self->{ct}->{pubid} = ''; # DOCTYPE
2662            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663            !!!next-input-character;
2664            redo A;
2665          } elsif ($self->{nc} eq 0x003E) { # >
2666            !!!cp (184);
2667            !!!parse-error (type => 'no PUBLIC literal');
2668    
2669            $self->{state} = DATA_STATE;
2670            !!!next-input-character;
2671    
2672            $self->{ct}->{quirks} = 1;
2673            !!!emit ($self->{ct}); # DOCTYPE
2674    
2675            redo A;
2676          } elsif ($self->{nc} == -1) {
2677            !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE  
2680          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2681            ## reconsume
2682    
2683            $self->{ct}->{quirks} = 1;
2684            !!!emit ($self->{ct}); # DOCTYPE
2685    
2686            redo A;
2687          } else {
2688            !!!cp (186);
2689            !!!parse-error (type => 'string after PUBLIC');
2690            $self->{ct}->{quirks} = 1;
2691    
2692            $self->{state} = BOGUS_DOCTYPE_STATE;
2693            !!!next-input-character;
2694            redo A;
2695          }
2696        } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697          if ($self->{nc} == 0x0022) { # "
2698            !!!cp (187);
2699            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700            !!!next-input-character;
2701            redo A;
2702          } elsif ($self->{nc} == 0x003E) { # >
2703            !!!cp (188);
2704            !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706            $self->{state} = DATA_STATE;
2707            !!!next-input-character;
2708    
2709            $self->{ct}->{quirks} = 1;
2710            !!!emit ($self->{ct}); # DOCTYPE
2711    
2712            redo A;
2713          } elsif ($self->{nc} == -1) {
2714            !!!cp (189);
2715            !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717            $self->{state} = DATA_STATE;
2718            ## reconsume
2719    
2720            $self->{ct}->{quirks} = 1;
2721            !!!emit ($self->{ct}); # DOCTYPE
2722    
2723            redo A;
2724          } else {
2725            !!!cp (190);
2726            $self->{ct}->{pubid} # DOCTYPE
2727                .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731            ## Stay in the state
2732            !!!next-input-character;
2733            redo A;
2734          }
2735        } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736          if ($self->{nc} == 0x0027) { # '
2737            !!!cp (191);
2738            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739            !!!next-input-character;
2740            redo A;
2741          } elsif ($self->{nc} == 0x003E) { # >
2742            !!!cp (192);
2743            !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745            $self->{state} = DATA_STATE;
2746            !!!next-input-character;
2747    
2748            $self->{ct}->{quirks} = 1;
2749            !!!emit ($self->{ct}); # DOCTYPE
2750    
2751            redo A;
2752          } elsif ($self->{nc} == -1) {
2753            !!!cp (193);
2754            !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756            $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          !!!emit ($self->{current_token});          $self->{ct}->{quirks} = 1;
2760          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          $self->{current_token}->{name}          !!!cp (194);
2765            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2766          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');              .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2776            $self->{next_input_character} == 0x000A or # LF          !!!cp (195);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x0022) { # "
2781          $self->{state} = 'data';          !!!cp (196);
2782            $self->{ct}->{sysid} = ''; # DOCTYPE
2783            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784            !!!next-input-character;
2785            redo A;
2786          } elsif ($self->{nc} == 0x0027) { # '
2787            !!!cp (197);
2788            $self->{ct}->{sysid} = ''; # DOCTYPE
2789            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790            !!!next-input-character;
2791            redo A;
2792          } elsif ($self->{nc} == 0x003E) { # >
2793            !!!cp (198);
2794            $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
         undef $self->{current_token};  
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2801            !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803          $self->{state} = 'data';  
2804            $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2808          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812          !!!parse-error (type => 'string after DOCTYPE name');          !!!cp (200);
2813          $self->{current_token}->{error} = 1; # DOCTYPE          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2815    
2816            $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ($self->{next_input_character} == 0x003E) { # >        if ($is_space->{$self->{nc}}) {
2822          $self->{state} = 'data';          !!!cp (201);
2823            ## Stay in the state
2824            !!!next-input-character;
2825            redo A;
2826          } elsif ($self->{nc} == 0x0022) { # "
2827            !!!cp (202);
2828            $self->{ct}->{sysid} = ''; # DOCTYPE
2829            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830            !!!next-input-character;
2831            redo A;
2832          } elsif ($self->{nc} == 0x0027) { # '
2833            !!!cp (203);
2834            $self->{ct}->{sysid} = ''; # DOCTYPE
2835            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836            !!!next-input-character;
2837            redo A;
2838          } elsif ($self->{nc} == 0x003E) { # >
2839            !!!cp (204);
2840            !!!parse-error (type => 'no SYSTEM literal');
2841            $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2845          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2849            !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851          $self->{state} = 'data';  
2852            $self->{state} = DATA_STATE;
2853            ## reconsume
2854    
2855            $self->{ct}->{quirks} = 1;
2856            !!!emit ($self->{ct}); # DOCTYPE
2857    
2858            redo A;
2859          } else {
2860            !!!cp (206);
2861            !!!parse-error (type => 'string after SYSTEM');
2862            $self->{ct}->{quirks} = 1;
2863    
2864            $self->{state} = BOGUS_DOCTYPE_STATE;
2865            !!!next-input-character;
2866            redo A;
2867          }
2868        } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869          if ($self->{nc} == 0x0022) { # "
2870            !!!cp (207);
2871            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872            !!!next-input-character;
2873            redo A;
2874          } elsif ($self->{nc} == 0x003E) { # >
2875            !!!cp (208);
2876            !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878            $self->{state} = DATA_STATE;
2879            !!!next-input-character;
2880    
2881            $self->{ct}->{quirks} = 1;
2882            !!!emit ($self->{ct}); # DOCTYPE
2883    
2884            redo A;
2885          } elsif ($self->{nc} == -1) {
2886            !!!cp (209);
2887            !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889            $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2893          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897            !!!cp (210);
2898            $self->{ct}->{sysid} # DOCTYPE
2899                .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } else {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        die "$0: $self->{state}: Unknown state";        if ($self->{nc} == 0x0027) { # '
2909      }          !!!cp (211);
2910    } # A            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911            !!!next-input-character;
2912            redo A;
2913          } elsif ($self->{nc} == 0x003E) { # >
2914            !!!cp (212);
2915            !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917    die "$0: _get_next_token: unexpected case";          $self->{state} = DATA_STATE;
2918  } # _get_next_token          !!!next-input-character;
2919    
2920  sub _tokenize_attempt_to_consume_an_entity ($) {          $self->{ct}->{quirks} = 1;
2921    my $self = shift;          !!!emit ($self->{ct}); # DOCTYPE
     
   if ($self->{next_input_character} == 0x0023) { # #  
     !!!next-input-character;  
     if ($self->{next_input_character} == 0x0078 or # x  
         $self->{next_input_character} == 0x0058) { # X  
       my $num;  
       X: {  
         my $x_char = $self->{next_input_character};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_input_character} and  
             $self->{next_input_character} <= 0x0039) { # 0..9  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           ## ISSUE: the spec says U+0078, which is apparently incorrect  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           ## ISSUE: the spec says U+0058, which is apparently incorrect  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $num) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           $self->{next_input_character} = 0x0023; # #  
           !!!back-next-input-character ($x_char);  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
         } else {  
           !!!parse-error (type => 'no refc');  
         }  
2922    
2923          ## TODO: check the definition for |a valid Unicode character|.          redo A;
2924          ## <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8189>        } elsif ($self->{nc} == -1) {
2925          if ($num > 1114111 or $num == 0) {          !!!cp (213);
2926            $num = 0xFFFD; # REPLACEMENT CHARACTER          !!!parse-error (type => 'unclosed SYSTEM literal');
2927            ## ISSUE: Why this is not an error?  
2928          } elsif (0x80 <= $num and $num <= 0x9F) {          $self->{state} = DATA_STATE;
2929            ## NOTE: <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8562>          ## reconsume
2930            ## ISSUE: Not in the spec yet; parse error?  
2931            $num = $c1_entity_char->{$num};          $self->{ct}->{quirks} = 1;
2932          }          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          return {type => 'character', data => chr $num};          redo A;
2935        } # X        } else {
2936      } elsif (0x0030 <= $self->{next_input_character} and          !!!cp (214);
2937               $self->{next_input_character} <= 0x0039) { # 0..9          $self->{ct}->{sysid} # DOCTYPE
2938        my $code = $self->{next_input_character} - 0x0030;              .= chr $self->{nc};
2939        !!!next-input-character;          $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942            ## Stay in the state
2943            !!!next-input-character;
2944            redo A;
2945          }
2946        } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947          if ($is_space->{$self->{nc}}) {
2948            !!!cp (215);
2949            ## Stay in the state
2950            !!!next-input-character;
2951            redo A;
2952          } elsif ($self->{nc} == 0x003E) { # >
2953            !!!cp (216);
2954            $self->{state} = DATA_STATE;
2955            !!!next-input-character;
2956    
2957            !!!emit ($self->{ct}); # DOCTYPE
2958    
2959            redo A;
2960          } elsif ($self->{nc} == -1) {
2961            !!!cp (217);
2962            !!!parse-error (type => 'unclosed DOCTYPE');
2963            $self->{state} = DATA_STATE;
2964            ## reconsume
2965    
2966            $self->{ct}->{quirks} = 1;
2967            !!!emit ($self->{ct}); # DOCTYPE
2968    
2969            redo A;
2970          } else {
2971            !!!cp (218);
2972            !!!parse-error (type => 'string after SYSTEM literal');
2973            #$self->{ct}->{quirks} = 1;
2974    
2975            $self->{state} = BOGUS_DOCTYPE_STATE;
2976            !!!next-input-character;
2977            redo A;
2978          }
2979        } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980          if ($self->{nc} == 0x003E) { # >
2981            !!!cp (219);
2982            $self->{state} = DATA_STATE;
2983            !!!next-input-character;
2984    
2985            !!!emit ($self->{ct}); # DOCTYPE
2986    
2987            redo A;
2988          } elsif ($self->{nc} == -1) {
2989            !!!cp (220);
2990            !!!parse-error (type => 'unclosed DOCTYPE');
2991            $self->{state} = DATA_STATE;
2992            ## reconsume
2993    
2994            !!!emit ($self->{ct}); # DOCTYPE
2995    
2996            redo A;
2997          } else {
2998            !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002            ## Stay in the state
3003            !!!next-input-character;
3004            redo A;
3005          }
3006        } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007          ## NOTE: "CDATA section state" in the state is jointly implemented
3008          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009          ## and |CDATA_SECTION_MSE2_STATE|.
3010                
3011        while (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x005D) { # ]
3012                  $self->{next_input_character} <= 0x0039) { # 0..9          !!!cp (221.1);
3013          $code *= 10;          $self->{state} = CDATA_SECTION_MSE1_STATE;
3014          $code += $self->{next_input_character} - 0x0030;          !!!next-input-character;
3015                    redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018          !!!next-input-character;          !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034            ## Stay in the state.
3035            !!!next-input-character;
3036            redo A;
3037        }        }
3038    
3039        if ($self->{next_input_character} == 0x003B) { # ;        ## ISSUE: "text tokens" in spec.
3040        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041          if ($self->{nc} == 0x005D) { # ]
3042            !!!cp (221.5);
3043            $self->{state} = CDATA_SECTION_MSE2_STATE;
3044          !!!next-input-character;          !!!next-input-character;
3045            redo A;
3046        } else {        } else {
3047          !!!parse-error (type => 'no refc');          !!!cp (221.6);
3048            $self->{ct}->{data} .= ']';
3049            $self->{state} = CDATA_SECTION_STATE;
3050            ## Reconsume.
3051            redo A;
3052          }
3053        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if ($self->{nc} == 0x003E) { # >
3055            $self->{state} = DATA_STATE;
3056            !!!next-input-character;
3057            if (length $self->{ct}->{data}) { # character
3058              !!!cp (221.7);
3059              !!!emit ($self->{ct}); # character
3060            } else {
3061              !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063            }
3064            redo A;
3065          } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!cp (221.9); # character
3067            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068            ## Stay in the state.
3069            !!!next-input-character;
3070            redo A;
3071          } else {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112        }        }
3113    
3114        ## TODO: check the definition for |a valid Unicode character|.        ## NOTE: No character is consumed by the "consume a character
3115        if ($code > 1114111 or $code == 0) {        ## reference" algorithm.  In other word, there is an "&" character
3116          $code = 0xFFFD; # REPLACEMENT CHARACTER        ## that does not introduce a character reference, which would be
3117          ## ISSUE: Why this is not an error?        ## appended to the parent element or the attribute value in later
3118        } elsif (0x80 <= $code and $code <= 0x9F) {        ## process of the tokenizer.
3119          ## NOTE: <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8562>  
3120          ## ISSUE: Not in the spec yet; parse error?        if ($self->{prev_state} == DATA_STATE) {
3121          $code = $c1_entity_char->{$code};          !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177        }        }
3178              } elsif ($self->{state} == NCR_NUM_STATE) {
3179        return {type => 'character', data => chr $code};        if (0x0030 <= $self->{nc} and
3180      } else {            $self->{nc} <= 0x0039) { # 0..9
3181        !!!parse-error (type => 'bare nero');          !!!cp (1012);
3182        !!!back-next-input-character ($self->{next_input_character});          $self->{s_kwd} *= 10;
3183        $self->{next_input_character} = 0x0023; # #          $self->{s_kwd} += $self->{nc} - 0x0030;
3184        return undef;          
3185      }          ## Stay in the state.
3186    } elsif ((0x0041 <= $self->{next_input_character} and          !!!next-input-character;
3187              $self->{next_input_character} <= 0x005A) or          redo A;
3188             (0x0061 <= $self->{next_input_character} and        } elsif ($self->{nc} == 0x003B) { # ;
3189              $self->{next_input_character} <= 0x007A)) {          !!!cp (1013);
3190      my $entity_name = chr $self->{next_input_character};          !!!next-input-character;
3191      !!!next-input-character;          #
   
     my $value = $entity_name;  
     my $match;  
   
     while (length $entity_name < 10 and  
            ## NOTE: Some number greater than the maximum length of entity name  
            ((0x0041 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x005A) or  
             (0x0061 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x007A) or  
             (0x0030 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x0039))) {  
       $entity_name .= chr $self->{next_input_character};  
       if (defined $entity_char->{$entity_name}) {  
         $value = $entity_char->{$entity_name};  
         $match = 1;  
3192        } else {        } else {
3193          $value .= chr $self->{next_input_character};          !!!cp (1014);
3194            !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198        !!!next-input-character;  
3199      }        my $code = $self->{s_kwd};
3200              my $l = $self->{line_prev};
3201      if ($match) {        my $c = $self->{column_prev};
3202        if ($self->{next_input_character} == 0x003B) { # ;        if ($charref_map->{$code}) {
3203            !!!cp (1015);
3204            !!!parse-error (type => 'invalid character reference',
3205                            text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208          } elsif ($code > 0x10FFFF) {
3209            !!!cp (1016);
3210            !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213            $code = 0xFFFD;
3214          }
3215    
3216          if ($self->{prev_state} == DATA_STATE) {
3217            !!!cp (992);
3218            $self->{state} = $self->{prev_state};
3219            ## Reconsume.
3220            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221                      line => $l, column => $c,
3222                     });
3223            redo A;
3224          } else {
3225            !!!cp (991);
3226            $self->{ca}->{value} .= chr $code;
3227            $self->{ca}->{has_reference} = 1;
3228            $self->{state} = $self->{prev_state};
3229            ## Reconsume.
3230            redo A;
3231          }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237            !!!cp (990);
3238            $self->{state} = HEXREF_HEX_STATE;
3239            $self->{s_kwd} = 0;
3240            ## Reconsume.
3241            redo A;
3242          } else {
3243            !!!parse-error (type => 'bare hcro',
3244                            line => $self->{line_prev},
3245                            column => $self->{column_prev} - 2);
3246    
3247            ## NOTE: According to the spec algorithm, nothing is returned,
3248            ## and then "&#" followed by "X" or "x" is appended to the parent
3249            ## element or the attribute value in the later processing.
3250    
3251            if ($self->{prev_state} == DATA_STATE) {
3252              !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261            } else {
3262              !!!cp (989);
3263              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264              $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296          !!!next-input-character;          !!!next-input-character;
3297            #
3298        } else {        } else {
3299          !!!parse-error (type => 'refc');          !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316          } elsif ($code > 0x10FFFF) {
3317            !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321            $code = 0xFFFD;
3322          }
3323    
3324          if ($self->{prev_state} == DATA_STATE) {
3325            !!!cp (988);
3326            $self->{state} = $self->{prev_state};
3327            ## Reconsume.
3328            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329                      line => $l, column => $c,
3330                     });
3331            redo A;
3332          } else {
3333            !!!cp (987);
3334            $self->{ca}->{value} .= chr $code;
3335            $self->{ca}->{has_reference} = 1;
3336            $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338            redo A;
3339          }
3340        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341          if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343              ((0x0041 <= $self->{nc} and # a
3344                $self->{nc} <= 0x005A) or # x
3345               (0x0061 <= $self->{nc} and # a
3346                $self->{nc} <= 0x007A) or # z
3347               (0x0030 <= $self->{nc} and # 0
3348                $self->{nc} <= 0x0039) or # 9
3349               $self->{nc} == 0x003B)) { # ;
3350            our $EntityChar;
3351            $self->{s_kwd} .= chr $self->{nc};
3352            if (defined $EntityChar->{$self->{s_kwd}}) {
3353              if ($self->{nc} == 0x003B) { # ;
3354                !!!cp (1020);
3355                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356                $self->{entity__match} = 1;
3357                !!!next-input-character;
3358                #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367            } else {
3368              !!!cp (1022);
3369              $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372              !!!next-input-character;
3373              redo A;
3374            }
3375        }        }
3376    
3377        return {type => 'character', data => $value};        my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396            }
3397          } else {
3398            !!!cp (1026);
3399            !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433          }
3434      } else {      } else {
3435        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       !!!back-token ({type => 'character', data => $value});  
       return undef;  
3436      }      }
3437    } else {    } # A  
3438      ## no characters are consumed  
3439      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3440      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 1636  sub _initialize_tree_constructor ($) { Line 3445  sub _initialize_tree_constructor ($) {
3445    $self->{document}->strict_error_checking (0);    $self->{document}->strict_error_checking (0);
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    ## TODO: Mark the Document as an HTML document # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1663  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474        
3475    !!!next-token;    !!!next-token;
3476    
   $self->{insertion_mode} = 'before head';  
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
3482      ## NOTE: The "initial" insertion mode.
3483    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3484    
3485      ## NOTE: The "before html" insertion mode.
3486    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3487      $self->{insertion_mode} = BEFORE_HEAD_IM;
3488    
3489      ## NOTE: The "before head" insertion mode and so on.
3490    $self->_tree_construction_main;    $self->_tree_construction_main;
3491  } # _construct_tree  } # _construct_tree
3492    
3493  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3494    my $self = shift;    my $self = shift;
3495    B: {  
3496        if ($token->{type} eq 'DOCTYPE') {    ## NOTE: "initial" insertion mode
3497          if ($token->{error}) {  
3498            ## ISSUE: Spec currently left this case undefined.    INITIAL: {
3499            !!!parse-error (type => 'bogus DOCTYPE');      if ($token->{type} == DOCTYPE_TOKEN) {
3500          }        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3501          my $doctype = $self->{document}->create_document_type_definition        ## error, switch to a conformance checking mode for another
3502            ($token->{name});        ## language.
3503          $self->{document}->append_child ($doctype);        my $doctype_name = $token->{name};
3504          #$phase = 'root element';        $doctype_name = '' unless defined $doctype_name;
3505          !!!next-token;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506          #redo B;        if (not defined $token->{name} or # <!DOCTYPE>
3507          return;            defined $token->{sysid}) {
3508        } elsif ({          !!!cp ('t1');
3509                  comment => 1,          !!!parse-error (type => 'not HTML5', token => $token);
3510                  'start tag' => 1,        } elsif ($doctype_name ne 'HTML') {
3511                  'end tag' => 1,          !!!cp ('t2');
3512                  'end-of-file' => 1,          !!!parse-error (type => 'not HTML5', token => $token);
3513                 }->{$token->{type}}) {        } elsif (defined $token->{pubid}) {
3514          ## ISSUE: Spec currently left this case undefined.          if ($token->{pubid} eq 'XSLT-compat') {
3515          !!!parse-error (type => 'missing DOCTYPE');            !!!cp ('t1.2');
3516          #$phase = 'root element';            !!!parse-error (type => 'XSLT-compat', token => $token,
3517          ## reprocess                            level => $self->{level}->{should});
3518          #redo B;          } else {
3519          return;            !!!parse-error (type => 'not HTML5', token => $token);
3520        } elsif ($token->{type} eq 'character') {          }
3521          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {        } else {
3522            $self->{document}->manakai_append_text ($1);          !!!cp ('t3');
3523            ## ISSUE: DOM3 Core does not allow Document > Text          #
3524            unless (length $token->{data}) {        }
3525              ## Stay in the phase        
3526              !!!next-token;        my $doctype = $self->{document}->create_document_type_definition
3527              redo B;          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528          ## NOTE: Default value for both |public_id| and |system_id| attributes
3529          ## are empty strings, so that we don't set any value in missing cases.
3530          $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531          $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532          ## NOTE: Other DocumentType attributes are null or empty lists.
3533          ## ISSUE: internalSubset = null??
3534          $self->{document}->append_child ($doctype);
3535          
3536          if ($token->{quirks} or $doctype_name ne 'HTML') {
3537            !!!cp ('t4');
3538            $self->{document}->manakai_compat_mode ('quirks');
3539          } elsif (defined $token->{pubid}) {
3540            my $pubid = $token->{pubid};
3541            $pubid =~ tr/a-z/A-z/;
3542            my $prefix = [
3543              "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544              "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545              "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546              "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547              "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548              "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549              "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550              "-//IETF//DTD HTML 2.0 STRICT//",
3551              "-//IETF//DTD HTML 2.0//",
3552              "-//IETF//DTD HTML 2.1E//",
3553              "-//IETF//DTD HTML 3.0//",
3554              "-//IETF//DTD HTML 3.2 FINAL//",
3555              "-//IETF//DTD HTML 3.2//",
3556              "-//IETF//DTD HTML 3//",
3557              "-//IETF//DTD HTML LEVEL 0//",
3558              "-//IETF//DTD HTML LEVEL 1//",
3559              "-//IETF//DTD HTML LEVEL 2//",
3560              "-//IETF//DTD HTML LEVEL 3//",
3561              "-//IETF//DTD HTML STRICT LEVEL 0//",
3562              "-//IETF//DTD HTML STRICT LEVEL 1//",
3563              "-//IETF//DTD HTML STRICT LEVEL 2//",
3564              "-//IETF//DTD HTML STRICT LEVEL 3//",
3565              "-//IETF//DTD HTML STRICT//",
3566              "-//IETF//DTD HTML//",
3567              "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574              "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575              "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576              "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579              "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580              "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581              "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582              "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585              "-//W3C//DTD HTML 3 1995-03-24//",
3586              "-//W3C//DTD HTML 3.2 DRAFT//",
3587              "-//W3C//DTD HTML 3.2 FINAL//",
3588              "-//W3C//DTD HTML 3.2//",
3589              "-//W3C//DTD HTML 3.2S DRAFT//",
3590              "-//W3C//DTD HTML 4.0 FRAMESET//",
3591              "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592              "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593              "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594              "-//W3C//DTD W3 HTML//",
3595              "-//W3O//DTD W3 HTML 3.0//",
3596              "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597              "-//WEBTECHS//DTD MOZILLA HTML//",
3598            ]; # $prefix
3599            my $match;
3600            for (@$prefix) {
3601              if (substr ($prefix, 0, length $_) eq $_) {
3602                $match = 1;
3603                last;
3604              }
3605            }
3606            if ($match or
3607                $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608                $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609                $pubid eq "HTML") {
3610              !!!cp ('t5');
3611              $self->{document}->manakai_compat_mode ('quirks');
3612            } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614              if (defined $token->{sysid}) {
3615                !!!cp ('t6');
3616                $self->{document}->manakai_compat_mode ('quirks');
3617              } else {
3618                !!!cp ('t7');
3619                $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621            } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623              !!!cp ('t8');
3624              $self->{document}->manakai_compat_mode ('limited quirks');
3625            } else {
3626              !!!cp ('t9');
3627            }
3628          } else {
3629            !!!cp ('t10');
3630          }
3631          if (defined $token->{sysid}) {
3632            my $sysid = $token->{sysid};
3633            $sysid =~ tr/A-Z/a-z/;
3634            if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637              $self->{document}->manakai_compat_mode ('quirks');
3638              !!!cp ('t11');
3639            } else {
3640              !!!cp ('t12');
3641            }
3642          } else {
3643            !!!cp ('t13');
3644          }
3645          
3646          ## Go to the "before html" insertion mode.
3647          !!!next-token;
3648          return;
3649        } elsif ({
3650                  START_TAG_TOKEN, 1,
3651                  END_TAG_TOKEN, 1,
3652                  END_OF_FILE_TOKEN, 1,
3653                 }->{$token->{type}}) {
3654          !!!cp ('t14');
3655          !!!parse-error (type => 'no DOCTYPE', token => $token);
3656          $self->{document}->manakai_compat_mode ('quirks');
3657          ## Go to the "before html" insertion mode.
3658          ## reprocess
3659          !!!ack-later;
3660          return;
3661        } elsif ($token->{type} == CHARACTER_TOKEN) {
3662          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663            ## Ignore the token
3664    
3665            unless (length $token->{data}) {
3666              !!!cp ('t15');
3667              ## Stay in the insertion mode.
3668              !!!next-token;
3669              redo INITIAL;
3670            } else {
3671              !!!cp ('t16');
3672          }          }
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error (type => 'missing DOCTYPE');  
         #$phase = 'root element';  
         ## reprocess  
         #redo B;  
         return;  
3673        } else {        } else {
3674          die "$0: $token->{type}: Unknown token";          !!!cp ('t17');
3675        }        }
3676      } # B  
3677          !!!parse-error (type => 'no DOCTYPE', token => $token);
3678          $self->{document}->manakai_compat_mode ('quirks');
3679          ## Go to the "before html" insertion mode.
3680          ## reprocess
3681          return;
3682        } elsif ($token->{type} == COMMENT_TOKEN) {
3683          !!!cp ('t18');
3684          my $comment = $self->{document}->create_comment ($token->{data});
3685          $self->{document}->append_child ($comment);
3686          
3687          ## Stay in the insertion mode.
3688          !!!next-token;
3689          redo INITIAL;
3690        } else {
3691          die "$0: $token->{type}: Unknown token type";
3692        }
3693      } # INITIAL
3694    
3695      die "$0: _tree_construction_initial: This should be never reached";
3696  } # _tree_construction_initial  } # _tree_construction_initial
3697    
3698  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3699    my $self = shift;    my $self = shift;
3700    
3701      ## NOTE: "before html" insertion mode.
3702        
3703    B: {    B: {
3704        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3706            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the phase          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
3710          redo B;          redo B;
3711        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3712            !!!cp ('t20');
3713          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3714          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3715          ## Stay in the phase          ## Stay in the insertion mode.
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            $self->{document}->manakai_append_text ($1);            ## Ignore the token.
3721            ## ISSUE: DOM3 Core does not allow Document > Text  
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
3723              ## Stay in the phase              !!!cp ('t21');
3724                ## Stay in the insertion mode.
3725              !!!next-token;              !!!next-token;
3726              redo B;              redo B;
3727              } else {
3728                !!!cp ('t22');
3729            }            }
3730            } else {
3731              !!!cp ('t23');
3732          }          }
3733    
3734            $self->{application_cache_selection}->(undef);
3735    
3736          #          #
3737          } elsif ($token->{type} == START_TAG_TOKEN) {
3738            if ($token->{tag_name} eq 'html') {
3739              my $root_element;
3740              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741              $self->{document}->append_child ($root_element);
3742              push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745              if ($token->{attributes}->{manifest}) {
3746                !!!cp ('t24');
3747                $self->{application_cache_selection}
3748                    ->($token->{attributes}->{manifest}->{value});
3749                ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753              } else {
3754                !!!cp ('t25');
3755                $self->{application_cache_selection}->(undef);
3756              }
3757    
3758              !!!nack ('t25c');
3759    
3760              !!!next-token;
3761              return; ## Go to the "before head" insertion mode.
3762            } else {
3763              !!!cp ('t25.1');
3764              #
3765            }
3766        } elsif ({        } elsif ({
3767                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3768                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3769                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3770          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3771          #          #
3772        } else {        } else {
3773          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775        my $root_element; !!!create-element ($root_element, 'html');  
3776        $self->{document}->append_child ($root_element);      my $root_element;
3777        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778        #$phase = 'main';      $self->{document}->append_child ($root_element);
3779        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780        #redo B;  
3781        return;      $self->{application_cache_selection}->(undef);
3782    
3783        ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785        return; ## Go to the "before head" insertion mode.
3786    } # B    } # B
3787    
3788      die "$0: _tree_construction_root_element: This should never be reached";
3789  } # _tree_construction_root_element  } # _tree_construction_root_element
3790    
3791  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 1782  sub _reset_insertion_mode ($) { Line 3800  sub _reset_insertion_mode ($) {
3800            
3801      ## Step 3      ## Step 3
3802      S3: {      S3: {
3803        $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3804        if (defined $self->{inner_html_node}) {          $last = 1;
3805          if ($self->{inner_html_node}->[1] eq 'td' or          if (defined $self->{inner_html_node}) {
3806              $self->{inner_html_node}->[1] eq 'th') {            !!!cp ('t28');
3807              $node = $self->{inner_html_node};
3808            } else {
3809              die "_reset_insertion_mode: t27";
3810            }
3811          }
3812          
3813          ## Step 4..14
3814          my $new_mode;
3815          if ($node->[1] & FOREIGN_EL) {
3816            !!!cp ('t28.1');
3817            ## NOTE: Strictly spaking, the line below only applies to MathML and
3818            ## SVG elements.  Currently the HTML syntax supports only MathML and
3819            ## SVG elements as foreigners.
3820            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3821          } elsif ($node->[1] & TABLE_CELL_EL) {
3822            if ($last) {
3823              !!!cp ('t28.2');
3824            #            #
3825          } else {          } else {
3826            $node = $self->{inner_html_node};            !!!cp ('t28.3');
3827              $new_mode = IN_CELL_IM;
3828          }          }
3829          } else {
3830            !!!cp ('t28.4');
3831            $new_mode = {
3832                          select => IN_SELECT_IM,
3833                          ## NOTE: |option| and |optgroup| do not set
3834                          ## insertion mode to "in select" by themselves.
3835                          tr => IN_ROW_IM,
3836                          tbody => IN_TABLE_BODY_IM,
3837                          thead => IN_TABLE_BODY_IM,
3838                          tfoot => IN_TABLE_BODY_IM,
3839                          caption => IN_CAPTION_IM,
3840                          colgroup => IN_COLUMN_GROUP_IM,
3841                          table => IN_TABLE_IM,
3842                          head => IN_BODY_IM, # not in head!
3843                          body => IN_BODY_IM,
3844                          frameset => IN_FRAMESET_IM,
3845                         }->{$node->[0]->manakai_local_name};
3846        }        }
       
       ## Step 4..13  
       my $new_mode = {  
                       select => 'in select',  
                       td => 'in cell',  
                       th => 'in cell',  
                       tr => 'in row',  
                       tbody => 'in table body',  
                       thead => 'in table head',  
                       tfoot => 'in table foot',  
                       caption => 'in caption',  
                       colgroup => 'in column group',  
                       table => 'in table',  
                       head => 'in body', # not in head!  
                       body => 'in body',  
                       frameset => 'in frameset',  
                      }->{$node->[1]};  
3847        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3848                
3849        ## Step 14        ## Step 15
3850        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3851          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3852            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3853              $self->{insertion_mode} = BEFORE_HEAD_IM;
3854          } else {          } else {
3855            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3856              !!!cp ('t30');
3857              $self->{insertion_mode} = AFTER_HEAD_IM;
3858          }          }
3859          return;          return;
3860          } else {
3861            !!!cp ('t31');
3862        }        }
3863                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3864        ## Step 16        ## Step 16
3865          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3866          
3867          ## Step 17
3868        $i--;        $i--;
3869        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3870                
3871        ## Step 17        ## Step 18
3872        redo S3;        redo S3;
3873      } # S3      } # S3
3874    
3875      die "$0: _reset_insertion_mode: This line should never be reached";
3876  } # _reset_insertion_mode  } # _reset_insertion_mode
3877    
3878  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3879    my $self = shift;    my $self = shift;
3880    
   my $phase = 'main';  
   
3881    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3882    
3883    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 1853  sub _tree_construction_main ($) { Line 3894  sub _tree_construction_main ($) {
3894      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3895      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3896        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3897            !!!cp ('t32');
3898          return;          return;
3899        }        }
3900      }      }
# Line 1867  sub _tree_construction_main ($) { Line 3909  sub _tree_construction_main ($) {
3909    
3910        ## Step 6        ## Step 6
3911        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3912            !!!cp ('t33_1');
3913          #          #
3914        } else {        } else {
3915          my $in_open_elements;          my $in_open_elements;
3916          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3917            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3918                !!!cp ('t33');
3919              $in_open_elements = 1;              $in_open_elements = 1;
3920              last OE;              last OE;
3921            }            }
3922          }          }
3923          if ($in_open_elements) {          if ($in_open_elements) {
3924              !!!cp ('t34');
3925            #            #
3926          } else {          } else {
3927              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3928              !!!cp ('t35');
3929            redo S4;            redo S4;
3930          }          }
3931        }        }
# Line 1901  sub _tree_construction_main ($) { Line 3948  sub _tree_construction_main ($) {
3948    
3949        ## Step 11        ## Step 11
3950        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3951            !!!cp ('t36');
3952          ## Step 7'          ## Step 7'
3953          $i++;          $i++;
3954          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3955                    
3956          redo S7;          redo S7;
3957        }        }
3958    
3959          !!!cp ('t37');
3960      } # S7      } # S7
3961    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3962    
3963    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3964      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3965        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3966            !!!cp ('t38');
3967          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3968          return;          return;
3969        }        }
3970      }      }
3971    
3972        !!!cp ('t39');
3973    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3974    
3975    my $style_start_tag = sub {    my $insert;
3976      my $style_el; !!!create-element ($style_el, 'style', $token->{attributes});  
3977      ## $self->{insertion_mode} eq 'in head' and ... (always true)    my $parse_rcdata = sub ($) {
3978      (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})      my ($content_model_flag) = @_;
3979       ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
3980        ->append_child ($style_el);      ## Step 1
3981      $self->{content_model_flag} = 'CDATA';      my $start_tag_name = $token->{tag_name};
3982                      my $el;
3983        !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3984    
3985        ## Step 2
3986        $insert->($el);
3987    
3988        ## Step 3
3989        $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3990        delete $self->{escape}; # MUST
3991    
3992        ## Step 4
3993      my $text = '';      my $text = '';
3994        !!!nack ('t40.1');
3995      !!!next-token;      !!!next-token;
3996      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3997          !!!cp ('t40');
3998        $text .= $token->{data};        $text .= $token->{data};
3999        !!!next-token;        !!!next-token;
4000      } # stop if non-character token or tokenizer stops tokenising      }
4001    
4002        ## Step 5
4003      if (length $text) {      if (length $text) {
4004        $style_el->manakai_append_text ($text);        !!!cp ('t41');
4005          my $text = $self->{document}->create_text_node ($text);
4006          $el->append_child ($text);
4007      }      }
4008        
4009      $self->{content_model_flag} = 'PCDATA';      ## Step 6
4010                      $self->{content_model} = PCDATA_CONTENT_MODEL;
4011      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {  
4012        ## Step 7
4013        if ($token->{type} == END_TAG_TOKEN and
4014            $token->{tag_name} eq $start_tag_name) {
4015          !!!cp ('t42');
4016        ## Ignore the token        ## Ignore the token
4017      } else {      } else {
4018        !!!parse-error (type => 'in CDATA:#'.$token->{type});        ## NOTE: An end-of-file token.
4019        ## ISSUE: And ignore?        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4020            !!!cp ('t43');
4021            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4022          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4023            !!!cp ('t44');
4024            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4025          } else {
4026            die "$0: $content_model_flag in parse_rcdata";
4027          }
4028      }      }
4029      !!!next-token;      !!!next-token;
4030    }; # $style_start_tag    }; # $parse_rcdata
4031    
4032    my $script_start_tag = sub {    my $script_start_tag = sub () {
4033      my $script_el;      my $script_el;
4034      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4035      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4036    
4037      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
4038        delete $self->{escape}; # MUST
4039            
4040      my $text = '';      my $text = '';
4041        !!!nack ('t45.1');
4042      !!!next-token;      !!!next-token;
4043      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
4044          !!!cp ('t45');
4045        $text .= $token->{data};        $text .= $token->{data};
4046        !!!next-token;        !!!next-token;
4047      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4048      if (length $text) {      if (length $text) {
4049          !!!cp ('t46');
4050        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4051      }      }
4052                                
4053      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
4054    
4055      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
4056          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4057          !!!cp ('t47');
4058        ## Ignore the token        ## Ignore the token
4059      } else {      } else {
4060        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4061          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4062        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4063        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4064      }      }
4065            
4066      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4067          !!!cp ('t49');
4068        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4069      } else {      } else {
4070          !!!cp ('t50');
4071        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4072        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4073          
4074        (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})        $insert->($script_el);
        ? $self->{head_element} : $self->{open_elements}->[-1]->[0])->append_child ($script_el);  
4075                
4076        ## TODO: insertion point = $old_insertion_point (might be "undefined")        ## TODO: insertion point = $old_insertion_point (might be "undefined")
4077                
# Line 1993  sub _tree_construction_main ($) { Line 4081  sub _tree_construction_main ($) {
4081      !!!next-token;      !!!next-token;
4082    }; # $script_start_tag    }; # $script_start_tag
4083    
4084      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4085      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4086      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4087    
4088    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4089      my $tag_name = shift;      my $end_tag_token = shift;
4090        my $tag_name = $end_tag_token->{tag_name};
4091    
4092        ## NOTE: The adoption agency algorithm (AAA).
4093    
4094      FET: {      FET: {
4095        ## Step 1        ## Step 1
4096        my $formatting_element;        my $formatting_element;
4097        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4098        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4099          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4100              !!!cp ('t52');
4101              last AFE;
4102            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4103                         eq $tag_name) {
4104              !!!cp ('t51');
4105            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4106            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4107            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4108          }          }
4109        } # AFE        } # AFE
4110        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4111          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4112            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4113          ## Ignore the token          ## Ignore the token
4114          !!!next-token;          !!!next-token;
4115          return;          return;
# Line 2022  sub _tree_construction_main ($) { Line 4121  sub _tree_construction_main ($) {
4121          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4122          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4123            if ($in_scope) {            if ($in_scope) {
4124                !!!cp ('t54');
4125              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4126              last INSCOPE;              last INSCOPE;
4127            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4128              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4129                !!!parse-error (type => 'unmatched end tag',
4130                                text => $token->{tag_name},
4131                                token => $end_tag_token);
4132              ## Ignore the token              ## Ignore the token
4133              !!!next-token;              !!!next-token;
4134              return;              return;
4135            }            }
4136          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4137                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4138            $in_scope = 0;            $in_scope = 0;
4139          }          }
4140        } # INSCOPE        } # INSCOPE
4141        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4142          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4143            !!!parse-error (type => 'unmatched end tag',
4144                            text => $token->{tag_name},
4145                            token => $end_tag_token);
4146          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4147          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4148          return;          return;
4149        }        }
4150        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4151          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4152            !!!parse-error (type => 'not closed',
4153                            text => $self->{open_elements}->[-1]->[0]
4154                                ->manakai_local_name,
4155                            token => $end_tag_token);
4156        }        }
4157                
4158        ## Step 2        ## Step 2
# Line 2052  sub _tree_construction_main ($) { Line 4160  sub _tree_construction_main ($) {
4160        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4161        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4162          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4163          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4164              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4165              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4166               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4167              !!!cp ('t59');
4168            $furthest_block = $node;            $furthest_block = $node;
4169            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4170          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4171              !!!cp ('t60');
4172            last OE;            last OE;
4173          }          }
4174        } # OE        } # OE
4175                
4176        ## Step 3        ## Step 3
4177        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4178            !!!cp ('t61');
4179          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4180          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4181          !!!next-token;          !!!next-token;
# Line 2077  sub _tree_construction_main ($) { Line 4188  sub _tree_construction_main ($) {
4188        ## Step 5        ## Step 5
4189        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4190        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4191            !!!cp ('t62');
4192          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4193        }        }
4194                
# Line 2099  sub _tree_construction_main ($) { Line 4211  sub _tree_construction_main ($) {
4211          S7S2: {          S7S2: {
4212            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4213              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4214                  !!!cp ('t63');
4215                $node_i_in_active = $_;                $node_i_in_active = $_;
4216                last S7S2;                last S7S2;
4217              }              }
# Line 2112  sub _tree_construction_main ($) { Line 4225  sub _tree_construction_main ($) {
4225                    
4226          ## Step 4          ## Step 4
4227          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4228              !!!cp ('t64');
4229            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4230          }          }
4231                    
4232          ## Step 5          ## Step 5
4233          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4234              !!!cp ('t65');
4235            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4236            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4237            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2134  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249        } # S7          } # S7  
4250                
4251        ## Step 8        ## Step 8
4252        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4253            my $foster_parent_element;
4254            my $next_sibling;
4255            OE: for (reverse 0..$#{$self->{open_elements}}) {
4256              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4257                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4258                                 if (defined $parent and $parent->node_type == 1) {
4259                                   !!!cp ('t65.1');
4260                                   $foster_parent_element = $parent;
4261                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4262                                 } else {
4263                                   !!!cp ('t65.2');
4264                                   $foster_parent_element
4265                                     = $self->{open_elements}->[$_ - 1]->[0];
4266                                 }
4267                                 last OE;
4268                               }
4269                             } # OE
4270                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4271                               unless defined $foster_parent_element;
4272            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4273            $open_tables->[-1]->[1] = 1; # tainted
4274          } else {
4275            !!!cp ('t65.3');
4276            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4277          }
4278                
4279        ## Step 9        ## Step 9
4280        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2151  sub _tree_construction_main ($) { Line 4291  sub _tree_construction_main ($) {
4291        my $i;        my $i;
4292        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4293          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4294              !!!cp ('t66');
4295            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4296            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4297          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4298              !!!cp ('t67');
4299            $i = $_;            $i = $_;
4300          }          }
4301        } # AFE        } # AFE
# Line 2163  sub _tree_construction_main ($) { Line 4305  sub _tree_construction_main ($) {
4305        undef $i;        undef $i;
4306        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4307          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4308              !!!cp ('t68');
4309            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4310            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4311          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4312              !!!cp ('t69');
4313            $i = $_;            $i = $_;
4314          }          }
4315        } # OE        } # OE
# Line 2176  sub _tree_construction_main ($) { Line 4320  sub _tree_construction_main ($) {
4320      } # FET      } # FET
4321    }; # $formatting_end_tag    }; # $formatting_end_tag
4322    
4323    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4324      $self->{open_elements}->[-1]->[0]->append_child (shift);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4325    }; # $insert_to_current    }; # $insert_to_current
4326    
4327    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4328                         my $child = shift;      my $child = shift;
4329                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4330                              table => 1, tbody => 1, tfoot => 1,        # MUST
4331                              thead => 1, tr => 1,        my $foster_parent_element;
4332                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4333                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4334                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
4335                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4336                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4337                                   !!!cp ('t70');
4338                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4339                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4340                               } else {                               } else {
4341                                   !!!cp ('t71');
4342                                 $foster_parent_element                                 $foster_parent_element
4343                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4344                               }                               }
# Line 2206  sub _tree_construction_main ($) { Line 4349  sub _tree_construction_main ($) {
4349                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4350                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4351                             ($child, $next_sibling);                             ($child, $next_sibling);
4352                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4353                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4354                         }        !!!cp ('t72');
4355          $self->{open_elements}->[-1]->[0]->append_child ($child);
4356        }
4357    }; # $insert_to_foster    }; # $insert_to_foster
4358    
4359    my $in_body = sub {    B: while (1) {
4360      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
4361      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
4362        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4363          $script_start_tag->();        ## Ignore the token
4364          return;        ## Stay in the phase
4365        } elsif ($token->{tag_name} eq 'style') {        !!!next-token;
4366          $style_start_tag->();        next B;
4367          return;      } elsif ($token->{type} == START_TAG_TOKEN and
4368        } elsif ({               $token->{tag_name} eq 'html') {
4369                  base => 1, link => 1, meta => 1,        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4370                 }->{$token->{tag_name}}) {          !!!cp ('t79');
4371          !!!parse-error (type => 'in body:'.$token->{tag_name});          !!!parse-error (type => 'after html', text => 'html', token => $token);
4372          ## NOTE: This is an "as if in head" code clone          $self->{insertion_mode} = AFTER_BODY_IM;
4373          my $el;        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4374          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!cp ('t80');
4375          if (defined $self->{head_element}) {          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376            $self->{head_element}->append_child ($el);          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4377          } else {        } else {
4378            $insert->($el);          !!!cp ('t81');
4379          }        }
4380            
4381          !!!next-token;        !!!cp ('t82');
4382          return;        !!!parse-error (type => 'not first start tag', token => $token);
4383        } elsif ($token->{tag_name} eq 'title') {        my $top_el = $self->{open_elements}->[0]->[0];
4384          !!!parse-error (type => 'in body:title');        for my $attr_name (keys %{$token->{attributes}}) {
4385          ## NOTE: There is an "as if in head" code clone          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4386          my $title_el;            !!!cp ('t84');
4387          !!!create-element ($title_el, 'title', $token->{attributes});            $top_el->set_attribute_ns
4388          (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])              (undef, [undef, $attr_name],
4389            ->append_child ($title_el);               $token->{attributes}->{$attr_name}->{value});
         $self->{content_model_flag} = 'RCDATA';  
           
         my $text = '';  
         !!!next-token;  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           $title_el->manakai_append_text ($text);  
         }  
           
         $self->{content_model_flag} = 'PCDATA';  
           
         if ($token->{type} eq 'end tag' and  
             $token->{tag_name} eq 'title') {  
           ## Ignore the token  
         } else {  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
           ## ISSUE: And ignore?  
         }  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error (type => 'in body:body');  
                 
         if (@{$self->{open_elements}} == 1 or  
             $self->{open_elements}->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $self->{open_elements}->[1]->[0];  
           for my $attr_name (keys %{$token->{attributes}}) {  
             unless ($body_el->has_attribute_ns (undef, $attr_name)) {  
               $body_el->set_attribute_ns  
                 (undef, [undef, $attr_name],  
                  $token->{attributes}->{$attr_name}->{value});  
             }  
           }  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, p => 1, ul => 1,  
                 pre => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         if ($token->{tag_name} eq 'pre') {  
           !!!next-token;  
           if ($token->{type} eq 'character') {  
             $token->{data} =~ s/^\x0A//;  
             unless (length $token->{data}) {  
               !!!next-token;  
             }  
           }  
         } else {  
           !!!next-token;  
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => 'end tag', tag_name => 'p'};  
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'li') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model_flag} = 'PLAINTEXT';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         if (defined $i) {  
           !!!parse-error (type => 'in hn:hn');  
           splice @{$self->{open_elements}}, $i;  
4390          }          }
4391                    }
4392          !!!insert-element-t ($token->{tag_name}, $token->{attributes});        !!!nack ('t84.1');
4393                    !!!next-token;
4394          next B;
4395        } elsif ($token->{type} == COMMENT_TOKEN) {
4396          my $comment = $self->{document}->create_comment ($token->{data});
4397          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4398            !!!cp ('t85');
4399            $self->{document}->append_child ($comment);
4400          } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4401            !!!cp ('t86');
4402            $self->{open_elements}->[0]->[0]->append_child ($comment);
4403          } else {
4404            !!!cp ('t87');
4405            $self->{open_elements}->[-1]->[0]->append_child ($comment);
4406          }
4407          !!!next-token;
4408          next B;
4409        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4410          if ($token->{type} == CHARACTER_TOKEN) {
4411            !!!cp ('t87.1');
4412            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4413          !!!next-token;          !!!next-token;
4414          return;          next B;
4415        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4416          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4417            my $node = $active_formatting_elements->[$i];               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4418            if ($node->[1] eq 'a') {              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4419              !!!parse-error (type => 'in a:a');              ($token->{tag_name} eq 'svg' and
4420                             $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4421              !!!back-token;            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4422              $token = {type => 'end tag', tag_name => 'a'};            !!!cp ('t87.2');
4423              $formatting_end_tag->($token->{tag_name});            #
4424                        } elsif ({
4425              AFE2: for (reverse 0..$#$active_formatting_elements) {                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4426                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4427                  splice @$active_formatting_elements, $_, 1;                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4428                  last AFE2;                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4429                }                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4430              } # AFE2                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4431              OE: for (reverse 0..$#{$self->{open_elements}}) {                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4432                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4433                  splice @{$self->{open_elements}}, $_, 1;                   }->{$token->{tag_name}}) {
4434                  last OE;            !!!cp ('t87.2');
4435                }            !!!parse-error (type => 'not closed',
4436              } # OE                            text => $self->{open_elements}->[-1]->[0]
4437              last AFE;                                ->manakai_local_name,
4438            } elsif ($node->[0] eq '#marker') {                            token => $token);
4439              last AFE;  
4440              pop @{$self->{open_elements}}
4441                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4442    
4443              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4444              ## Reprocess.
4445              next B;
4446            } else {
4447              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4448              my $tag_name = $token->{tag_name};
4449              if ($nsuri eq $SVG_NS) {
4450                $tag_name = {
4451                   altglyph => 'altGlyph',
4452                   altglyphdef => 'altGlyphDef',
4453                   altglyphitem => 'altGlyphItem',
4454                   animatecolor => 'animateColor',
4455                   animatemotion => 'animateMotion',
4456                   animatetransform => 'animateTransform',
4457                   clippath => 'clipPath',
4458                   feblend => 'feBlend',
4459                   fecolormatrix => 'feColorMatrix',
4460                   fecomponenttransfer => 'feComponentTransfer',
4461                   fecomposite => 'feComposite',
4462                   feconvolvematrix => 'feConvolveMatrix',
4463                   fediffuselighting => 'feDiffuseLighting',
4464                   fedisplacementmap => 'feDisplacementMap',
4465                   fedistantlight => 'feDistantLight',
4466                   feflood => 'feFlood',
4467                   fefunca => 'feFuncA',
4468                   fefuncb => 'feFuncB',
4469                   fefuncg => 'feFuncG',
4470                   fefuncr => 'feFuncR',
4471                   fegaussianblur => 'feGaussianBlur',
4472                   feimage => 'feImage',
4473                   femerge => 'feMerge',
4474                   femergenode => 'feMergeNode',
4475                   femorphology => 'feMorphology',
4476                   feoffset => 'feOffset',
4477                   fepointlight => 'fePointLight',
4478                   fespecularlighting => 'feSpecularLighting',
4479                   fespotlight => 'feSpotLight',
4480                   fetile => 'feTile',
4481                   feturbulence => 'feTurbulence',
4482                   foreignobject => 'foreignObject',
4483                   glyphref => 'glyphRef',
4484                   lineargradient => 'linearGradient',
4485                   radialgradient => 'radialGradient',
4486                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4487                   textpath => 'textPath',  
4488                }->{$tag_name} || $tag_name;
4489            }            }
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4490    
4491          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            ## "adjust SVG attributes" (SVG only) - done in insert-element-f
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
4492    
4493          !!!next-token;            ## "adjust foreign attributes" - done in insert-element-f
         return;  
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
4494    
4495          !!!next-token;            !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4496          return;  
4497        } elsif ($token->{tag_name} eq 'marquee' or            if ($self->{self_closing}) {
4498                 $token->{tag_name} eq 'object') {              pop @{$self->{open_elements}};
4499          $reconstruct_active_formatting_elements->($insert_to_current);              !!!ack ('t87.3');
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{content_model_flag} = 'CDATA';  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = 'in table';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,  
                 image => 1,  
                }->{$token->{tag_name}}) {  
         if ($token->{tag_name} eq 'image') {  
           !!!parse-error (type => 'image');  
           $token->{tag_name} = 'img';  
         }  
           
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                         {type => 'character',  
                          data => 'This is a searchable index. Insert your search keywords here: '}, # SHOULD  
                         ## TODO: make this configurable  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'},  
                        );  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ({  
                 textarea => 1,  
                 iframe => 1,  
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         if ($token->{tag_name} eq 'textarea') {  
           ## TODO: $self->{form_element} if defined  
           $self->{content_model_flag} = 'RCDATA';  
         } else {  
           $self->{content_model_flag} = 'CDATA';  
         }  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model_flag} = 'PCDATA';  
           
         if ($token->{type} eq 'end tag' and  
             $token->{tag_name} eq $tag_name) {  
           ## Ignore the token  
         } else {  
           if ($token->{tag_name} eq 'textarea') {  
             !!!parse-error (type => 'in CDATA:#'.$token->{type});  
4500            } else {            } else {
4501              !!!parse-error (type => 'in RCDATA:#'.$token->{type});              !!!cp ('t87.4');
4502            }            }
4503            ## ISSUE: And ignore?  
4504              !!!next-token;
4505              next B;
4506          }          }
4507          !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
4508          return;          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4509        } elsif ($token->{tag_name} eq 'select') {          !!!cp ('t87.5');
4510          $reconstruct_active_formatting_elements->($insert_to_current);          #
4511                  } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4512          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!cp ('t87.6');
4513                    !!!parse-error (type => 'not closed',
4514          $self->{insertion_mode} = 'in select';                          text => $self->{open_elements}->[-1]->[0]
4515          !!!next-token;                              ->manakai_local_name,
4516          return;                          token => $token);
4517        } elsif ({  
4518                  caption => 1, col => 1, colgroup => 1, frame => 1,          pop @{$self->{open_elements}}
4519                  frameset => 1, head => 1, option => 1, optgroup => 1,              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4520                  tbody => 1, td => 1, tfoot => 1, th => 1,  
4521                  thead => 1, tr => 1,          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4522                 }->{$token->{tag_name}}) {          ## Reprocess.
4523          !!!parse-error (type => 'in body:'.$token->{tag_name});          next B;
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
4524        } else {        } else {
4525          $reconstruct_active_formatting_elements->($insert_to_current);          die "$0: $token->{type}: Unknown token type";        
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
4526        }        }
4527      } elsif ($token->{type} eq 'end tag') {      }
4528        if ($token->{tag_name} eq 'body') {  
4529          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {      if ($self->{insertion_mode} & HEAD_IMS) {
4530            ## ISSUE: There is an issue in the spec.        if ($token->{type} == CHARACTER_TOKEN) {
4531            if ($self->{open_elements}->[-1]->[1] ne 'body') {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4532              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4533            }              !!!cp ('t88.2');
4534            $self->{insertion_mode} = 'after body';              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4535            !!!next-token;              #
4536            return;            } else {
4537          } else {              !!!cp ('t88.1');
4538            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              ## Ignore the token.
4539            ## Ignore the token              #
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'html') {  
         if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
           }  
           $self->{insertion_mode} = 'after body';  
           ## reprocess  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 form => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4540            }            }
4541          } # INSCOPE            unless (length $token->{data}) {
4542                        !!!cp ('t88');
4543          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {              !!!next-token;
4544            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              next B;
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         undef $self->{form_element} if $token->{tag_name} eq 'form';  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4545            }            }
4546          } # INSCOPE  ## TODO: set $token->{column} appropriately
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4547          }          }
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } 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, br => 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}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
   
         ## Step 2  
         S2: {  
           if ($node->[1] eq $token->{tag_name}) {  
             ## Step 1  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
           
             ## Step 2  
             if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
             }  
               
             ## Step 3  
             splice @{$self->{open_elements}}, $node_i;  
4548    
4549              !!!next-token;          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4550              last S2;            !!!cp ('t89');
4551            } else {            ## As if <head>
4552              ## Step 3            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4553              if (not $formatting_category->{$node->[1]} and            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4554                  #not $phrasing_category->{$node->[1]} and            push @{$self->{open_elements}},
4555                  ($special_category->{$node->[1]} or                [$self->{head_element}, $el_category->{head}];
4556                   $scoping_category->{$node->[1]})) {  
4557                !!!parse-error (type => 'not closed:'.$node->[1]);            ## Reprocess in the "in head" insertion mode...
4558                ## Ignore the token            pop @{$self->{open_elements}};
4559                !!!next-token;  
4560                last S2;            ## Reprocess in the "after head" insertion mode...
4561              }          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4562            }            !!!cp ('t90');
4563              ## As if </noscript>
4564              pop @{$self->{open_elements}};
4565              !!!parse-error (type => 'in noscript:#text', token => $token);
4566                        
4567            ## Step 4            ## Reprocess in the "in head" insertion mode...
4568            $node_i--;            ## As if </head>
4569            $node = $self->{open_elements}->[$node_i];            pop @{$self->{open_elements}};
             
           ## Step 5;  
           redo S2;  
         } # S2  
         return;  
       }  
     }  
   }; # $in_body  
4570    
4571    B: {            ## Reprocess in the "after head" insertion mode...
4572      if ($phase eq 'main') {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4573        if ($token->{type} eq 'DOCTYPE') {            !!!cp ('t91');
4574          !!!parse-error (type => 'in html:#DOCTYPE');            pop @{$self->{open_elements}};
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
         ## TODO: unless it is the first start tag token, parse-error  
         my $top_el = $self->{open_elements}->[0]->[0];  
         for my $attr_name (keys %{$token->{attributes}}) {  
           unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
             $top_el->set_attribute_ns  
               (undef, [undef, $attr_name],  
                $token->{attributes}->{$attr_name}->{value});  
           }  
         }  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
4575    
4576          ## Stop parsing            ## Reprocess in the "after head" insertion mode...
4577          last B;          } else {
4578              !!!cp ('t92');
4579            }
4580    
4581          ## ISSUE: There is an issue in the spec.          ## "after head" insertion mode
4582        } else {          ## As if <body>
4583          if ($self->{insertion_mode} eq 'before head') {          !!!insert-element ('body',, $token);
4584            if ($token->{type} eq 'character') {          $self->{insertion_mode} = IN_BODY_IM;
4585              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          ## reprocess
4586                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);          next B;
4587                unless (length $token->{data}) {        } elsif ($token->{type} == START_TAG_TOKEN) {
4588                  !!!next-token;          if ($token->{tag_name} eq 'head') {
4589                  redo B;            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4590                }              !!!cp ('t93');
4591              }              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4592              ## As if <head>              $self->{open_elements}->[-1]->[0]->append_child
4593              !!!create-element ($self->{head_element}, 'head');                  ($self->{head_element});
4594              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              push @{$self->{open_elements}},
4595              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  [$self->{head_element}, $el_category->{head}];
4596              $self->{insertion_mode} = 'in head';              $self->{insertion_mode} = IN_HEAD_IM;
4597              ## reprocess              !!!nack ('t93.1');
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
4598              !!!next-token;              !!!next-token;
4599              redo B;              next B;
4600            } elsif ($token->{type} eq 'start tag') {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4601              my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};              !!!cp ('t93.2');
4602              !!!create-element ($self->{head_element}, 'head', $attr);              !!!parse-error (type => 'after head', text => 'head',
4603              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                              token => $token);
4604              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              ## Ignore the token
4605              $self->{insertion_mode} = 'in head';              !!!nack ('t93.3');
4606              if ($token->{tag_name} eq 'head') {              !!!next-token;
4607                !!!next-token;              next B;
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               ## As if <head>  
               !!!create-element ($self->{head_element}, 'head');  
               $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
               push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             }  
4608            } else {            } else {
4609              die "$0: $token->{type}: Unknown type";              !!!cp ('t95');
4610            }              !!!parse-error (type => 'in head:head',
4611          } elsif ($self->{insertion_mode} eq 'in head') {                              token => $token); # or in head noscript
4612            if ($token->{type} eq 'character') {              ## Ignore the token
4613              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              !!!nack ('t95.1');
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
4614              !!!next-token;              !!!next-token;
4615              redo B;              next B;
4616            } elsif ($token->{type} eq 'start tag') {            }
4617              if ($token->{tag_name} eq 'title') {          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4618                ## NOTE: There is an "as if in head" code clone            !!!cp ('t96');
4619                my $title_el;            ## As if <head>
4620                !!!create-element ($title_el, 'title', $token->{attributes});            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4621                (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4622                  ->append_child ($title_el);            push @{$self->{open_elements}},
4623                $self->{content_model_flag} = 'RCDATA';                [$self->{head_element}, $el_category->{head}];
4624    
4625                my $text = '';            $self->{insertion_mode} = IN_HEAD_IM;
4626                !!!next-token;            ## Reprocess in the "in head" insertion mode...
4627                while ($token->{type} eq 'character') {          } else {
4628                  $text .= $token->{data};            !!!cp ('t97');
4629                  !!!next-token;          }
4630                }  
4631                if (length $text) {              if ($token->{tag_name} eq 'base') {
4632                  $title_el->manakai_append_text ($text);                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4633                }                  !!!cp ('t98');
4634                                  ## As if </noscript>
4635                $self->{content_model_flag} = 'PCDATA';                  pop @{$self->{open_elements}};
4636                    !!!parse-error (type => 'in noscript', text => 'base',
4637                                    token => $token);
4638                                
4639                if ($token->{type} eq 'end tag' and                  $self->{insertion_mode} = IN_HEAD_IM;
4640                    $token->{tag_name} eq 'title') {                  ## Reprocess in the "in head" insertion mode...
                 ## Ignore the token  
4641                } else {                } else {
4642                  !!!parse-error (type => 'in RCDATA:#'.$token->{type});                  !!!cp ('t99');
                 ## ISSUE: And ignore?  
4643                }                }
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'style') {  
               $style_start_tag->();  
               redo B;  
             } elsif ($token->{tag_name} eq 'script') {  
               $script_start_tag->();  
               redo B;  
             } elsif ({base => 1, link => 1, meta => 1}->{$token->{tag_name}}) {  
               ## NOTE: There are "as if in head" code clones  
               my $el;  
               !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
               (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
                 ->append_child ($el);  
4644    
4645                !!!next-token;                ## NOTE: There is a "as if in head" code clone.
4646                redo B;                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4647              } elsif ($token->{tag_name} eq 'head') {                  !!!cp ('t100');
4648                !!!parse-error (type => 'in head:head');                  !!!parse-error (type => 'after head',
4649                ## Ignore the token                                  text => $token->{tag_name}, token => $token);
4650                !!!next-token;                  push @{$self->{open_elements}},
4651                redo B;                      [$self->{head_element}, $el_category->{head}];
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'head') {  
               if ($self->{open_elements}->[-1]->[1] eq 'head') {  
                 pop @{$self->{open_elements}};  
4652                } else {                } else {
4653                  !!!parse-error (type => 'unmatched end tag:head');                  !!!cp ('t101');
4654                }                }
4655                $self->{insertion_mode} = 'after head';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4656                !!!next-token;                pop @{$self->{open_elements}};
4657                redo B;                pop @{$self->{open_elements}} # <head>
4658              } elsif ($token->{tag_name} eq 'html') {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4659                #                !!!nack ('t101.1');
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
4660                !!!next-token;                !!!next-token;
4661                redo B;                next B;
4662              }          } elsif ($token->{tag_name} eq 'link') {
4663              ## NOTE: There is a "as if in head" code clone.
4664              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4665                !!!cp ('t102');
4666                !!!parse-error (type => 'after head',
4667                                text => $token->{tag_name}, token => $token);
4668                push @{$self->{open_elements}},
4669                    [$self->{head_element}, $el_category->{head}];
4670            } else {            } else {
4671              #              !!!cp ('t103');
4672            }            }
4673              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4674              pop @{$self->{open_elements}};
4675              pop @{$self->{open_elements}} # <head>
4676                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4677              !!!ack ('t103.1');
4678              !!!next-token;
4679              next B;
4680            } elsif ($token->{tag_name} eq 'command' or
4681                     $token->{tag_name} eq 'eventsource') {
4682              if ($self->{insertion_mode} == IN_HEAD_IM) {
4683                ## NOTE: If the insertion mode at the time of the emission
4684                ## of the token was "before head", $self->{insertion_mode}
4685                ## is already changed to |IN_HEAD_IM|.
4686    
4687            if ($self->{open_elements}->[-1]->[1] eq 'head') {              ## NOTE: There is a "as if in head" code clone.
4688              ## As if </head>              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4689              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
4690            }              pop @{$self->{open_elements}} # <head>
4691            $self->{insertion_mode} = 'after head';                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4692            ## reprocess              !!!ack ('t103.2');
           redo B;  
   
           ## ISSUE: An issue in the spec.  
         } elsif ($self->{insertion_mode} eq 'after head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
4693              !!!next-token;              !!!next-token;
4694              redo B;              next B;
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $self->{insertion_mode} = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'frameset') {  
               !!!insert-element ('frameset', $token->{attributes});  
               $self->{insertion_mode} = 'in frameset';  
               !!!next-token;  
               redo B;  
             } elsif ({  
                       base => 1, link => 1, meta => 1,  
                       script => 1, style => 1, title => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'after head:'.$token->{tag_name});  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
4695            } else {            } else {
4696                ## NOTE: "in head noscript" or "after head" insertion mode
4697                ## - in these cases, these tags are treated as same as
4698                ## normal in-body tags.
4699                !!!cp ('t103.3');
4700              #              #
4701            }            }
4702                          } elsif ($token->{tag_name} eq 'meta') {
4703            ## As if <body>                ## NOTE: There is a "as if in head" code clone.
4704            !!!insert-element ('body');                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4705            $self->{insertion_mode} = 'in body';                  !!!cp ('t104');
4706            ## reprocess                  !!!parse-error (type => 'after head',
4707            redo B;                                  text => $token->{tag_name}, token => $token);
4708          } elsif ($self->{insertion_mode} eq 'in body') {                  push @{$self->{open_elements}},
4709            if ($token->{type} eq 'character') {                      [$self->{head_element}, $el_category->{head}];
4710              ## NOTE: There is a code clone of "character in body".                } else {
4711              $reconstruct_active_formatting_elements->($insert_to_current);                  !!!cp ('t105');
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
   
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## NOTE: There is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } else {  
             $in_body->($insert_to_current);  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There are "character in table" code clones.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
4712                }                }
4713              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4714                  my $meta_el = pop @{$self->{open_elements}};
             !!!parse-error (type => 'in table:#character');  
4715    
4716              ## As if in body, but insert into foster parent element                unless ($self->{confident}) {
4717              ## ISSUE: Spec says that "whenever a node would be inserted                  if ($token->{attributes}->{charset}) {
4718              ## into the current node" while characters might not be                    !!!cp ('t106');
4719              ## result in a new Text node.                    ## NOTE: Whether the encoding is supported or not is handled
4720              $reconstruct_active_formatting_elements->($insert_to_foster);                    ## in the {change_encoding} callback.
4721                                  $self->{change_encoding}
4722              if ({                        ->($self, $token->{attributes}->{charset}->{value},
4723                   table => 1, tbody => 1, tfoot => 1,                           $token);
4724                   thead => 1, tr => 1,                    
4725                  }->{$self->{open_elements}->[-1]->[1]}) {                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4726                # MUST                        ->set_user_data (manakai_has_reference =>
4727                my $foster_parent_element;                                             $token->{attributes}->{charset}
4728                my $next_sibling;                                                 ->{has_reference});
4729                my $prev_sibling;                  } elsif ($token->{attributes}->{content}) {
4730                OE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($token->{attributes}->{content}->{value}
4731                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4732                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                            [\x09\x0A\x0C\x0D\x20]*=
4733                    if (defined $parent and $parent->node_type == 1) {                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4734                      $foster_parent_element = $parent;                            ([^"'\x09\x0A\x0C\x0D\x20]
4735                      $next_sibling = $self->{open_elements}->[$_]->[0];                             [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4736                      $prev_sibling = $next_sibling->previous_sibling;                      !!!cp ('t107');
4737                        ## NOTE: Whether the encoding is supported or not is handled
4738                        ## in the {change_encoding} callback.
4739                        $self->{change_encoding}
4740                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4741                               $token);
4742                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4743                            ->set_user_data (manakai_has_reference =>
4744                                                 $token->{attributes}->{content}
4745                                                       ->{has_reference});
4746                    } else {                    } else {
4747                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      !!!cp ('t108');
                     $prev_sibling = $foster_parent_element->last_child;  
4748                    }                    }
                   last OE;  
4749                  }                  }
               } # 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) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
4750                } else {                } else {
4751                  $foster_parent_element->insert_before                  if ($token->{attributes}->{charset}) {
4752                    ($self->{document}->create_text_node ($token->{data}),                    !!!cp ('t109');
4753                     $next_sibling);                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4754                }                        ->set_user_data (manakai_has_reference =>
4755              } else {                                             $token->{attributes}->{charset}
4756                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                                                 ->{has_reference});
4757              }                  }
4758                                if ($token->{attributes}->{content}) {
4759              !!!next-token;                    !!!cp ('t110');
4760              redo B;                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4761            } elsif ($token->{type} eq 'comment') {                        ->set_user_data (manakai_has_reference =>
4762              my $comment = $self->{document}->create_comment ($token->{data});                                             $token->{attributes}->{content}
4763              $self->{open_elements}->[-1]->[0]->append_child ($comment);                                                 ->{has_reference});
4764              !!!next-token;                  }
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({  
                  caption => 1,  
                  colgroup => 1,  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
4765                }                }
4766    
4767                push @$active_formatting_elements, ['#marker', '']                pop @{$self->{open_elements}} # <head>
4768                  if $token->{tag_name} eq 'caption';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4769                  !!!ack ('t110.1');
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = {  
                                  caption => 'in caption',  
                                  colgroup => 'in column group',  
                                  tbody => 'in table body',  
                                  tfoot => 'in table body',  
                                  thead => 'in table body',  
                                 }->{$token->{tag_name}};  
4770                !!!next-token;                !!!next-token;
4771                redo B;                next B;
4772              } elsif ({              } elsif ($token->{tag_name} eq 'title') {
4773                        col => 1,                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4774                        td => 1, th => 1, tr => 1,                  !!!cp ('t111');
4775                       }->{$token->{tag_name}}) {                  ## As if </noscript>
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4776                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4777                    !!!parse-error (type => 'in noscript', text => 'title',
4778                                    token => $token);
4779                  
4780                    $self->{insertion_mode} = IN_HEAD_IM;
4781                    ## Reprocess in the "in head" insertion mode...
4782                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4783                    !!!cp ('t112');
4784                    !!!parse-error (type => 'after head',
4785                                    text => $token->{tag_name}, token => $token);
4786                    push @{$self->{open_elements}},
4787                        [$self->{head_element}, $el_category->{head}];
4788                  } else {
4789                    !!!cp ('t113');
4790                }                }
4791    
4792                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                ## NOTE: There is a "as if in head" code clone.
4793                $self->{insertion_mode} = $token->{tag_name} eq 'col'                my $parent = defined $self->{head_element} ? $self->{head_element}
4794                  ? 'in column group' : 'in table body';                    : $self->{open_elements}->[-1]->[0];
4795                ## reprocess                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4796                redo B;                pop @{$self->{open_elements}} # <head>
4797              } elsif ($token->{tag_name} eq 'table') {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4798                ## NOTE: There are code clones for this "table in table"                next B;
4799                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              } elsif ($token->{tag_name} eq 'style' or
4800                         $token->{tag_name} eq 'noframes') {
4801                ## As if </table>                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4802                ## have a table element in table scope                ## insertion mode IN_HEAD_IM)
4803                my $i;                ## NOTE: There is a "as if in head" code clone.
4804                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4805                  my $node = $self->{open_elements}->[$_];                  !!!cp ('t114');
4806                  if ($node->[1] eq 'table') {                  !!!parse-error (type => 'after head',
4807                    $i = $_;                                  text => $token->{tag_name}, token => $token);
4808                    last INSCOPE;                  push @{$self->{open_elements}},
4809                  } elsif ({                      [$self->{head_element}, $el_category->{head}];
4810                            table => 1, html => 1,                } else {
4811                           }->{$node->[1]}) {                  !!!cp ('t115');
4812                    last INSCOPE;                }
4813                  }                $parse_rcdata->(CDATA_CONTENT_MODEL);
4814                } # INSCOPE                pop @{$self->{open_elements}} # <head>
4815                unless (defined $i) {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4816                  !!!parse-error (type => 'unmatched end tag:table');                next B;
4817                  ## Ignore tokens </table><table>              } elsif ($token->{tag_name} eq 'noscript') {
4818                  if ($self->{insertion_mode} == IN_HEAD_IM) {
4819                    !!!cp ('t116');
4820                    ## NOTE: and scripting is disalbed
4821                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4822                    $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4823                    !!!nack ('t116.1');
4824                  !!!next-token;                  !!!next-token;
4825                  redo B;                  next B;
4826                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4827                    !!!cp ('t117');
4828                    !!!parse-error (type => 'in noscript', text => 'noscript',
4829                                    token => $token);
4830                    ## Ignore the token
4831                    !!!nack ('t117.1');
4832                    !!!next-token;
4833                    next B;
4834                  } else {
4835                    !!!cp ('t118');
4836                    #
4837                }                }
4838                } elsif ($token->{tag_name} eq 'script') {
4839                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4840                    !!!cp ('t119');
4841                    ## As if </noscript>
4842                    pop @{$self->{open_elements}};
4843                    !!!parse-error (type => 'in noscript', text => 'script',
4844                                    token => $token);
4845                                
4846                ## generate implied end tags                  $self->{insertion_mode} = IN_HEAD_IM;
4847                if ({                  ## Reprocess in the "in head" insertion mode...
4848                     dd => 1, dt => 1, li => 1, p => 1,                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4849                     td => 1, th => 1, tr => 1,                  !!!cp ('t120');
4850                    }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'after head',
4851                  !!!back-token; # <table>                                  text => $token->{tag_name}, token => $token);
4852                  $token = {type => 'end tag', tag_name => 'table'};                  push @{$self->{open_elements}},
4853                  !!!back-token;                      [$self->{head_element}, $el_category->{head}];
4854                  $token = {type => 'end tag',                } else {
4855                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                  !!!cp ('t121');
                 redo B;  
4856                }                }
4857    
4858                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## NOTE: There is a "as if in head" code clone.
4859                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                $script_start_tag->();
4860                  pop @{$self->{open_elements}} # <head>
4861                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4862                  next B;
4863                } elsif ($token->{tag_name} eq 'body' or
4864                         $token->{tag_name} eq 'frameset') {
4865                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4866                    !!!cp ('t122');
4867                    ## As if </noscript>
4868                    pop @{$self->{open_elements}};
4869                    !!!parse-error (type => 'in noscript',
4870                                    text => $token->{tag_name}, token => $token);
4871                    
4872                    ## Reprocess in the "in head" insertion mode...
4873                    ## As if </head>
4874                    pop @{$self->{open_elements}};
4875                    
4876                    ## Reprocess in the "after head" insertion mode...
4877                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4878                    !!!cp ('t124');
4879                    pop @{$self->{open_elements}};
4880                    
4881                    ## Reprocess in the "after head" insertion mode...
4882                  } else {
4883                    !!!cp ('t125');
4884                }                }
4885    
4886                splice @{$self->{open_elements}}, $i;                ## "after head" insertion mode
4887                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4888                  if ($token->{tag_name} eq 'body') {
4889                    !!!cp ('t126');
4890                    $self->{insertion_mode} = IN_BODY_IM;
4891                  } elsif ($token->{tag_name} eq 'frameset') {
4892                    !!!cp ('t127');
4893                    $self->{insertion_mode} = IN_FRAMESET_IM;
4894                  } else {
4895                    die "$0: tag name: $self->{tag_name}";
4896                  }
4897                  !!!nack ('t127.1');
4898                  !!!next-token;
4899                  next B;
4900                } else {
4901                  !!!cp ('t128');
4902                  #
4903                }
4904    
4905                $self->_reset_insertion_mode;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4906                  !!!cp ('t129');
4907                  ## As if </noscript>
4908                  pop @{$self->{open_elements}};
4909                  !!!parse-error (type => 'in noscript:/',
4910                                  text => $token->{tag_name}, token => $token);
4911                  
4912                  ## Reprocess in the "in head" insertion mode...
4913                  ## As if </head>
4914                  pop @{$self->{open_elements}};
4915    
4916                ## reprocess                ## Reprocess in the "after head" insertion mode...
4917                redo B;              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4918                  !!!cp ('t130');
4919                  ## As if </head>
4920                  pop @{$self->{open_elements}};
4921    
4922                  ## Reprocess in the "after head" insertion mode...
4923              } else {              } else {
4924                #                !!!cp ('t131');
4925              }              }
4926            } elsif ($token->{type} eq 'end tag') {  
4927              if ($token->{tag_name} eq 'table') {              ## "after head" insertion mode
4928                ## have a table element in table scope              ## As if <body>
4929                my $i;              !!!insert-element ('body',, $token);
4930                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              $self->{insertion_mode} = IN_BODY_IM;
4931                  my $node = $self->{open_elements}->[$_];              ## reprocess
4932                  if ($node->[1] eq $token->{tag_name}) {              !!!ack-later;
4933                    $i = $_;              next B;
4934                    last INSCOPE;            } elsif ($token->{type} == END_TAG_TOKEN) {
4935                  } elsif ({              if ($token->{tag_name} eq 'head') {
4936                            table => 1, html => 1,                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4937                           }->{$node->[1]}) {                  !!!cp ('t132');
4938                    last INSCOPE;                  ## As if <head>
4939                  }                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4940                } # INSCOPE                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4941                unless (defined $i) {                  push @{$self->{open_elements}},
4942                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      [$self->{head_element}, $el_category->{head}];
4943    
4944                    ## Reprocess in the "in head" insertion mode...
4945                    pop @{$self->{open_elements}};
4946                    $self->{insertion_mode} = AFTER_HEAD_IM;
4947                    !!!next-token;
4948                    next B;
4949                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4950                    !!!cp ('t133');
4951                    ## As if </noscript>
4952                    pop @{$self->{open_elements}};
4953                    !!!parse-error (type => 'in noscript:/',
4954                                    text => 'head', token => $token);
4955                    
4956                    ## Reprocess in the "in head" insertion mode...
4957                    pop @{$self->{open_elements}};
4958                    $self->{insertion_mode} = AFTER_HEAD_IM;
4959                    !!!next-token;
4960                    next B;
4961                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4962                    !!!cp ('t134');
4963                    pop @{$self->{open_elements}};
4964                    $self->{insertion_mode} = AFTER_HEAD_IM;
4965                    !!!next-token;
4966                    next B;
4967                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4968                    !!!cp ('t134.1');
4969                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4970                                    token => $token);
4971                  ## Ignore the token                  ## Ignore the token
4972                  !!!next-token;                  !!!next-token;
4973                  redo B;                  next B;
4974                  } else {
4975                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4976                }                }
4977                              } elsif ($token->{tag_name} eq 'noscript') {
4978                ## generate implied end tags                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4979                if ({                  !!!cp ('t136');
4980                     dd => 1, dt => 1, li => 1, p => 1,                  pop @{$self->{open_elements}};
4981                     td => 1, th => 1, tr => 1,                  $self->{insertion_mode} = IN_HEAD_IM;
4982                    }->{$self->{open_elements}->[-1]->[1]}) {                  !!!next-token;
4983                  !!!back-token;                  next B;
4984                  $token = {type => 'end tag',                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4985                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                         $self->{insertion_mode} == AFTER_HEAD_IM) {
4986                  redo B;                  !!!cp ('t137');
4987                    !!!parse-error (type => 'unmatched end tag',
4988                                    text => 'noscript', token => $token);
4989                    ## Ignore the token ## ISSUE: An issue in the spec.
4990                    !!!next-token;
4991                    next B;
4992                  } else {
4993                    !!!cp ('t138');
4994                    #
4995                }                }
4996                } elsif ({
4997                if ($self->{open_elements}->[-1]->[1] ne 'table') {                        body => 1, html => 1,
4998                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                       }->{$token->{tag_name}}) {
4999                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5000                      $self->{insertion_mode} == IN_HEAD_IM or
5001                      $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5002                    !!!cp ('t140');
5003                    !!!parse-error (type => 'unmatched end tag',
5004                                    text => $token->{tag_name}, token => $token);
5005                    ## Ignore the token
5006                    !!!next-token;
5007                    next B;
5008                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5009                    !!!cp ('t140.1');
5010                    !!!parse-error (type => 'unmatched end tag',
5011                                    text => $token->{tag_name}, token => $token);
5012                    ## Ignore the token
5013                    !!!next-token;
5014                    next B;
5015                  } else {
5016                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5017                }                }
5018                } elsif ($token->{tag_name} eq 'p') {
5019                  !!!cp ('t142');
5020                  !!!parse-error (type => 'unmatched end tag',
5021                                  text => $token->{tag_name}, token => $token);
5022                  ## Ignore the token
5023                  !!!next-token;
5024                  next B;
5025                } elsif ($token->{tag_name} eq 'br') {
5026                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5027                    !!!cp ('t142.2');
5028                    ## (before head) as if <head>, (in head) as if </head>
5029                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5030                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5031                    $self->{insertion_mode} = AFTER_HEAD_IM;
5032      
5033                    ## Reprocess in the "after head" insertion mode...
5034                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5035                    !!!cp ('t143.2');
5036                    ## As if </head>
5037                    pop @{$self->{open_elements}};
5038                    $self->{insertion_mode} = AFTER_HEAD_IM;
5039      
5040                    ## Reprocess in the "after head" insertion mode...
5041                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5042                    !!!cp ('t143.3');
5043                    ## ISSUE: Two parse errors for <head><noscript></br>
5044                    !!!parse-error (type => 'unmatched end tag',
5045                                    text => 'br', token => $token);
5046                    ## As if </noscript>
5047                    pop @{$self->{open_elements}};
5048                    $self->{insertion_mode} = IN_HEAD_IM;
5049    
5050                splice @{$self->{open_elements}}, $i;                  ## Reprocess in the "in head" insertion mode...
5051                    ## As if </head>
5052                    pop @{$self->{open_elements}};
5053                    $self->{insertion_mode} = AFTER_HEAD_IM;
5054    
5055                $self->_reset_insertion_mode;                  ## Reprocess in the "after head" insertion mode...
5056                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5057                    !!!cp ('t143.4');
5058                    #
5059                  } else {
5060                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5061                  }
5062    
5063                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5064                  !!!parse-error (type => 'unmatched end tag',
5065                                  text => 'br', token => $token);
5066                  ## Ignore the token
5067                !!!next-token;                !!!next-token;
5068                redo B;                next B;
5069              } elsif ({              } else {
5070                        body => 1, caption => 1, col => 1, colgroup => 1,                !!!cp ('t145');
5071                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,                !!!parse-error (type => 'unmatched end tag',
5072                        thead => 1, tr => 1,                                text => $token->{tag_name}, token => $token);
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5073                ## Ignore the token                ## Ignore the token
5074                !!!next-token;                !!!next-token;
5075                redo B;                next B;
5076                }
5077    
5078                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5079                  !!!cp ('t146');
5080                  ## As if </noscript>
5081                  pop @{$self->{open_elements}};
5082                  !!!parse-error (type => 'in noscript:/',
5083                                  text => $token->{tag_name}, token => $token);
5084                  
5085                  ## Reprocess in the "in head" insertion mode...
5086                  ## As if </head>
5087                  pop @{$self->{open_elements}};
5088    
5089                  ## Reprocess in the "after head" insertion mode...
5090                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5091                  !!!cp ('t147');
5092                  ## As if </head>
5093                  pop @{$self->{open_elements}};
5094    
5095                  ## Reprocess in the "after head" insertion mode...
5096                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5097    ## ISSUE: This case cannot be reached?
5098                  !!!cp ('t148');
5099                  !!!parse-error (type => 'unmatched end tag',
5100                                  text => $token->{tag_name}, token => $token);
5101                  ## Ignore the token ## ISSUE: An issue in the spec.
5102                  !!!next-token;
5103                  next B;
5104              } else {              } else {
5105                #                !!!cp ('t149');
5106              }              }
           } else {  
             #  
           }  
5107    
5108            !!!parse-error (type => 'in table:'.$token->{tag_name});              ## "after head" insertion mode
5109            $in_body->($insert_to_foster);              ## As if <body>
5110            redo B;              !!!insert-element ('body',, $token);
5111          } elsif ($self->{insertion_mode} eq 'in caption') {              $self->{insertion_mode} = IN_BODY_IM;
5112            if ($token->{type} eq 'character') {              ## reprocess
5113              ## NOTE: This is a code clone of "character in body".              next B;
5114          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5115            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5116              !!!cp ('t149.1');
5117    
5118              ## NOTE: As if <head>
5119              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5120              $self->{open_elements}->[-1]->[0]->append_child
5121                  ($self->{head_element});
5122              #push @{$self->{open_elements}},
5123              #    [$self->{head_element}, $el_category->{head}];
5124              #$self->{insertion_mode} = IN_HEAD_IM;
5125              ## NOTE: Reprocess.
5126    
5127              ## NOTE: As if </head>
5128              #pop @{$self->{open_elements}};
5129              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5130              ## NOTE: Reprocess.
5131              
5132              #
5133            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5134              !!!cp ('t149.2');
5135    
5136              ## NOTE: As if </head>
5137              pop @{$self->{open_elements}};
5138              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5139              ## NOTE: Reprocess.
5140    
5141              #
5142            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5143              !!!cp ('t149.3');
5144    
5145              !!!parse-error (type => 'in noscript:#eof', token => $token);
5146    
5147              ## As if </noscript>
5148              pop @{$self->{open_elements}};
5149              #$self->{insertion_mode} = IN_HEAD_IM;
5150              ## NOTE: Reprocess.
5151    
5152              ## NOTE: As if </head>
5153              pop @{$self->{open_elements}};
5154              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5155              ## NOTE: Reprocess.
5156    
5157              #
5158            } else {
5159              !!!cp ('t149.4');
5160              #
5161            }
5162    
5163            ## NOTE: As if <body>
5164            !!!insert-element ('body',, $token);
5165            $self->{insertion_mode} = IN_BODY_IM;
5166            ## NOTE: Reprocess.
5167            next B;
5168          } else {
5169            die "$0: $token->{type}: Unknown token type";
5170          }
5171        } elsif ($self->{insertion_mode} & BODY_IMS) {
5172              if ($token->{type} == CHARACTER_TOKEN) {
5173                !!!cp ('t150');
5174                ## NOTE: There is a code clone of "character in body".
5175              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5176                            
5177              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5178    
5179              !!!next-token;              !!!next-token;
5180              redo B;              next B;
5181            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} == START_TAG_TOKEN) {
             ## NOTE: This is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
5182              if ({              if ({
5183                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5184                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5185                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5186                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
5187                    ## have an element in table scope
5188                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
5189                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
5190                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
5191                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
5192                  my $node = $self->{open_elements}->[$_];  
5193                  if ($node->[1] eq 'caption') {                      ## Close the cell
5194                    $i = $_;                      !!!back-token; # <x>
5195                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
5196                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
5197                            table => 1, html => 1,                                line => $token->{line},
5198                           }->{$node->[1]}) {                                column => $token->{column}};
5199                    last INSCOPE;                      next B;
5200                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5201                        !!!cp ('t152');
5202                        ## ISSUE: This case can never be reached, maybe.
5203                        last;
5204                      }
5205                  }                  }
5206                } # INSCOPE  
5207                unless (defined $i) {                  !!!cp ('t153');
5208                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
5209                        text => $token->{tag_name}, token => $token);
5210                  ## Ignore the token                  ## Ignore the token
5211                    !!!nack ('t153.1');
5212                  !!!next-token;                  !!!next-token;
5213                  redo B;                  next B;
5214                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5215                                  !!!parse-error (type => 'not closed', text => 'caption',
5216                ## generate implied end tags                                  token => $token);
5217                if ({                  
5218                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5219                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5220                    }->{$self->{open_elements}->[-1]->[1]}) {                  my $i;
5221                  !!!back-token; # <?>                  INSCOPE: {
5222                  $token = {type => 'end tag', tag_name => 'caption'};                    for (reverse 0..$#{$self->{open_elements}}) {
5223                  !!!back-token;                      my $node = $self->{open_elements}->[$_];
5224                  $token = {type => 'end tag',                      if ($node->[1] & CAPTION_EL) {
5225                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        !!!cp ('t155');
5226                  redo B;                        $i = $_;
5227                }                        last INSCOPE;
5228                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5229                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        !!!cp ('t156');
5230                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                        last;
5231                }                      }
5232                      }
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
5233    
5234                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
5235                      !!!parse-error (type => 'start tag not allowed',
5236                                      text => $token->{tag_name}, token => $token);
5237                      ## Ignore the token
5238                      !!!nack ('t157.1');
5239                      !!!next-token;
5240                      next B;
5241                    } # INSCOPE
5242                    
5243                    ## generate implied end tags
5244                    while ($self->{open_elements}->[-1]->[1]
5245                               & END_TAG_OPTIONAL_EL) {
5246                      !!!cp ('t158');
5247                      pop @{$self->{open_elements}};
5248                    }
5249    
5250                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5251                redo B;                    !!!cp ('t159');
5252                      !!!parse-error (type => 'not closed',
5253                                      text => $self->{open_elements}->[-1]->[0]
5254                                          ->manakai_local_name,
5255                                      token => $token);
5256                    } else {
5257                      !!!cp ('t160');
5258                    }
5259                    
5260                    splice @{$self->{open_elements}}, $i;
5261                    
5262                    $clear_up_to_marker->();
5263                    
5264                    $self->{insertion_mode} = IN_TABLE_IM;
5265                    
5266                    ## reprocess
5267                    !!!ack-later;
5268                    next B;
5269                  } else {
5270                    !!!cp ('t161');
5271                    #
5272                  }
5273              } else {              } else {
5274                  !!!cp ('t162');
5275                #                #
5276              }              }
5277            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5278              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5279                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
5280                my $i;                  ## have an element in table scope
5281                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
5282                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5283                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5284                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5285                    last INSCOPE;                      !!!cp ('t163');
5286                  } elsif ({                      $i = $_;
5287                            table => 1, html => 1,                      last INSCOPE;
5288                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5289                    last INSCOPE;                      !!!cp ('t164');
5290                        last INSCOPE;
5291                      }
5292                    } # INSCOPE
5293                      unless (defined $i) {
5294                        !!!cp ('t165');
5295                        !!!parse-error (type => 'unmatched end tag',
5296                                        text => $token->{tag_name},
5297                                        token => $token);
5298                        ## Ignore the token
5299                        !!!next-token;
5300                        next B;
5301                      }
5302                    
5303                    ## generate implied end tags
5304                    while ($self->{open_elements}->[-1]->[1]
5305                               & END_TAG_OPTIONAL_EL) {
5306                      !!!cp ('t166');
5307                      pop @{$self->{open_elements}};
5308                  }                  }
5309                } # INSCOPE  
5310                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5311                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5312                      !!!cp ('t167');
5313                      !!!parse-error (type => 'not closed',
5314                                      text => $self->{open_elements}->[-1]->[0]
5315                                          ->manakai_local_name,
5316                                      token => $token);
5317                    } else {
5318                      !!!cp ('t168');
5319                    }
5320                    
5321                    splice @{$self->{open_elements}}, $i;
5322                    
5323                    $clear_up_to_marker->();
5324                    
5325                    $self->{insertion_mode} = IN_ROW_IM;
5326                    
5327                    !!!next-token;
5328                    next B;
5329                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5330                    !!!cp ('t169');
5331                    !!!parse-error (type => 'unmatched end tag',
5332                                    text => $token->{tag_name}, token => $token);
5333                  ## Ignore the token                  ## Ignore the token
5334                  !!!next-token;                  !!!next-token;
5335                  redo B;                  next B;
5336                }                } else {
5337                                  !!!cp ('t170');
5338                ## generate implied end tags                  #
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5339                }                }
5340                } elsif ($token->{tag_name} eq 'caption') {
5341                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5342                    ## have a table element in table scope
5343                    my $i;
5344                    INSCOPE: {
5345                      for (reverse 0..$#{$self->{open_elements}}) {
5346                        my $node = $self->{open_elements}->[$_];
5347                        if ($node->[1] & CAPTION_EL) {
5348                          !!!cp ('t171');
5349                          $i = $_;
5350                          last INSCOPE;
5351                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5352                          !!!cp ('t172');
5353                          last;
5354                        }
5355                      }
5356    
5357                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5358                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5359                                      text => $token->{tag_name}, token => $token);
5360                      ## Ignore the token
5361                      !!!next-token;
5362                      next B;
5363                    } # INSCOPE
5364                    
5365                    ## generate implied end tags
5366                    while ($self->{open_elements}->[-1]->[1]
5367                               & END_TAG_OPTIONAL_EL) {
5368                      !!!cp ('t174');
5369                      pop @{$self->{open_elements}};
5370                    }
5371                    
5372                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5373                      !!!cp ('t175');
5374                      !!!parse-error (type => 'not closed',
5375                                      text => $self->{open_elements}->[-1]->[0]
5376                                          ->manakai_local_name,
5377                                      token => $token);
5378                    } else {
5379                      !!!cp ('t176');
5380                    }
5381                    
5382                    splice @{$self->{open_elements}}, $i;
5383                    
5384                    $clear_up_to_marker->();
5385                    
5386                    $self->{insertion_mode} = IN_TABLE_IM;
5387                    
5388                    !!!next-token;
5389                    next B;
5390                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5391                    !!!cp ('t177');
5392                    !!!parse-error (type => 'unmatched end tag',
5393                                    text => $token->{tag_name}, token => $token);
5394                    ## Ignore the token
5395                    !!!next-token;
5396                    next B;
5397                  } else {
5398                    !!!cp ('t178');
5399                    #
5400                }                }
5401                } elsif ({
5402                          table => 1, tbody => 1, tfoot => 1,
5403                          thead => 1, tr => 1,
5404                         }->{$token->{tag_name}} and
5405                         $self->{insertion_mode} == IN_CELL_IM) {
5406                  ## have an element in table scope
5407                  my $i;
5408                  my $tn;
5409                  INSCOPE: {
5410                    for (reverse 0..$#{$self->{open_elements}}) {
5411                      my $node = $self->{open_elements}->[$_];
5412                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5413                        !!!cp ('t179');
5414                        $i = $_;
5415    
5416                        ## Close the cell
5417                        !!!back-token; # </x>
5418                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5419                                  line => $token->{line},
5420                                  column => $token->{column}};
5421                        next B;
5422                      } elsif ($node->[1] & TABLE_CELL_EL) {
5423                        !!!cp ('t180');
5424                        $tn = $node->[0]->manakai_local_name;
5425                        ## NOTE: There is exactly one |td| or |th| element
5426                        ## in scope in the stack of open elements by definition.
5427                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5428                        ## ISSUE: Can this be reached?
5429                        !!!cp ('t181');
5430                        last;
5431                      }
5432                    }
5433    
5434                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5435                    !!!parse-error (type => 'unmatched end tag',
5436                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5437                    ## Ignore the token
5438                $self->{insertion_mode} = 'in table';                  !!!next-token;
5439                    next B;
5440                !!!next-token;                } # INSCOPE
5441                redo B;              } elsif ($token->{tag_name} eq 'table' and
5442              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5443                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5444                                  token => $token);
5445    
5446                ## As if </caption>                ## As if </caption>
5447                ## have a table element in table scope                ## have a table element in table scope
5448                my $i;                my $i;
5449                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5450                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5451                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5452                      !!!cp ('t184');
5453                    $i = $_;                    $i = $_;
5454                    last INSCOPE;                    last INSCOPE;
5455                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5456                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5457                    last INSCOPE;                    last INSCOPE;
5458                  }                  }
5459                } # INSCOPE                } # INSCOPE
5460                unless (defined $i) {                unless (defined $i) {
5461                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5462                    !!!parse-error (type => 'unmatched end tag',
5463                                    text => 'caption', token => $token);
5464                  ## Ignore the token                  ## Ignore the token
5465                  !!!next-token;                  !!!next-token;
5466                  redo B;                  next B;
5467                }                }
5468                                
5469                ## generate implied end tags                ## generate implied end tags
5470                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5471                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5472                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5473                }                }
5474    
5475                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5476                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5477                    !!!parse-error (type => 'not closed',
5478                                    text => $self->{open_elements}->[-1]->[0]
5479                                        ->manakai_local_name,
5480                                    token => $token);
5481                  } else {
5482                    !!!cp ('t189');
5483                }                }
5484    
5485                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5486    
5487                $clear_up_to_marker->();                $clear_up_to_marker->();
5488    
5489                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5490    
5491                ## reprocess                ## reprocess
5492                redo B;                next B;
5493              } elsif ({              } elsif ({
5494                        body => 1, col => 1, colgroup => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
                       html => 1, tbody => 1, td => 1, tfoot => 1,  
                       th => 1, thead => 1, tr => 1,  
5495                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5496                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5497                ## Ignore the token                  !!!cp ('t190');
5498                redo B;                  !!!parse-error (type => 'unmatched end tag',
5499              } else {                                  text => $token->{tag_name}, token => $token);
               #  
             }  
           } else {  
             #  
           }  
                 
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in column group') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'col') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'colgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html') {  
                 !!!parse-error (type => 'unmatched end tag:colgroup');  
5500                  ## Ignore the token                  ## Ignore the token
5501                  !!!next-token;                  !!!next-token;
5502                  redo B;                  next B;
5503                } else {                } else {
5504                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
5505                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5506                }                }
5507              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5508                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
5509                          thead => 1, tr => 1,
5510                         }->{$token->{tag_name}} and
5511                         $self->{insertion_mode} == IN_CAPTION_IM) {
5512                  !!!cp ('t192');
5513                  !!!parse-error (type => 'unmatched end tag',
5514                                  text => $token->{tag_name}, token => $token);
5515                ## Ignore the token                ## Ignore the token
5516                !!!next-token;                !!!next-token;
5517                redo B;                next B;
5518              } else {              } else {
5519                #                !!!cp ('t193');
5520                  #
5521              }              }
5522            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5523              #          for my $entry (@{$self->{open_elements}}) {
5524              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5525                !!!cp ('t75');
5526                !!!parse-error (type => 'in body:#eof', token => $token);
5527                last;
5528            }            }
5529            }
5530    
5531            ## As if </colgroup>          ## Stop parsing.
5532            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
5533              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
5534              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5535          }
5536    
5537          $insert = $insert_to_current;
5538          #
5539        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5540          if ($token->{type} == CHARACTER_TOKEN) {
5541            if (not $open_tables->[-1]->[1] and # tainted
5542                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5543              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5544                  
5545              unless (length $token->{data}) {
5546                !!!cp ('t194');
5547              !!!next-token;              !!!next-token;
5548              redo B;              next B;
5549            } else {            } else {
5550              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
5551            }            }
5552          } elsif ($self->{insertion_mode} eq 'in table body') {          }
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
5553    
5554              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5555    
5556              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5557              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
5558              ## into the current node" while characters might not be              ## into the current node" while characters might not be
5559              ## result in a new Text node.              ## result in a new Text node.
5560              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5561                
5562              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5563                # MUST                # MUST
5564                my $foster_parent_element;                my $foster_parent_element;
5565                my $next_sibling;                my $next_sibling;
5566                my $prev_sibling;                my $prev_sibling;
5567                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5568                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5569                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5570                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5571                        !!!cp ('t196');
5572                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5573                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5574                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5575                    } else {                    } else {
5576                        !!!cp ('t197');
5577                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5578                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5579                    }                    }
# Line 3727  sub _tree_construction_main ($) { Line 5585  sub _tree_construction_main ($) {
5585                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5586                if (defined $prev_sibling and                if (defined $prev_sibling and
5587                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5588                    !!!cp ('t198');
5589                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5590                } else {                } else {
5591                    !!!cp ('t199');
5592                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5593                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5594                     $next_sibling);                     $next_sibling);
5595                }                }
5596              } else {            $open_tables->[-1]->[1] = 1; # tainted
5597                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5598              !!!cp ('t200');
5599              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5600            }
5601                
5602            !!!next-token;
5603            next B;
5604          } elsif ($token->{type} == START_TAG_TOKEN) {
5605            if ({
5606                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5607                 th => 1, td => 1,
5608                }->{$token->{tag_name}}) {
5609              if ($self->{insertion_mode} == IN_TABLE_IM) {
5610                ## Clear back to table context
5611                while (not ($self->{open_elements}->[-1]->[1]
5612                                & TABLE_SCOPING_EL)) {
5613                  !!!cp ('t201');
5614                  pop @{$self->{open_elements}};
5615              }              }
5616                            
5617              !!!next-token;              !!!insert-element ('tbody',, $token);
5618              redo B;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5619            } elsif ($token->{type} eq 'comment') {              ## reprocess in the "in table body" insertion mode...
5620              ## Copied from 'in table'            }
5621              my $comment = $self->{document}->create_comment ($token->{data});            
5622              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5623              !!!next-token;              unless ($token->{tag_name} eq 'tr') {
5624              redo B;                !!!cp ('t202');
5625            } elsif ($token->{type} eq 'start tag') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
5626              if ({              }
5627                   tr => 1,                  
5628                   th => 1, td => 1,              ## Clear back to table body context
5629                  }->{$token->{tag_name}}) {              while (not ($self->{open_elements}->[-1]->[1]
5630                unless ($token->{tag_name} eq 'tr') {                              & TABLE_ROWS_SCOPING_EL)) {
5631                  !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t203');
5632                  ## ISSUE: Can this case be reached?
5633                  pop @{$self->{open_elements}};
5634                }
5635                    
5636                    $self->{insertion_mode} = IN_ROW_IM;
5637                    if ($token->{tag_name} eq 'tr') {
5638                      !!!cp ('t204');
5639                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5640                      !!!nack ('t204');
5641                      !!!next-token;
5642                      next B;
5643                    } else {
5644                      !!!cp ('t205');
5645                      !!!insert-element ('tr',, $token);
5646                      ## reprocess in the "in row" insertion mode
5647                    }
5648                  } else {
5649                    !!!cp ('t206');
5650                }                }
5651    
5652                ## Clear back to table body context                ## Clear back to table row context
5653                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5654                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5655                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5656                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5657                }                }
5658                                
5659                $self->{insertion_mode} = 'in row';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5660                if ($token->{tag_name} eq 'tr') {                $self->{insertion_mode} = IN_CELL_IM;
5661                  !!!insert-element ($token->{tag_name}, $token->{attributes});  
5662                  !!!next-token;                push @$active_formatting_elements, ['#marker', ''];
5663                } else {                
5664                  !!!insert-element ('tr');                !!!nack ('t207.1');
5665                  ## reprocess                !!!next-token;
5666                }                next B;
               redo B;  
5667              } elsif ({              } elsif ({
5668                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5669                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5670                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5671                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5672                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
5673                my $i;                  ## As if </tr>
5674                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
5675                  my $node = $self->{open_elements}->[$_];                  my $i;
5676                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5677                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
5678                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
5679                    $i = $_;                      !!!cp ('t208');
5680                    last INSCOPE;                      $i = $_;
5681                  } elsif ({                      last INSCOPE;
5682                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5683                           }->{$node->[1]}) {                      !!!cp ('t209');
5684                    last INSCOPE;                      last INSCOPE;
5685                      }
5686                    } # INSCOPE
5687                    unless (defined $i) {
5688                      !!!cp ('t210');
5689    ## TODO: This type is wrong.
5690                      !!!parse-error (type => 'unmacthed end tag',
5691                                      text => $token->{tag_name}, token => $token);
5692                      ## Ignore the token
5693                      !!!nack ('t210.1');
5694                      !!!next-token;
5695                      next B;
5696                    }
5697                    
5698                    ## Clear back to table row context
5699                    while (not ($self->{open_elements}->[-1]->[1]
5700                                    & TABLE_ROW_SCOPING_EL)) {
5701                      !!!cp ('t211');
5702                      ## ISSUE: Can this case be reached?
5703                      pop @{$self->{open_elements}};
5704                    }
5705                    
5706                    pop @{$self->{open_elements}}; # tr
5707                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5708                    if ($token->{tag_name} eq 'tr') {
5709                      !!!cp ('t212');
5710                      ## reprocess
5711                      !!!ack-later;
5712                      next B;
5713                    } else {
5714                      !!!cp ('t213');
5715                      ## reprocess in the "in table body" insertion mode...
5716                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5717                }                }
5718    
5719                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5720                while (not {                  ## have an element in table scope
5721                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5722                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5723                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5724                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5725                        !!!cp ('t214');
5726                        $i = $_;
5727                        last INSCOPE;
5728                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5729                        !!!cp ('t215');
5730                        last INSCOPE;
5731                      }
5732                    } # INSCOPE
5733                    unless (defined $i) {
5734                      !!!cp ('t216');
5735    ## TODO: This erorr type is wrong.
5736                      !!!parse-error (type => 'unmatched end tag',
5737                                      text => $token->{tag_name}, token => $token);
5738                      ## Ignore the token
5739                      !!!nack ('t216.1');
5740                      !!!next-token;
5741                      next B;
5742                    }
5743    
5744                    ## Clear back to table body context
5745                    while (not ($self->{open_elements}->[-1]->[1]
5746                                    & TABLE_ROWS_SCOPING_EL)) {
5747                      !!!cp ('t217');
5748                      ## ISSUE: Can this state be reached?
5749                      pop @{$self->{open_elements}};
5750                    }
5751                    
5752                    ## As if <{current node}>
5753                    ## have an element in table scope
5754                    ## true by definition
5755                    
5756                    ## Clear back to table body context
5757                    ## nop by definition
5758                    
5759                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5760                    $self->{insertion_mode} = IN_TABLE_IM;
5761                    ## reprocess in "in table" insertion mode...
5762                  } else {
5763                    !!!cp ('t218');
5764                }                }
5765    
5766                ## As if <{current node}>                if ($token->{tag_name} eq 'col') {
5767                ## have an element in table scope                  ## Clear back to table context
5768                ## true by definition                  while (not ($self->{open_elements}->[-1]->[1]
5769                                    & TABLE_SCOPING_EL)) {
5770                ## Clear back to table body context                    !!!cp ('t219');
5771                ## nop by definition                    ## ISSUE: Can this state be reached?
5772                      pop @{$self->{open_elements}};
5773                pop @{$self->{open_elements}};                  }
5774                $self->{insertion_mode} = 'in table';                  
5775                ## reprocess                  !!!insert-element ('colgroup',, $token);
5776                redo B;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5777                    ## reprocess
5778                    !!!ack-later;
5779                    next B;
5780                  } elsif ({
5781                            caption => 1,
5782                            colgroup => 1,
5783                            tbody => 1, tfoot => 1, thead => 1,
5784                           }->{$token->{tag_name}}) {
5785                    ## Clear back to table context
5786                    while (not ($self->{open_elements}->[-1]->[1]
5787                                    & TABLE_SCOPING_EL)) {
5788                      !!!cp ('t220');
5789                      ## ISSUE: Can this state be reached?
5790                      pop @{$self->{open_elements}};
5791                    }
5792                    
5793                    push @$active_formatting_elements, ['#marker', '']
5794                        if $token->{tag_name} eq 'caption';
5795                    
5796                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5797                    $self->{insertion_mode} = {
5798                                               caption => IN_CAPTION_IM,
5799                                               colgroup => IN_COLUMN_GROUP_IM,
5800                                               tbody => IN_TABLE_BODY_IM,
5801                                               tfoot => IN_TABLE_BODY_IM,
5802                                               thead => IN_TABLE_BODY_IM,
5803                                              }->{$token->{tag_name}};
5804                    !!!next-token;
5805                    !!!nack ('t220.1');
5806                    next B;
5807                  } else {
5808                    die "$0: in table: <>: $token->{tag_name}";
5809                  }
5810              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5811                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5812                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5813                                      ->manakai_local_name,
5814                                  token => $token);
5815    
5816                ## As if </table>                ## As if </table>
5817                ## have a table element in table scope                ## have a table element in table scope
5818                my $i;                my $i;
5819                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5820                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5821                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5822                      !!!cp ('t221');
5823                    $i = $_;                    $i = $_;
5824                    last INSCOPE;                    last INSCOPE;
5825                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5826                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5827                    last INSCOPE;                    last INSCOPE;
5828                  }                  }
5829                } # INSCOPE                } # INSCOPE
5830                unless (defined $i) {                unless (defined $i) {
5831                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5832    ## TODO: The following is wrong, maybe.
5833                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5834                                    token => $token);
5835                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5836                    !!!nack ('t223.1');
5837                  !!!next-token;                  !!!next-token;
5838                  redo B;                  next B;
5839                }                }
5840                                
5841    ## TODO: Followings are removed from the latest spec.
5842                ## generate implied end tags                ## generate implied end tags
5843                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5844                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5845                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5846                }                }
5847    
5848                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5849                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5850                    ## NOTE: |<table><tr><table>|
5851                    !!!parse-error (type => 'not closed',
5852                                    text => $self->{open_elements}->[-1]->[0]
5853                                        ->manakai_local_name,
5854                                    token => $token);
5855                  } else {
5856                    !!!cp ('t226');
5857                }                }
5858    
5859                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5860                  pop @{$open_tables};
5861    
5862                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5863    
5864                ## reprocess            ## reprocess
5865                redo B;            !!!ack-later;
5866              } else {            next B;
5867                #          } elsif ($token->{tag_name} eq 'style') {
5868              }            if (not $open_tables->[-1]->[1]) { # tainted
5869            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5870              if ({              ## NOTE: This is a "as if in head" code clone.
5871                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5872                  }->{$token->{tag_name}}) {              next B;
5873                ## have an element in table scope            } else {
5874                my $i;              !!!cp ('t227.7');
5875                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              #
5876                  my $node = $self->{open_elements}->[$_];            }
5877                  if ($node->[1] eq $token->{tag_name}) {          } elsif ($token->{tag_name} eq 'script') {
5878                    $i = $_;            if (not $open_tables->[-1]->[1]) { # tainted
5879                    last INSCOPE;              !!!cp ('t227.6');
5880                  } elsif ({              ## NOTE: This is a "as if in head" code clone.
5881                            table => 1, html => 1,              $script_start_tag->();
5882                           }->{$node->[1]}) {              next B;
5883                    last INSCOPE;            } else {
5884                  }              !!!cp ('t227.5');
5885                } # INSCOPE              #
5886                unless (defined $i) {            }
5887                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'input') {
5888                  ## Ignore the token            if (not $open_tables->[-1]->[1]) { # tainted
5889                  !!!next-token;              if ($token->{attributes}->{type}) { ## TODO: case
5890                  redo B;                my $type = lc $token->{attributes}->{type}->{value};
5891                }                if ($type eq 'hidden') {
5892                    !!!cp ('t227.3');
5893                    !!!parse-error (type => 'in table',
5894                                    text => $token->{tag_name}, token => $token);
5895    
5896                ## Clear back to table body context                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
5897    
5898                pop @{$self->{open_elements}};                  ## TODO: form element pointer
               $self->{insertion_mode} = 'in table';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ({  
                      tbody => 1, thead => 1, tfoot => 1,  
                     }->{$node->[1]}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
5899    
               ## Clear back to table body context  
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5900                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
5901    
5902                ## Clear back to table body context                  !!!next-token;
5903                ## nop by definition                  !!!ack ('t227.2.1');
5904                    next B;
5905                pop @{$self->{open_elements}};                } else {
5906                $self->{insertion_mode} = 'in table';                  !!!cp ('t227.2');
5907                ## reprocess                  #
5908                redo B;                }
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
5909              } else {              } else {
5910                  !!!cp ('t227.1');
5911                #                #
5912              }              }
5913            } else {            } else {
5914                !!!cp ('t227.4');
5915              #              #
5916            }            }
5917                      } else {
5918            ## As if in table            !!!cp ('t227');
5919            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5920            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in row') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
5921    
5922              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table', text => $token->{tag_name},
5923              ## ISSUE: Spec says that "whenever a node would be inserted                          token => $token);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $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) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
5924    
5925                push @$active_formatting_elements, ['#marker', ''];          $insert = $insert_to_foster;
5926                          #
5927                !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
5928                redo B;              if ($token->{tag_name} eq 'tr' and
5929              } elsif ({                  $self->{insertion_mode} == IN_ROW_IM) {
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
5930                ## have an element in table scope                ## have an element in table scope
5931                my $i;                my $i;
5932                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5933                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5934                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5935                      !!!cp ('t228');
5936                    $i = $_;                    $i = $_;
5937                    last INSCOPE;                    last INSCOPE;
5938                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5939                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5940                    last INSCOPE;                    last INSCOPE;
5941                  }                  }
5942                } # INSCOPE                } # INSCOPE
5943                unless (defined $i) {                unless (defined $i) {
5944                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
5945                    !!!parse-error (type => 'unmatched end tag',
5946                                    text => $token->{tag_name}, token => $token);
5947                  ## Ignore the token                  ## Ignore the token
5948                    !!!nack ('t230.1');
5949                  !!!next-token;                  !!!next-token;
5950                  redo B;                  next B;
5951                  } else {
5952                    !!!cp ('t232');
5953                }                }
5954    
5955                ## Clear back to table row context                ## Clear back to table row context
5956                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5957                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5958                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
5959                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
5960                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5961                }                }
5962    
5963                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5964                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5965                ## reprocess                !!!next-token;
5966                redo B;                !!!nack ('t231.1');
5967                  next B;
5968              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5969                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
5970                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
5971                    ## have an element in table scope
5972                ## As if </table>                  my $i;
5973                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5974                my $i;                    my $node = $self->{open_elements}->[$_];
5975                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
5976                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
5977                  if ($node->[1] eq 'table') {                      $i = $_;
5978                    $i = $_;                      last INSCOPE;
5979                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5980                  } elsif ({                      !!!cp ('t234');
5981                            table => 1, html => 1,                      last INSCOPE;
5982                           }->{$node->[1]}) {                    }
5983                    last INSCOPE;                  } # INSCOPE
5984                    unless (defined $i) {
5985                      !!!cp ('t235');
5986    ## TODO: The following is wrong.
5987                      !!!parse-error (type => 'unmatched end tag',
5988                                      text => $token->{type}, token => $token);
5989                      ## Ignore the token
5990                      !!!nack ('t236.1');
5991                      !!!next-token;
5992                      next B;
5993                  }                  }
5994                } # INSCOPE                  
5995                unless (defined $i) {                  ## Clear back to table row context
5996                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
5997                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
5998                  !!!next-token;                    !!!cp ('t236');
5999                  redo B;  ## ISSUE: Can this state be reached?
6000                }                    pop @{$self->{open_elements}};
6001                                  }
6002                ## generate implied end tags                  
6003                if ({                  pop @{$self->{open_elements}}; # tr
6004                     dd => 1, dt => 1, li => 1, p => 1,                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6005                     td => 1, th => 1, tr => 1,                  ## reprocess in the "in table body" insertion mode...
6006                    }->{$self->{open_elements}->[-1]->[1]}) {                }
6007                  !!!back-token; # <table>  
6008                  $token = {type => 'end tag', tag_name => 'table'};                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6009                  !!!back-token;                  ## have an element in table scope
6010                  $token = {type => 'end tag',                  my $i;
6011                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6012                  redo B;                    my $node = $self->{open_elements}->[$_];
6013                }                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6014                        !!!cp ('t237');
6015                if ($self->{open_elements}->[-1]->[1] ne 'table') {                      $i = $_;
6016                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      last INSCOPE;
6017                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6018                        !!!cp ('t238');
6019                        last INSCOPE;
6020                      }
6021                    } # INSCOPE
6022                    unless (defined $i) {
6023                      !!!cp ('t239');
6024                      !!!parse-error (type => 'unmatched end tag',
6025                                      text => $token->{tag_name}, token => $token);
6026                      ## Ignore the token
6027                      !!!nack ('t239.1');
6028                      !!!next-token;
6029                      next B;
6030                    }
6031                    
6032                    ## Clear back to table body context
6033                    while (not ($self->{open_elements}->[-1]->[1]
6034                                    & TABLE_ROWS_SCOPING_EL)) {
6035                      !!!cp ('t240');
6036                      pop @{$self->{open_elements}};
6037                    }
6038                    
6039                    ## As if <{current node}>
6040                    ## have an element in table scope
6041                    ## true by definition
6042                    
6043                    ## Clear back to table body context
6044                    ## nop by definition
6045                    
6046                    pop @{$self->{open_elements}};
6047                    $self->{insertion_mode} = IN_TABLE_IM;
6048                    ## reprocess in the "in table" insertion mode...
6049                }                }
6050    
6051                splice @{$self->{open_elements}}, $i;                ## NOTE: </table> in the "in table" insertion mode.
6052                  ## When you edit the code fragment below, please ensure that
6053                  ## the code for <table> in the "in table" insertion mode
6054                  ## is synced with it.
6055    
6056                $self->_reset_insertion_mode;                ## have a table element in table scope
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
6057                my $i;                my $i;
6058                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6059                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6060                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6061                      !!!cp ('t241');
6062                    $i = $_;                    $i = $_;
6063                    last INSCOPE;                    last INSCOPE;
6064                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6065                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6066                    last INSCOPE;                    last INSCOPE;
6067                  }                  }
6068                } # INSCOPE                } # INSCOPE
6069                unless (defined $i) {                unless (defined $i) {
6070                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6071                    !!!parse-error (type => 'unmatched end tag',
6072                                    text => $token->{tag_name}, token => $token);
6073                  ## Ignore the token                  ## Ignore the token
6074                    !!!nack ('t243.1');
6075                  !!!next-token;                  !!!next-token;
6076                  redo B;                  next B;
6077                }                }
6078                    
6079                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6080                while (not {                pop @{$open_tables};
6081                  tr => 1, html => 1,                
6082                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6083                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6084                  pop @{$self->{open_elements}};                !!!next-token;
6085                  next B;
6086                } elsif ({
6087                          tbody => 1, tfoot => 1, thead => 1,
6088                         }->{$token->{tag_name}} and
6089                         $self->{insertion_mode} & ROW_IMS) {
6090                  if ($self->{insertion_mode} == IN_ROW_IM) {
6091                    ## have an element in table scope
6092                    my $i;
6093                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6094                      my $node = $self->{open_elements}->[$_];
6095                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6096                        !!!cp ('t247');
6097                        $i = $_;
6098                        last INSCOPE;
6099                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6100                        !!!cp ('t248');
6101                        last INSCOPE;
6102                      }
6103                    } # INSCOPE
6104                      unless (defined $i) {
6105                        !!!cp ('t249');
6106                        !!!parse-error (type => 'unmatched end tag',
6107                                        text => $token->{tag_name}, token => $token);
6108                        ## Ignore the token
6109                        !!!nack ('t249.1');
6110                        !!!next-token;
6111                        next B;
6112                      }
6113                    
6114                    ## As if </tr>
6115                    ## have an element in table scope
6116                    my $i;
6117                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6118                      my $node = $self->{open_elements}->[$_];
6119                      if ($node->[1] & TABLE_ROW_EL) {
6120                        !!!cp ('t250');
6121                        $i = $_;
6122                        last INSCOPE;
6123                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6124                        !!!cp ('t251');
6125                        last INSCOPE;
6126                      }
6127                    } # INSCOPE
6128                      unless (defined $i) {
6129                        !!!cp ('t252');
6130                        !!!parse-error (type => 'unmatched end tag',
6131                                        text => 'tr', token => $token);
6132                        ## Ignore the token
6133                        !!!nack ('t252.1');
6134                        !!!next-token;
6135                        next B;
6136                      }
6137                    
6138                    ## Clear back to table row context
6139                    while (not ($self->{open_elements}->[-1]->[1]
6140                                    & TABLE_ROW_SCOPING_EL)) {
6141                      !!!cp ('t253');
6142    ## ISSUE: Can this case be reached?
6143                      pop @{$self->{open_elements}};
6144                    }
6145                    
6146                    pop @{$self->{open_elements}}; # tr
6147                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
6148                    ## reprocess in the "in table body" insertion mode...
6149                }                }
6150    
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </tr>  
6151                ## have an element in table scope                ## have an element in table scope
6152                my $i;                my $i;
6153                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6154                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6155                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6156                      !!!cp ('t254');
6157                    $i = $_;                    $i = $_;
6158                    last INSCOPE;                    last INSCOPE;
6159                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6160                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6161                    last INSCOPE;                    last INSCOPE;
6162                  }                  }
6163                } # INSCOPE                } # INSCOPE
6164                unless (defined $i) {                unless (defined $i) {
6165                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t256');
6166                    !!!parse-error (type => 'unmatched end tag',
6167                                    text => $token->{tag_name}, token => $token);
6168                  ## Ignore the token                  ## Ignore the token
6169                    !!!nack ('t256.1');
6170                  !!!next-token;                  !!!next-token;
6171                  redo B;                  next B;
6172                }                }
6173    
6174                ## Clear back to table row context                ## Clear back to table body context
6175                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6176                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6177                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6178                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6179                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6180                }                }
6181    
6182                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6183                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6184                ## reprocess                !!!nack ('t257.1');
6185                redo B;                !!!next-token;
6186                  next B;
6187              } elsif ({              } elsif ({
6188                        tbody => 1, tfoot => 1, thead => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6189                          html => 1, td => 1, th => 1,
6190                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6191                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6192                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6193                ## have an element in table scope            !!!cp ('t258');
6194                my $i;            !!!parse-error (type => 'unmatched end tag',
6195                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
6196                  my $node = $self->{open_elements}->[$_];            ## Ignore the token
6197                  if ($node->[1] eq $token->{tag_name}) {            !!!nack ('t258.1');
6198                    $i = $_;             !!!next-token;
6199                    last INSCOPE;            next B;
6200                  } elsif ({          } else {
6201                            table => 1, html => 1,            !!!cp ('t259');
6202                           }->{$node->[1]}) {            !!!parse-error (type => 'in table:/',
6203                    last INSCOPE;                            text => $token->{tag_name}, token => $token);
6204                  }  
6205                } # INSCOPE            $insert = $insert_to_foster;
6206                unless (defined $i) {            #
6207                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          }
6208                  ## Ignore the token        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6209            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6210                    @{$self->{open_elements}} == 1) { # redundant, maybe
6211              !!!parse-error (type => 'in body:#eof', token => $token);
6212              !!!cp ('t259.1');
6213              #
6214            } else {
6215              !!!cp ('t259.2');
6216              #
6217            }
6218    
6219            ## Stop parsing
6220            last B;
6221          } else {
6222            die "$0: $token->{type}: Unknown token type";
6223          }
6224        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6225              if ($token->{type} == CHARACTER_TOKEN) {
6226                if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6227                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6228                  unless (length $token->{data}) {
6229                    !!!cp ('t260');
6230                  !!!next-token;                  !!!next-token;
6231                  redo B;                  next B;
6232                }                }
6233                }
6234                ## As if </tr>              
6235                ## have an element in table scope              !!!cp ('t261');
6236                my $i;              #
6237                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6238                  my $node = $self->{open_elements}->[$_];              if ($token->{tag_name} eq 'col') {
6239                  if ($node->[1] eq 'tr') {                !!!cp ('t262');
6240                    $i = $_;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6241                    last INSCOPE;                pop @{$self->{open_elements}};
6242                  } elsif ({                !!!ack ('t262.1');
6243                            table => 1, html => 1,                !!!next-token;
6244                           }->{$node->[1]}) {                next B;
6245                    last INSCOPE;              } else {
6246                  }                !!!cp ('t263');
6247                } # INSCOPE                #
6248                unless (defined $i) {              }
6249                  !!!parse-error (type => 'unmatched end tag:tr');            } elsif ($token->{type} == END_TAG_TOKEN) {
6250                if ($token->{tag_name} eq 'colgroup') {
6251                  if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6252                    !!!cp ('t264');
6253                    !!!parse-error (type => 'unmatched end tag',
6254                                    text => 'colgroup', token => $token);
6255                  ## Ignore the token                  ## Ignore the token
6256                  !!!next-token;                  !!!next-token;
6257                  redo B;                  next B;
6258                }                } else {
6259                    !!!cp ('t265');
6260                ## Clear back to table row context                  pop @{$self->{open_elements}}; # colgroup
6261                while (not {                  $self->{insertion_mode} = IN_TABLE_IM;
6262                  tr => 1, html => 1,                  !!!next-token;
6263                }->{$self->{open_elements}->[-1]->[1]}) {                  next B;            
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
6264                }                }
6265                } elsif ($token->{tag_name} eq 'col') {
6266                pop @{$self->{open_elements}}; # tr                !!!cp ('t266');
6267                $self->{insertion_mode} = 'in table body';                !!!parse-error (type => 'unmatched end tag',
6268                ## reprocess                                text => 'col', token => $token);
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1, td => 1, th => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
6269                ## Ignore the token                ## Ignore the token
6270                !!!next-token;                !!!next-token;
6271                redo B;                next B;
6272              } else {              } else {
6273                #                !!!cp ('t267');
6274                  #
6275              }              }
6276          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6277            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6278                @{$self->{open_elements}} == 1) { # redundant, maybe
6279              !!!cp ('t270.2');
6280              ## Stop parsing.
6281              last B;
6282            } else {
6283              ## NOTE: As if </colgroup>.
6284              !!!cp ('t270.1');
6285              pop @{$self->{open_elements}}; # colgroup
6286              $self->{insertion_mode} = IN_TABLE_IM;
6287              ## Reprocess.
6288              next B;
6289            }
6290          } else {
6291            die "$0: $token->{type}: Unknown token type";
6292          }
6293    
6294              ## As if </colgroup>
6295              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6296                !!!cp ('t269');
6297    ## TODO: Wrong error type?
6298                !!!parse-error (type => 'unmatched end tag',
6299                                text => 'colgroup', token => $token);
6300                ## Ignore the token
6301                !!!nack ('t269.1');
6302                !!!next-token;
6303                next B;
6304            } else {            } else {
6305              #              !!!cp ('t270');
6306                pop @{$self->{open_elements}}; # colgroup
6307                $self->{insertion_mode} = IN_TABLE_IM;
6308                !!!ack-later;
6309                ## reprocess
6310                next B;
6311              }
6312        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6313          if ($token->{type} == CHARACTER_TOKEN) {
6314            !!!cp ('t271');
6315            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6316            !!!next-token;
6317            next B;
6318          } elsif ($token->{type} == START_TAG_TOKEN) {
6319            if ($token->{tag_name} eq 'option') {
6320              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6321                !!!cp ('t272');
6322                ## As if </option>
6323                pop @{$self->{open_elements}};
6324              } else {
6325                !!!cp ('t273');
6326            }            }
6327    
6328            ## As if in table            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6329            !!!parse-error (type => 'in table:'.$token->{tag_name});            !!!nack ('t273.1');
6330            $in_body->($insert_to_foster);            !!!next-token;
6331            redo B;            next B;
6332          } elsif ($self->{insertion_mode} eq 'in cell') {          } elsif ($token->{tag_name} eq 'optgroup') {
6333            if ($token->{type} eq 'character') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6334              ## NOTE: This is a code clone of "character in body".              !!!cp ('t274');
6335              $reconstruct_active_formatting_elements->($insert_to_current);              ## As if </option>
6336                            pop @{$self->{open_elements}};
6337              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            } else {
6338                !!!cp ('t275');
6339              }
6340    
6341              if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6342                !!!cp ('t276');
6343                ## As if </optgroup>
6344                pop @{$self->{open_elements}};
6345              } else {
6346                !!!cp ('t277');
6347              }
6348    
6349              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6350              !!!nack ('t277.1');
6351              !!!next-token;
6352              next B;
6353            } elsif ({
6354                       select => 1, input => 1, textarea => 1,
6355                     }->{$token->{tag_name}} or
6356                     ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6357                      {
6358                       caption => 1, table => 1,
6359                       tbody => 1, tfoot => 1, thead => 1,
6360                       tr => 1, td => 1, th => 1,
6361                      }->{$token->{tag_name}})) {
6362              ## TODO: The type below is not good - <select> is replaced by </select>
6363              !!!parse-error (type => 'not closed', text => 'select',
6364                              token => $token);
6365              ## NOTE: As if the token were </select> (<select> case) or
6366              ## as if there were </select> (otherwise).
6367              ## have an element in table scope
6368              my $i;
6369              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6370                my $node = $self->{open_elements}->[$_];
6371                if ($node->[1] & SELECT_EL) {
6372                  !!!cp ('t278');
6373                  $i = $_;
6374                  last INSCOPE;
6375                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6376                  !!!cp ('t279');
6377                  last INSCOPE;
6378                }
6379              } # INSCOPE
6380              unless (defined $i) {
6381                !!!cp ('t280');
6382                !!!parse-error (type => 'unmatched end tag',
6383                                text => 'select', token => $token);
6384                ## Ignore the token
6385                !!!nack ('t280.1');
6386              !!!next-token;              !!!next-token;
6387              redo B;              next B;
6388            } elsif ($token->{type} eq 'comment') {            }
6389              ## NOTE: This is a code clone of "comment in body".                
6390              my $comment = $self->{document}->create_comment ($token->{data});            !!!cp ('t281');
6391              $self->{open_elements}->[-1]->[0]->append_child ($comment);            splice @{$self->{open_elements}}, $i;
6392    
6393              $self->_reset_insertion_mode;
6394    
6395              if ($token->{tag_name} eq 'select') {
6396                !!!nack ('t281.2');
6397              !!!next-token;              !!!next-token;
6398              redo B;              next B;
6399            } elsif ($token->{type} eq 'start tag') {            } else {
6400              if ({              !!!cp ('t281.1');
6401                   caption => 1, col => 1, colgroup => 1,              !!!ack-later;
6402                   tbody => 1, td => 1, tfoot => 1, th => 1,              ## Reprocess the token.
6403                   thead => 1, tr => 1,              next B;
6404                  }->{$token->{tag_name}}) {            }
6405                ## have an element in table scope          } else {
6406                my $tn;            !!!cp ('t282');
6407                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            !!!parse-error (type => 'in select',
6408                  my $node = $self->{open_elements}->[$_];                            text => $token->{tag_name}, token => $token);
6409                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {            ## Ignore the token
6410                    $tn = $node->[1];            !!!nack ('t282.1');
6411                    last INSCOPE;            !!!next-token;
6412                  } elsif ({            next B;
6413                            table => 1, html => 1,          }
6414                           }->{$node->[1]}) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6415                    last INSCOPE;          if ($token->{tag_name} eq 'optgroup') {
6416                  }            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6417                } # INSCOPE                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6418                unless (defined $tn) {              !!!cp ('t283');
6419                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              ## As if </option>
6420                  ## Ignore the token              splice @{$self->{open_elements}}, -2;
6421                  !!!next-token;            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6422                  redo B;              !!!cp ('t284');
6423                }              pop @{$self->{open_elements}};
6424              } else {
6425                !!!cp ('t285');
6426                !!!parse-error (type => 'unmatched end tag',
6427                                text => $token->{tag_name}, token => $token);
6428                ## Ignore the token
6429              }
6430              !!!nack ('t285.1');
6431              !!!next-token;
6432              next B;
6433            } elsif ($token->{tag_name} eq 'option') {
6434              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6435                !!!cp ('t286');
6436                pop @{$self->{open_elements}};
6437              } else {
6438                !!!cp ('t287');
6439                !!!parse-error (type => 'unmatched end tag',
6440                                text => $token->{tag_name}, token => $token);
6441                ## Ignore the token
6442              }
6443              !!!nack ('t287.1');
6444              !!!next-token;
6445              next B;
6446            } elsif ($token->{tag_name} eq 'select') {
6447              ## have an element in table scope
6448              my $i;
6449              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6450                my $node = $self->{open_elements}->[$_];
6451                if ($node->[1] & SELECT_EL) {
6452                  !!!cp ('t288');
6453                  $i = $_;
6454                  last INSCOPE;
6455                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6456                  !!!cp ('t289');
6457                  last INSCOPE;
6458                }
6459              } # INSCOPE
6460              unless (defined $i) {
6461                !!!cp ('t290');
6462                !!!parse-error (type => 'unmatched end tag',
6463                                text => $token->{tag_name}, token => $token);
6464                ## Ignore the token
6465                !!!nack ('t290.1');
6466                !!!next-token;
6467                next B;
6468              }
6469                  
6470              !!!cp ('t291');
6471              splice @{$self->{open_elements}}, $i;
6472    
6473                ## Close the cell            $self->_reset_insertion_mode;
6474                !!!back-token; # <?>  
6475                $token = {type => 'end tag', tag_name => $tn};            !!!nack ('t291.1');
6476                redo B;            !!!next-token;
6477              } else {            next B;
6478                #          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6479                     {
6480                      caption => 1, table => 1, tbody => 1,
6481                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6482                     }->{$token->{tag_name}}) {
6483    ## TODO: The following is wrong?
6484              !!!parse-error (type => 'unmatched end tag',
6485                              text => $token->{tag_name}, token => $token);
6486                  
6487              ## have an element in table scope
6488              my $i;
6489              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6490                my $node = $self->{open_elements}->[$_];
6491                if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6492                  !!!cp ('t292');
6493                  $i = $_;
6494                  last INSCOPE;
6495                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6496                  !!!cp ('t293');
6497                  last INSCOPE;
6498              }              }
6499            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6500              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {            unless (defined $i) {
6501                ## have an element in table scope              !!!cp ('t294');
6502                my $i;              ## Ignore the token
6503                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!nack ('t294.1');
6504                  my $node = $self->{open_elements}->[$_];              !!!next-token;
6505                  if ($node->[1] eq $token->{tag_name}) {              next B;
6506                    $i = $_;            }
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6507                                
6508                ## generate implied end tags            ## As if </select>
6509                if ({            ## have an element in table scope
6510                     dd => 1, dt => 1, li => 1, p => 1,            undef $i;
6511                     td => ($token->{tag_name} eq 'th'),            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6512                     th => ($token->{tag_name} eq 'td'),              my $node = $self->{open_elements}->[$_];
6513                     tr => 1,              if ($node->[1] & SELECT_EL) {
6514                    }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t295');
6515                  !!!back-token;                $i = $_;
6516                  $token = {type => 'end tag',                last INSCOPE;
6517                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6518                  redo B;  ## ISSUE: Can this state be reached?
6519                }                !!!cp ('t296');
6520                  last INSCOPE;
6521                }
6522              } # INSCOPE
6523              unless (defined $i) {
6524                !!!cp ('t297');
6525    ## TODO: The following error type is correct?
6526                !!!parse-error (type => 'unmatched end tag',
6527                                text => 'select', token => $token);
6528                ## Ignore the </select> token
6529                !!!nack ('t297.1');
6530                !!!next-token; ## TODO: ok?
6531                next B;
6532              }
6533                  
6534              !!!cp ('t298');
6535              splice @{$self->{open_elements}}, $i;
6536    
6537                if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            $self->_reset_insertion_mode;
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
6538    
6539                splice @{$self->{open_elements}}, $i;            !!!ack-later;
6540              ## reprocess
6541              next B;
6542            } else {
6543              !!!cp ('t299');
6544              !!!parse-error (type => 'in select:/',
6545                              text => $token->{tag_name}, token => $token);
6546              ## Ignore the token
6547              !!!nack ('t299.3');
6548              !!!next-token;
6549              next B;
6550            }
6551          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6552            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6553                    @{$self->{open_elements}} == 1) { # redundant, maybe
6554              !!!cp ('t299.1');
6555              !!!parse-error (type => 'in body:#eof', token => $token);
6556            } else {
6557              !!!cp ('t299.2');
6558            }
6559    
6560                $clear_up_to_marker->();          ## Stop parsing.
6561            last B;
6562          } else {
6563            die "$0: $token->{type}: Unknown token type";
6564          }
6565        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6566          if ($token->{type} == CHARACTER_TOKEN) {
6567            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6568              my $data = $1;
6569              ## As if in body
6570              $reconstruct_active_formatting_elements->($insert_to_current);
6571                  
6572              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6573              
6574              unless (length $token->{data}) {
6575                !!!cp ('t300');
6576                !!!next-token;
6577                next B;
6578              }
6579            }
6580            
6581            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6582              !!!cp ('t301');
6583              !!!parse-error (type => 'after html:#text', token => $token);
6584              #
6585            } else {
6586              !!!cp ('t302');
6587              ## "after body" insertion mode
6588              !!!parse-error (type => 'after body:#text', token => $token);
6589              #
6590            }
6591    
6592                $self->{insertion_mode} = 'in row';          $self->{insertion_mode} = IN_BODY_IM;
6593            ## reprocess
6594            next B;
6595          } elsif ($token->{type} == START_TAG_TOKEN) {
6596            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6597              !!!cp ('t303');
6598              !!!parse-error (type => 'after html',
6599                              text => $token->{tag_name}, token => $token);
6600              #
6601            } else {
6602              !!!cp ('t304');
6603              ## "after body" insertion mode
6604              !!!parse-error (type => 'after body',
6605                              text => $token->{tag_name}, token => $token);
6606              #
6607            }
6608    
6609                !!!next-token;          $self->{insertion_mode} = IN_BODY_IM;
6610                redo B;          !!!ack-later;
6611              } elsif ({          ## reprocess
6612                        body => 1, caption => 1, col => 1,          next B;
6613                        colgroup => 1, html => 1,        } elsif ($token->{type} == END_TAG_TOKEN) {
6614                       }->{$token->{tag_name}}) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6615                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t305');
6616                ## Ignore the token            !!!parse-error (type => 'after html:/',
6617                !!!next-token;                            text => $token->{tag_name}, token => $token);
6618                redo B;            
6619              } elsif ({            $self->{insertion_mode} = IN_BODY_IM;
6620                        table => 1, tbody => 1, tfoot => 1,            ## Reprocess.
6621                        thead => 1, tr => 1,            next B;
6622                       }->{$token->{tag_name}}) {          } else {
6623                ## have an element in table scope            !!!cp ('t306');
6624                my $i;          }
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   ## NOTE: There is exactly one |td| or |th| element  
                   ## in scope in the stack of open elements by definition.  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6625    
6626                ## Close the cell          ## "after body" insertion mode
6627                !!!back-token; # </?>          if ($token->{tag_name} eq 'html') {
6628                $token = {type => 'end tag', tag_name => $tn};            if (defined $self->{inner_html_node}) {
6629                redo B;              !!!cp ('t307');
6630              } else {              !!!parse-error (type => 'unmatched end tag',
6631                #                              text => 'html', token => $token);
6632              }              ## Ignore the token
6633                !!!next-token;
6634                next B;
6635            } else {            } else {
6636              #              !!!cp ('t308');
6637                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6638                !!!next-token;
6639                next B;
6640            }            }
6641            } else {
6642              !!!cp ('t309');
6643              !!!parse-error (type => 'after body:/',
6644                              text => $token->{tag_name}, token => $token);
6645    
6646              $self->{insertion_mode} = IN_BODY_IM;
6647              ## reprocess
6648              next B;
6649            }
6650          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6651            !!!cp ('t309.2');
6652            ## Stop parsing
6653            last B;
6654          } else {
6655            die "$0: $token->{type}: Unknown token type";
6656          }
6657        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6658          if ($token->{type} == CHARACTER_TOKEN) {
6659            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6660              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6661                        
6662            $in_body->($insert_to_current);            unless (length $token->{data}) {
6663            redo B;              !!!cp ('t310');
         } elsif ($self->{insertion_mode} eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
6664              !!!next-token;              !!!next-token;
6665              redo B;              next B;
6666            } elsif ($token->{type} eq 'comment') {            }
6667              my $comment = $self->{document}->create_comment ($token->{data});          }
6668              $self->{open_elements}->[-1]->[0]->append_child ($comment);          
6669            if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6670              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6671                !!!cp ('t311');
6672                !!!parse-error (type => 'in frameset:#text', token => $token);
6673              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6674                !!!cp ('t312');
6675                !!!parse-error (type => 'after frameset:#text', token => $token);
6676              } else { # "after after frameset"
6677                !!!cp ('t313');
6678                !!!parse-error (type => 'after html:#text', token => $token);
6679              }
6680              
6681              ## Ignore the token.
6682              if (length $token->{data}) {
6683                !!!cp ('t314');
6684                ## reprocess the rest of characters
6685              } else {
6686                !!!cp ('t315');
6687              !!!next-token;              !!!next-token;
6688              redo B;            }
6689            } elsif ($token->{type} eq 'start tag') {            next B;
6690              if ($token->{tag_name} eq 'option') {          }
6691                if ($self->{open_elements}->[-1]->[1] eq 'option') {          
6692                  ## As if </option>          die qq[$0: Character "$token->{data}"];
6693                  pop @{$self->{open_elements}};        } elsif ($token->{type} == START_TAG_TOKEN) {
6694                }          if ($token->{tag_name} eq 'frameset' and
6695                $self->{insertion_mode} == IN_FRAMESET_IM) {
6696              !!!cp ('t318');
6697              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6698              !!!nack ('t318.1');
6699              !!!next-token;
6700              next B;
6701            } elsif ($token->{tag_name} eq 'frame' and
6702                     $self->{insertion_mode} == IN_FRAMESET_IM) {
6703              !!!cp ('t319');
6704              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6705              pop @{$self->{open_elements}};
6706              !!!ack ('t319.1');
6707              !!!next-token;
6708              next B;
6709            } elsif ($token->{tag_name} eq 'noframes') {
6710              !!!cp ('t320');
6711              ## NOTE: As if in head.
6712              $parse_rcdata->(CDATA_CONTENT_MODEL);
6713              next B;
6714    
6715              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6716              ## has no parse error.
6717            } else {
6718              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6719                !!!cp ('t321');
6720                !!!parse-error (type => 'in frameset',
6721                                text => $token->{tag_name}, token => $token);
6722              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6723                !!!cp ('t322');
6724                !!!parse-error (type => 'after frameset',
6725                                text => $token->{tag_name}, token => $token);
6726              } else { # "after after frameset"
6727                !!!cp ('t322.2');
6728                !!!parse-error (type => 'after after frameset',
6729                                text => $token->{tag_name}, token => $token);
6730              }
6731              ## Ignore the token
6732              !!!nack ('t322.1');
6733              !!!next-token;
6734              next B;
6735            }
6736          } elsif ($token->{type} == END_TAG_TOKEN) {
6737            if ($token->{tag_name} eq 'frameset' and
6738                $self->{insertion_mode} == IN_FRAMESET_IM) {
6739              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6740                  @{$self->{open_elements}} == 1) {
6741                !!!cp ('t325');
6742                !!!parse-error (type => 'unmatched end tag',
6743                                text => $token->{tag_name}, token => $token);
6744                ## Ignore the token
6745                !!!next-token;
6746              } else {
6747                !!!cp ('t326');
6748                pop @{$self->{open_elements}};
6749                !!!next-token;
6750              }
6751    
6752                !!!insert-element ($token->{tag_name}, $token->{attributes});            if (not defined $self->{inner_html_node} and
6753                !!!next-token;                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6754                redo B;              !!!cp ('t327');
6755              } elsif ($token->{tag_name} eq 'optgroup') {              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6756                if ($self->{open_elements}->[-1]->[1] eq 'option') {            } else {
6757                  ## As if </option>              !!!cp ('t328');
6758                  pop @{$self->{open_elements}};            }
6759                }            next B;
6760            } elsif ($token->{tag_name} eq 'html' and
6761                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6762              !!!cp ('t329');
6763              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6764              !!!next-token;
6765              next B;
6766            } else {
6767              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6768                !!!cp ('t330');
6769                !!!parse-error (type => 'in frameset:/',
6770                                text => $token->{tag_name}, token => $token);
6771              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6772                !!!cp ('t330.1');
6773                !!!parse-error (type => 'after frameset:/',
6774                                text => $token->{tag_name}, token => $token);
6775              } else { # "after after html"
6776                !!!cp ('t331');
6777                !!!parse-error (type => 'after after frameset:/',
6778                                text => $token->{tag_name}, token => $token);
6779              }
6780              ## Ignore the token
6781              !!!next-token;
6782              next B;
6783            }
6784          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6785            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6786                    @{$self->{open_elements}} == 1) { # redundant, maybe
6787              !!!cp ('t331.1');
6788              !!!parse-error (type => 'in body:#eof', token => $token);
6789            } else {
6790              !!!cp ('t331.2');
6791            }
6792            
6793            ## Stop parsing
6794            last B;
6795          } else {
6796            die "$0: $token->{type}: Unknown token type";
6797          }
6798        } else {
6799          die "$0: $self->{insertion_mode}: Unknown insertion mode";
6800        }
6801    
6802                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {      ## "in body" insertion mode
6803                  ## As if </optgroup>      if ($token->{type} == START_TAG_TOKEN) {
6804                  pop @{$self->{open_elements}};        if ($token->{tag_name} eq 'script') {
6805                }          !!!cp ('t332');
6806            ## NOTE: This is an "as if in head" code clone
6807            $script_start_tag->();
6808            next B;
6809          } elsif ($token->{tag_name} eq 'style') {
6810            !!!cp ('t333');
6811            ## NOTE: This is an "as if in head" code clone
6812            $parse_rcdata->(CDATA_CONTENT_MODEL);
6813            next B;
6814          } elsif ({
6815                    base => 1, command => 1, eventsource => 1, link => 1,
6816                   }->{$token->{tag_name}}) {
6817            !!!cp ('t334');
6818            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6819            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6820            pop @{$self->{open_elements}};
6821            !!!ack ('t334.1');
6822            !!!next-token;
6823            next B;
6824          } elsif ($token->{tag_name} eq 'meta') {
6825            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6826            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6827            my $meta_el = pop @{$self->{open_elements}};
6828    
6829            unless ($self->{confident}) {
6830              if ($token->{attributes}->{charset}) {
6831                !!!cp ('t335');
6832                ## NOTE: Whether the encoding is supported or not is handled
6833                ## in the {change_encoding} callback.
6834                $self->{change_encoding}
6835                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6836                
6837                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6838                    ->set_user_data (manakai_has_reference =>
6839                                         $token->{attributes}->{charset}
6840                                             ->{has_reference});
6841              } elsif ($token->{attributes}->{content}) {
6842                if ($token->{attributes}->{content}->{value}
6843                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6844                        [\x09\x0A\x0C\x0D\x20]*=
6845                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6846                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6847                       /x) {
6848                  !!!cp ('t336');
6849                  ## NOTE: Whether the encoding is supported or not is handled
6850                  ## in the {change_encoding} callback.
6851                  $self->{change_encoding}
6852                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6853                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6854                      ->set_user_data (manakai_has_reference =>
6855                                           $token->{attributes}->{content}
6856                                                 ->{has_reference});
6857                }
6858              }
6859            } else {
6860              if ($token->{attributes}->{charset}) {
6861                !!!cp ('t337');
6862                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6863                    ->set_user_data (manakai_has_reference =>
6864                                         $token->{attributes}->{charset}
6865                                             ->{has_reference});
6866              }
6867              if ($token->{attributes}->{content}) {
6868                !!!cp ('t338');
6869                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6870                    ->set_user_data (manakai_has_reference =>
6871                                         $token->{attributes}->{content}
6872                                             ->{has_reference});
6873              }
6874            }
6875    
6876                !!!insert-element ($token->{tag_name}, $token->{attributes});          !!!ack ('t338.1');
6877                !!!next-token;          !!!next-token;
6878                redo B;          next B;
6879              } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'title') {
6880                !!!parse-error (type => 'not closed:select');          !!!cp ('t341');
6881                ## As if </select> instead          ## NOTE: This is an "as if in head" code clone
6882                ## have an element in table scope          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6883                my $i;          next B;
6884                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {        } elsif ($token->{tag_name} eq 'body') {
6885                  my $node = $self->{open_elements}->[$_];          !!!parse-error (type => 'in body', text => 'body', token => $token);
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:select');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6886                                
6887                splice @{$self->{open_elements}}, $i;          if (@{$self->{open_elements}} == 1 or
6888                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6889                $self->_reset_insertion_mode;            !!!cp ('t342');
6890              ## Ignore the token
6891                !!!next-token;          } else {
6892                redo B;            my $body_el = $self->{open_elements}->[1]->[0];
6893              } else {            for my $attr_name (keys %{$token->{attributes}}) {
6894                #              unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6895                  !!!cp ('t343');
6896                  $body_el->set_attribute_ns
6897                    (undef, [undef, $attr_name],
6898                     $token->{attributes}->{$attr_name}->{value});
6899              }              }
6900            } elsif ($token->{type} eq 'end tag') {            }
6901              if ($token->{tag_name} eq 'optgroup') {          }
6902                if ($self->{open_elements}->[-1]->[1] eq 'option' and          !!!nack ('t343.1');
6903                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {          !!!next-token;
6904                  ## As if </option>          next B;
6905                  splice @{$self->{open_elements}}, -2;        } elsif ({
6906                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {                  ## NOTE: Start tags for non-phrasing flow content elements
                 pop @{$self->{open_elements}};  
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 pop @{$self->{open_elements}};  
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               splice @{$self->{open_elements}}, $i;  
6907    
6908                $self->_reset_insertion_mode;                  ## NOTE: The normal one
6909                    address => 1, article => 1, aside => 1, blockquote => 1,
6910                    center => 1, datagrid => 1, details => 1, dialog => 1,
6911                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6912                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6913                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6914                    section => 1, ul => 1,
6915                    ## NOTE: As normal, but drops leading newline
6916                    pre => 1, listing => 1,
6917                    ## NOTE: As normal, but interacts with the form element pointer
6918                    form => 1,
6919                    
6920                    table => 1,
6921                    hr => 1,
6922                   }->{$token->{tag_name}}) {
6923            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6924              !!!cp ('t350');
6925              !!!parse-error (type => 'in form:form', token => $token);
6926              ## Ignore the token
6927              !!!nack ('t350.1');
6928              !!!next-token;
6929              next B;
6930            }
6931    
6932            ## has a p element in scope
6933            INSCOPE: for (reverse @{$self->{open_elements}}) {
6934              if ($_->[1] & P_EL) {
6935                !!!cp ('t344');
6936                !!!back-token; # <form>
6937                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6938                          line => $token->{line}, column => $token->{column}};
6939                next B;
6940              } elsif ($_->[1] & SCOPING_EL) {
6941                !!!cp ('t345');
6942                last INSCOPE;
6943              }
6944            } # INSCOPE
6945              
6946            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6947            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6948              !!!nack ('t346.1');
6949              !!!next-token;
6950              if ($token->{type} == CHARACTER_TOKEN) {
6951                $token->{data} =~ s/^\x0A//;
6952                unless (length $token->{data}) {
6953                  !!!cp ('t346');
6954                !!!next-token;                !!!next-token;
               redo B;  
             } elsif ({  
                       caption => 1, table => 1, tbody => 1,  
                       tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## As if </select>  
               ## have an element in table scope  
               undef $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'select') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:select');  
                 ## Ignore the </select> token  
                 !!!next-token; ## TODO: ok?  
                 redo B;  
               }  
                 
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
6955              } else {              } else {
6956                #                !!!cp ('t349');
6957              }              }
6958            } else {            } else {
6959              #              !!!cp ('t348');
6960            }            }
6961            } elsif ($token->{tag_name} eq 'form') {
6962              !!!cp ('t347.1');
6963              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6964    
6965            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!nack ('t347.2');
           ## Ignore the token  
6966            !!!next-token;            !!!next-token;
6967            redo B;          } elsif ($token->{tag_name} eq 'table') {
6968          } elsif ($self->{insertion_mode} eq 'after body') {            !!!cp ('t382');
6969            if ($token->{type} eq 'character') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6970              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            
6971                ## As if in body            $self->{insertion_mode} = IN_TABLE_IM;
               $reconstruct_active_formatting_elements->($insert_to_current);  
                 
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
6972    
6973                unless (length $token->{data}) {            !!!nack ('t382.1');
6974                  !!!next-token;            !!!next-token;
6975                  redo B;          } elsif ($token->{tag_name} eq 'hr') {
6976              !!!cp ('t386');
6977              pop @{$self->{open_elements}};
6978            
6979              !!!nack ('t386.1');
6980              !!!next-token;
6981            } else {
6982              !!!nack ('t347.1');
6983              !!!next-token;
6984            }
6985            next B;
6986          } elsif ($token->{tag_name} eq 'li') {
6987            ## NOTE: As normal, but imply </li> when there's another <li> ...
6988    
6989            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6990              ## Interpreted as <li><foo/></li><li/> (non-conforming)
6991              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6992              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6993              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6994              ## object (Fx)
6995              ## Generate non-tree (non-conforming)
6996              ## basefont (IE7 (where basefont is non-void)), center (IE),
6997              ## form (IE), hn (IE)
6998            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
6999              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7000              ## div (Fx, S)
7001    
7002            my $non_optional;
7003            my $i = -1;
7004    
7005            ## 1.
7006            for my $node (reverse @{$self->{open_elements}}) {
7007              if ($node->[1] & LI_EL) {
7008                ## 2. (a) As if </li>
7009                {
7010                  ## If no </li> - not applied
7011                  #
7012    
7013                  ## Otherwise
7014    
7015                  ## 1. generate implied end tags, except for </li>
7016                  #
7017    
7018                  ## 2. If current node != "li", parse error
7019                  if ($non_optional) {
7020                    !!!parse-error (type => 'not closed',
7021                                    text => $non_optional->[0]->manakai_local_name,
7022                                    token => $token);
7023                    !!!cp ('t355');
7024                  } else {
7025                    !!!cp ('t356');
7026                }                }
7027    
7028                  ## 3. Pop
7029                  splice @{$self->{open_elements}}, $i;
7030              }              }
7031                
7032                last; ## 2. (b) goto 5.
7033              } elsif (
7034                       ## NOTE: not "formatting" and not "phrasing"
7035                       ($node->[1] & SPECIAL_EL or
7036                        $node->[1] & SCOPING_EL) and
7037                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7038    
7039                       (not $node->[1] & ADDRESS_EL) &
7040                       (not $node->[1] & DIV_EL) &
7041                       (not $node->[1] & P_EL)) {
7042                ## 3.
7043                !!!cp ('t357');
7044                last; ## goto 5.
7045              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7046                !!!cp ('t358');
7047              #              #
7048              !!!parse-error (type => 'after body:#'.$token->{type});            } else {
7049            } elsif ($token->{type} eq 'comment') {              !!!cp ('t359');
7050              my $comment = $self->{document}->create_comment ($token->{data});              $non_optional ||= $node;
             $self->{open_elements}->[0]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             !!!parse-error (type => 'after body:'.$token->{tag_name});  
7051              #              #
7052            } elsif ($token->{type} eq 'end tag') {            }
7053              if ($token->{tag_name} eq 'html') {            ## 4.
7054                if (defined $self->{inner_html_node}) {            ## goto 2.
7055                  !!!parse-error (type => 'unmatched end tag:html');            $i--;
7056                  ## Ignore the token          }
7057                  !!!next-token;  
7058                  redo B;          ## 5. (a) has a |p| element in scope
7059            INSCOPE: for (reverse @{$self->{open_elements}}) {
7060              if ($_->[1] & P_EL) {
7061                !!!cp ('t353');
7062    
7063                ## NOTE: |<p><li>|, for example.
7064    
7065                !!!back-token; # <x>
7066                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7067                          line => $token->{line}, column => $token->{column}};
7068                next B;
7069              } elsif ($_->[1] & SCOPING_EL) {
7070                !!!cp ('t354');
7071                last INSCOPE;
7072              }
7073            } # INSCOPE
7074    
7075            ## 5. (b) insert
7076            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7077            !!!nack ('t359.1');
7078            !!!next-token;
7079            next B;
7080          } elsif ($token->{tag_name} eq 'dt' or
7081                   $token->{tag_name} eq 'dd') {
7082            ## NOTE: As normal, but imply </dt> or </dd> when ...
7083    
7084            my $non_optional;
7085            my $i = -1;
7086    
7087            ## 1.
7088            for my $node (reverse @{$self->{open_elements}}) {
7089              if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7090                ## 2. (a) As if </li>
7091                {
7092                  ## If no </li> - not applied
7093                  #
7094    
7095                  ## Otherwise
7096    
7097                  ## 1. generate implied end tags, except for </dt> or </dd>
7098                  #
7099    
7100                  ## 2. If current node != "dt"|"dd", parse error
7101                  if ($non_optional) {
7102                    !!!parse-error (type => 'not closed',
7103                                    text => $non_optional->[0]->manakai_local_name,
7104                                    token => $token);
7105                    !!!cp ('t355.1');
7106                } else {                } else {
7107                  $phase = 'trailing end';                  !!!cp ('t356.1');
                 !!!next-token;  
                 redo B;  
7108                }                }
7109              } else {  
7110                !!!parse-error (type => 'after body:/'.$token->{tag_name});                ## 3. Pop
7111                  splice @{$self->{open_elements}}, $i;
7112              }              }
7113    
7114                last; ## 2. (b) goto 5.
7115              } elsif (
7116                       ## NOTE: not "formatting" and not "phrasing"
7117                       ($node->[1] & SPECIAL_EL or
7118                        $node->[1] & SCOPING_EL) and
7119                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7120    
7121                       (not $node->[1] & ADDRESS_EL) &
7122                       (not $node->[1] & DIV_EL) &
7123                       (not $node->[1] & P_EL)) {
7124                ## 3.
7125                !!!cp ('t357.1');
7126                last; ## goto 5.
7127              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7128                !!!cp ('t358.1');
7129                #
7130            } else {            } else {
7131              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t359.1');
7132                $non_optional ||= $node;
7133                #
7134            }            }
7135              ## 4.
7136              ## goto 2.
7137              $i--;
7138            }
7139    
7140            $self->{insertion_mode} = 'in body';          ## 5. (a) has a |p| element in scope
7141            ## reprocess          INSCOPE: for (reverse @{$self->{open_elements}}) {
7142            redo B;            if ($_->[1] & P_EL) {
7143          } elsif ($self->{insertion_mode} eq 'in frameset') {              !!!cp ('t353.1');
7144            if ($token->{type} eq 'character') {              !!!back-token; # <x>
7145              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7146                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                        line => $token->{line}, column => $token->{column}};
7147                next B;
7148              } elsif ($_->[1] & SCOPING_EL) {
7149                !!!cp ('t354.1');
7150                last INSCOPE;
7151              }
7152            } # INSCOPE
7153    
7154                unless (length $token->{data}) {          ## 5. (b) insert
7155                  !!!next-token;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7156                  redo B;          !!!nack ('t359.2');
7157            !!!next-token;
7158            next B;
7159          } elsif ($token->{tag_name} eq 'plaintext') {
7160            ## NOTE: As normal, but effectively ends parsing
7161    
7162            ## has a p element in scope
7163            INSCOPE: for (reverse @{$self->{open_elements}}) {
7164              if ($_->[1] & P_EL) {
7165                !!!cp ('t367');
7166                !!!back-token; # <plaintext>
7167                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7168                          line => $token->{line}, column => $token->{column}};
7169                next B;
7170              } elsif ($_->[1] & SCOPING_EL) {
7171                !!!cp ('t368');
7172                last INSCOPE;
7173              }
7174            } # INSCOPE
7175              
7176            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7177              
7178            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7179              
7180            !!!nack ('t368.1');
7181            !!!next-token;
7182            next B;
7183          } elsif ($token->{tag_name} eq 'a') {
7184            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7185              my $node = $active_formatting_elements->[$i];
7186              if ($node->[1] & A_EL) {
7187                !!!cp ('t371');
7188                !!!parse-error (type => 'in a:a', token => $token);
7189                
7190                !!!back-token; # <a>
7191                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7192                          line => $token->{line}, column => $token->{column}};
7193                $formatting_end_tag->($token);
7194                
7195                AFE2: for (reverse 0..$#$active_formatting_elements) {
7196                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7197                    !!!cp ('t372');
7198                    splice @$active_formatting_elements, $_, 1;
7199                    last AFE2;
7200                }                }
7201              }              } # AFE2
7202                OE: for (reverse 0..$#{$self->{open_elements}}) {
7203                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7204                    !!!cp ('t373');
7205                    splice @{$self->{open_elements}}, $_, 1;
7206                    last OE;
7207                  }
7208                } # OE
7209                last AFE;
7210              } elsif ($node->[0] eq '#marker') {
7211                !!!cp ('t374');
7212                last AFE;
7213              }
7214            } # AFE
7215              
7216            $reconstruct_active_formatting_elements->($insert_to_current);
7217    
7218              #          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7219            } elsif ($token->{type} eq 'comment') {          push @$active_formatting_elements, $self->{open_elements}->[-1];
7220              my $comment = $self->{document}->create_comment ($token->{data});  
7221              $self->{open_elements}->[-1]->[0]->append_child ($comment);          !!!nack ('t374.1');
7222            !!!next-token;
7223            next B;
7224          } elsif ($token->{tag_name} eq 'nobr') {
7225            $reconstruct_active_formatting_elements->($insert_to_current);
7226    
7227            ## has a |nobr| element in scope
7228            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7229              my $node = $self->{open_elements}->[$_];
7230              if ($node->[1] & NOBR_EL) {
7231                !!!cp ('t376');
7232                !!!parse-error (type => 'in nobr:nobr', token => $token);
7233                !!!back-token; # <nobr>
7234                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7235                          line => $token->{line}, column => $token->{column}};
7236                next B;
7237              } elsif ($node->[1] & SCOPING_EL) {
7238                !!!cp ('t377');
7239                last INSCOPE;
7240              }
7241            } # INSCOPE
7242            
7243            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7244            push @$active_formatting_elements, $self->{open_elements}->[-1];
7245            
7246            !!!nack ('t377.1');
7247            !!!next-token;
7248            next B;
7249          } elsif ($token->{tag_name} eq 'button') {
7250            ## has a button element in scope
7251            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7252              my $node = $self->{open_elements}->[$_];
7253              if ($node->[1] & BUTTON_EL) {
7254                !!!cp ('t378');
7255                !!!parse-error (type => 'in button:button', token => $token);
7256                !!!back-token; # <button>
7257                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7258                          line => $token->{line}, column => $token->{column}};
7259                next B;
7260              } elsif ($node->[1] & SCOPING_EL) {
7261                !!!cp ('t379');
7262                last INSCOPE;
7263              }
7264            } # INSCOPE
7265              
7266            $reconstruct_active_formatting_elements->($insert_to_current);
7267              
7268            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7269    
7270            ## TODO: associate with $self->{form_element} if defined
7271    
7272            push @$active_formatting_elements, ['#marker', ''];
7273    
7274            !!!nack ('t379.1');
7275            !!!next-token;
7276            next B;
7277          } elsif ({
7278                    xmp => 1,
7279                    iframe => 1,
7280                    noembed => 1,
7281                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7282                    noscript => 0, ## TODO: 1 if scripting is enabled
7283                   }->{$token->{tag_name}}) {
7284            if ($token->{tag_name} eq 'xmp') {
7285              !!!cp ('t381');
7286              $reconstruct_active_formatting_elements->($insert_to_current);
7287            } else {
7288              !!!cp ('t399');
7289            }
7290            ## NOTE: There is an "as if in body" code clone.
7291            $parse_rcdata->(CDATA_CONTENT_MODEL);
7292            next B;
7293          } elsif ($token->{tag_name} eq 'isindex') {
7294            !!!parse-error (type => 'isindex', token => $token);
7295            
7296            if (defined $self->{form_element}) {
7297              !!!cp ('t389');
7298              ## Ignore the token
7299              !!!nack ('t389'); ## NOTE: Not acknowledged.
7300              !!!next-token;
7301              next B;
7302            } else {
7303              !!!ack ('t391.1');
7304    
7305              my $at = $token->{attributes};
7306              my $form_attrs;
7307              $form_attrs->{action} = $at->{action} if $at->{action};
7308              my $prompt_attr = $at->{prompt};
7309              $at->{name} = {name => 'name', value => 'isindex'};
7310              delete $at->{action};
7311              delete $at->{prompt};
7312              my @tokens = (
7313                            {type => START_TAG_TOKEN, tag_name => 'form',
7314                             attributes => $form_attrs,
7315                             line => $token->{line}, column => $token->{column}},
7316                            {type => START_TAG_TOKEN, tag_name => 'hr',
7317                             line => $token->{line}, column => $token->{column}},
7318                            {type => START_TAG_TOKEN, tag_name => 'p',
7319                             line => $token->{line}, column => $token->{column}},
7320                            {type => START_TAG_TOKEN, tag_name => 'label',
7321                             line => $token->{line}, column => $token->{column}},
7322                           );
7323              if ($prompt_attr) {
7324                !!!cp ('t390');
7325                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7326                               #line => $token->{line}, column => $token->{column},
7327                              };
7328              } else {
7329                !!!cp ('t391');
7330                push @tokens, {type => CHARACTER_TOKEN,
7331                               data => 'This is a searchable index. Insert your search keywords here: ',
7332                               #line => $token->{line}, column => $token->{column},
7333                              }; # SHOULD
7334                ## TODO: make this configurable
7335              }
7336              push @tokens,
7337                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7338                             line => $token->{line}, column => $token->{column}},
7339                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7340                            {type => END_TAG_TOKEN, tag_name => 'label',
7341                             line => $token->{line}, column => $token->{column}},
7342                            {type => END_TAG_TOKEN, tag_name => 'p',
7343                             line => $token->{line}, column => $token->{column}},
7344                            {type => START_TAG_TOKEN, tag_name => 'hr',
7345                             line => $token->{line}, column => $token->{column}},
7346                            {type => END_TAG_TOKEN, tag_name => 'form',
7347                             line => $token->{line}, column => $token->{column}};
7348              !!!back-token (@tokens);
7349              !!!next-token;
7350              next B;
7351            }
7352          } elsif ($token->{tag_name} eq 'textarea') {
7353            my $tag_name = $token->{tag_name};
7354            my $el;
7355            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7356            
7357            ## TODO: $self->{form_element} if defined
7358            $self->{content_model} = RCDATA_CONTENT_MODEL;
7359            delete $self->{escape}; # MUST
7360            
7361            $insert->($el);
7362            
7363            my $text = '';
7364            !!!nack ('t392.1');
7365            !!!next-token;
7366            if ($token->{type} == CHARACTER_TOKEN) {
7367              $token->{data} =~ s/^\x0A//;
7368              unless (length $token->{data}) {
7369                !!!cp ('t392');
7370              !!!next-token;              !!!next-token;
7371              redo B;            } else {
7372            } elsif ($token->{type} eq 'start tag') {              !!!cp ('t393');
7373              if ($token->{tag_name} eq 'frameset') {            }
7374                !!!insert-element ($token->{tag_name}, $token->{attributes});          } else {
7375                !!!next-token;            !!!cp ('t394');
7376                redo B;          }
7377              } elsif ($token->{tag_name} eq 'frame') {          while ($token->{type} == CHARACTER_TOKEN) {
7378                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t395');
7379              $text .= $token->{data};
7380              !!!next-token;
7381            }
7382            if (length $text) {
7383              !!!cp ('t396');
7384              $el->manakai_append_text ($text);
7385            }
7386            
7387            $self->{content_model} = PCDATA_CONTENT_MODEL;
7388            
7389            if ($token->{type} == END_TAG_TOKEN and
7390                $token->{tag_name} eq $tag_name) {
7391              !!!cp ('t397');
7392              ## Ignore the token
7393            } else {
7394              !!!cp ('t398');
7395              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7396            }
7397            !!!next-token;
7398            next B;
7399          } elsif ($token->{tag_name} eq 'rt' or
7400                   $token->{tag_name} eq 'rp') {
7401            ## has a |ruby| element in scope
7402            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7403              my $node = $self->{open_elements}->[$_];
7404              if ($node->[1] & RUBY_EL) {
7405                !!!cp ('t398.1');
7406                ## generate implied end tags
7407                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7408                  !!!cp ('t398.2');
7409                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
7410              }              }
7411            } elsif ($token->{type} eq 'end tag') {              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7412              if ($token->{tag_name} eq 'frameset') {                !!!cp ('t398.3');
7413                if ($self->{open_elements}->[-1]->[1] eq 'html' and                !!!parse-error (type => 'not closed',
7414                    @{$self->{open_elements}} == 1) {                                text => $self->{open_elements}->[-1]->[0]
7415                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                                    ->manakai_local_name,
7416                  ## Ignore the token                                token => $token);
7417                  !!!next-token;                pop @{$self->{open_elements}}
7418                } else {                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
                 pop @{$self->{open_elements}};  
                 !!!next-token;  
               }  
                 
               ## if not inner_html and  
               if ($self->{open_elements}->[-1]->[1] ne 'frameset') {  
                 $self->{insertion_mode} = 'after frameset';  
               }  
               redo B;  
             } else {  
               #  
7419              }              }
7420                last INSCOPE;
7421              } elsif ($node->[1] & SCOPING_EL) {
7422                !!!cp ('t398.4');
7423                last INSCOPE;
7424              }
7425            } # INSCOPE
7426    
7427            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7428    
7429            !!!nack ('t398.5');
7430            !!!next-token;
7431            redo B;
7432          } elsif ($token->{tag_name} eq 'math' or
7433                   $token->{tag_name} eq 'svg') {
7434            $reconstruct_active_formatting_elements->($insert_to_current);
7435    
7436            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7437    
7438            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7439    
7440            ## "adjust foreign attributes" - done in insert-element-f
7441            
7442            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7443            
7444            if ($self->{self_closing}) {
7445              pop @{$self->{open_elements}};
7446              !!!ack ('t398.1');
7447            } else {
7448              !!!cp ('t398.2');
7449              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7450              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7451              ## mode, "in body" (not "in foreign content") secondary insertion
7452              ## mode, maybe.
7453            }
7454    
7455            !!!next-token;
7456            next B;
7457          } elsif ({
7458                    caption => 1, col => 1, colgroup => 1, frame => 1,
7459                    frameset => 1, head => 1, option => 1, optgroup => 1,
7460                    tbody => 1, td => 1, tfoot => 1, th => 1,
7461                    thead => 1, tr => 1,
7462                   }->{$token->{tag_name}}) {
7463            !!!cp ('t401');
7464            !!!parse-error (type => 'in body',
7465                            text => $token->{tag_name}, token => $token);
7466            ## Ignore the token
7467            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7468            !!!next-token;
7469            next B;
7470          } elsif ($token->{tag_name} eq 'param' or
7471                   $token->{tag_name} eq 'source') {
7472            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7473            pop @{$self->{open_elements}};
7474    
7475            !!!ack ('t398.5');
7476            !!!next-token;
7477            redo B;
7478          } else {
7479            if ($token->{tag_name} eq 'image') {
7480              !!!cp ('t384');
7481              !!!parse-error (type => 'image', token => $token);
7482              $token->{tag_name} = 'img';
7483            } else {
7484              !!!cp ('t385');
7485            }
7486    
7487            ## NOTE: There is an "as if <br>" code clone.
7488            $reconstruct_active_formatting_elements->($insert_to_current);
7489            
7490            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7491    
7492            if ({
7493                 applet => 1, marquee => 1, object => 1,
7494                }->{$token->{tag_name}}) {
7495              !!!cp ('t380');
7496              push @$active_formatting_elements, ['#marker', ''];
7497              !!!nack ('t380.1');
7498            } elsif ({
7499                      b => 1, big => 1, em => 1, font => 1, i => 1,
7500                      s => 1, small => 1, strike => 1,
7501                      strong => 1, tt => 1, u => 1,
7502                     }->{$token->{tag_name}}) {
7503              !!!cp ('t375');
7504              push @$active_formatting_elements, $self->{open_elements}->[-1];
7505              !!!nack ('t375.1');
7506            } elsif ($token->{tag_name} eq 'input') {
7507              !!!cp ('t388');
7508              ## TODO: associate with $self->{form_element} if defined
7509              pop @{$self->{open_elements}};
7510              !!!ack ('t388.2');
7511            } elsif ({
7512                      area => 1, basefont => 1, bgsound => 1, br => 1,
7513                      embed => 1, img => 1, spacer => 1, wbr => 1,
7514                     }->{$token->{tag_name}}) {
7515              !!!cp ('t388.1');
7516              pop @{$self->{open_elements}};
7517              !!!ack ('t388.3');
7518            } elsif ($token->{tag_name} eq 'select') {
7519              ## TODO: associate with $self->{form_element} if defined
7520            
7521              if ($self->{insertion_mode} & TABLE_IMS or
7522                  $self->{insertion_mode} & BODY_TABLE_IMS or
7523                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7524                !!!cp ('t400.1');
7525                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7526            } else {            } else {
7527              #              !!!cp ('t400.2');
7528                $self->{insertion_mode} = IN_SELECT_IM;
7529            }            }
7530                        !!!nack ('t400.3');
7531            if (defined $token->{tag_name}) {          } else {
7532              !!!parse-error (type => 'in frameset:'.$token->{tag_name});            !!!nack ('t402');
7533            }
7534            
7535            !!!next-token;
7536            next B;
7537          }
7538        } elsif ($token->{type} == END_TAG_TOKEN) {
7539          if ($token->{tag_name} eq 'body') {
7540            ## has a |body| element in scope
7541            my $i;
7542            INSCOPE: {
7543              for (reverse @{$self->{open_elements}}) {
7544                if ($_->[1] & BODY_EL) {
7545                  !!!cp ('t405');
7546                  $i = $_;
7547                  last INSCOPE;
7548                } elsif ($_->[1] & SCOPING_EL) {
7549                  !!!cp ('t405.1');
7550                  last;
7551                }
7552              }
7553    
7554              !!!parse-error (type => 'start tag not allowed',
7555                              text => $token->{tag_name}, token => $token);
7556              ## NOTE: Ignore the token.
7557              !!!next-token;
7558              next B;
7559            } # INSCOPE
7560    
7561            for (@{$self->{open_elements}}) {
7562              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7563                !!!cp ('t403');
7564                !!!parse-error (type => 'not closed',
7565                                text => $_->[0]->manakai_local_name,
7566                                token => $token);
7567                last;
7568            } else {            } else {
7569              !!!parse-error (type => 'in frameset:#'.$token->{type});              !!!cp ('t404');
7570            }            }
7571            }
7572    
7573            $self->{insertion_mode} = AFTER_BODY_IM;
7574            !!!next-token;
7575            next B;
7576          } elsif ($token->{tag_name} eq 'html') {
7577            ## TODO: Update this code.  It seems that the code below is not
7578            ## up-to-date, though it has same effect as speced.
7579            if (@{$self->{open_elements}} > 1 and
7580                $self->{open_elements}->[1]->[1] & BODY_EL) {
7581              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7582                !!!cp ('t406');
7583                !!!parse-error (type => 'not closed',
7584                                text => $self->{open_elements}->[1]->[0]
7585                                    ->manakai_local_name,
7586                                token => $token);
7587              } else {
7588                !!!cp ('t407');
7589              }
7590              $self->{insertion_mode} = AFTER_BODY_IM;
7591              ## reprocess
7592              next B;
7593            } else {
7594              !!!cp ('t408');
7595              !!!parse-error (type => 'unmatched end tag',
7596                              text => $token->{tag_name}, token => $token);
7597            ## Ignore the token            ## Ignore the token
7598            !!!next-token;            !!!next-token;
7599            redo B;            next B;
7600          } elsif ($self->{insertion_mode} eq 'after frameset') {          }
7601            if ($token->{type} eq 'character') {        } elsif ({
7602              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                  ## NOTE: End tags for non-phrasing flow content elements
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
7603    
7604                unless (length $token->{data}) {                  ## NOTE: The normal ones
7605                  !!!next-token;                  address => 1, article => 1, aside => 1, blockquote => 1,
7606                  redo B;                  center => 1, datagrid => 1, details => 1, dialog => 1,
7607                }                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7608              }                  footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7609                    ol => 1, pre => 1, section => 1, ul => 1,
7610    
7611                    ## NOTE: As normal, but ... optional tags
7612                    dd => 1, dt => 1, li => 1,
7613    
7614                    applet => 1, button => 1, marquee => 1, object => 1,
7615                   }->{$token->{tag_name}}) {
7616            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7617            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7618            ## </dd>" code.
7619    
7620            ## has an element in scope
7621            my $i;
7622            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7623              my $node = $self->{open_elements}->[$_];
7624              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7625                !!!cp ('t410');
7626                $i = $_;
7627                last INSCOPE;
7628              } elsif ($node->[1] & SCOPING_EL) {
7629                !!!cp ('t411');
7630                last INSCOPE;
7631              }
7632            } # INSCOPE
7633    
7634            unless (defined $i) { # has an element in scope
7635              !!!cp ('t413');
7636              !!!parse-error (type => 'unmatched end tag',
7637                              text => $token->{tag_name}, token => $token);
7638              ## NOTE: Ignore the token.
7639            } else {
7640              ## Step 1. generate implied end tags
7641              while ({
7642                      ## END_TAG_OPTIONAL_EL
7643                      dd => ($token->{tag_name} ne 'dd'),
7644                      dt => ($token->{tag_name} ne 'dt'),
7645                      li => ($token->{tag_name} ne 'li'),
7646                      option => 1,
7647                      optgroup => 1,
7648                      p => 1,
7649                      rt => 1,
7650                      rp => 1,
7651                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7652                !!!cp ('t409');
7653                pop @{$self->{open_elements}};
7654              }
7655    
7656              ## Step 2.
7657              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7658                      ne $token->{tag_name}) {
7659                !!!cp ('t412');
7660                !!!parse-error (type => 'not closed',
7661                                text => $self->{open_elements}->[-1]->[0]
7662                                    ->manakai_local_name,
7663                                token => $token);
7664              } else {
7665                !!!cp ('t414');
7666              }
7667    
7668              ## Step 3.
7669              splice @{$self->{open_elements}}, $i;
7670    
7671              ## Step 4.
7672              $clear_up_to_marker->()
7673                  if {
7674                    applet => 1, button => 1, marquee => 1, object => 1,
7675                  }->{$token->{tag_name}};
7676            }
7677            !!!next-token;
7678            next B;
7679          } elsif ($token->{tag_name} eq 'form') {
7680            ## NOTE: As normal, but interacts with the form element pointer
7681    
7682            undef $self->{form_element};
7683    
7684            ## has an element in scope
7685            my $i;
7686            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7687              my $node = $self->{open_elements}->[$_];
7688              if ($node->[1] & FORM_EL) {
7689                !!!cp ('t418');
7690                $i = $_;
7691                last INSCOPE;
7692              } elsif ($node->[1] & SCOPING_EL) {
7693                !!!cp ('t419');
7694                last INSCOPE;
7695              }
7696            } # INSCOPE
7697    
7698            unless (defined $i) { # has an element in scope
7699              !!!cp ('t421');
7700              !!!parse-error (type => 'unmatched end tag',
7701                              text => $token->{tag_name}, token => $token);
7702              ## NOTE: Ignore the token.
7703            } else {
7704              ## Step 1. generate implied end tags
7705              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7706                !!!cp ('t417');
7707                pop @{$self->{open_elements}};
7708              }
7709              
7710              ## Step 2.
7711              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7712                      ne $token->{tag_name}) {
7713                !!!cp ('t417.1');
7714                !!!parse-error (type => 'not closed',
7715                                text => $self->{open_elements}->[-1]->[0]
7716                                    ->manakai_local_name,
7717                                token => $token);
7718              } else {
7719                !!!cp ('t420');
7720              }  
7721              
7722              ## Step 3.
7723              splice @{$self->{open_elements}}, $i;
7724            }
7725    
7726            !!!next-token;
7727            next B;
7728          } elsif ({
7729                    ## NOTE: As normal, except acts as a closer for any ...
7730                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7731                   }->{$token->{tag_name}}) {
7732            ## has an element in scope
7733            my $i;
7734            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7735              my $node = $self->{open_elements}->[$_];
7736              if ($node->[1] & HEADING_EL) {
7737                !!!cp ('t423');
7738                $i = $_;
7739                last INSCOPE;
7740              } elsif ($node->[1] & SCOPING_EL) {
7741                !!!cp ('t424');
7742                last INSCOPE;
7743              }
7744            } # INSCOPE
7745    
7746            unless (defined $i) { # has an element in scope
7747              !!!cp ('t425.1');
7748              !!!parse-error (type => 'unmatched end tag',
7749                              text => $token->{tag_name}, token => $token);
7750              ## NOTE: Ignore the token.
7751            } else {
7752              ## Step 1. generate implied end tags
7753              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7754                !!!cp ('t422');
7755                pop @{$self->{open_elements}};
7756              }
7757              
7758              ## Step 2.
7759              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7760                      ne $token->{tag_name}) {
7761                !!!cp ('t425');
7762                !!!parse-error (type => 'unmatched end tag',
7763                                text => $token->{tag_name}, token => $token);
7764              } else {
7765                !!!cp ('t426');
7766              }
7767    
7768              ## Step 3.
7769              splice @{$self->{open_elements}}, $i;
7770            }
7771            
7772            !!!next-token;
7773            next B;
7774          } elsif ($token->{tag_name} eq 'p') {
7775            ## NOTE: As normal, except </p> implies <p> and ...
7776    
7777            ## has an element in scope
7778            my $non_optional;
7779            my $i;
7780            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7781              my $node = $self->{open_elements}->[$_];
7782              if ($node->[1] & P_EL) {
7783                !!!cp ('t410.1');
7784                $i = $_;
7785                last INSCOPE;
7786              } elsif ($node->[1] & SCOPING_EL) {
7787                !!!cp ('t411.1');
7788                last INSCOPE;
7789              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7790                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7791                !!!cp ('t411.2');
7792              #              #
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               $phase = 'trailing end';  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
7793            } else {            } else {
7794                !!!cp ('t411.3');
7795                $non_optional ||= $node;
7796              #              #
7797            }            }
7798                      } # INSCOPE
7799            if (defined $token->{tag_name}) {  
7800              !!!parse-error (type => 'after frameset:'.$token->{tag_name});          if (defined $i) {
7801              ## 1. Generate implied end tags
7802              #
7803    
7804              ## 2. If current node != "p", parse error
7805              if ($non_optional) {
7806                !!!cp ('t412.1');
7807                !!!parse-error (type => 'not closed',
7808                                text => $non_optional->[0]->manakai_local_name,
7809                                token => $token);
7810            } else {            } else {
7811              !!!parse-error (type => 'after frameset:#'.$token->{type});              !!!cp ('t414.1');
7812            }            }
           ## Ignore the token  
           !!!next-token;  
           redo B;  
7813    
7814            ## ISSUE: An issue in spec there            ## 3. Pop
7815              splice @{$self->{open_elements}}, $i;
7816          } else {          } else {
7817            die "$0: $self->{insertion_mode}: Unknown insertion mode";            !!!cp ('t413.1');
7818              !!!parse-error (type => 'unmatched end tag',
7819                              text => $token->{tag_name}, token => $token);
7820    
7821              !!!cp ('t415.1');
7822              ## As if <p>, then reprocess the current token
7823              my $el;
7824              !!!create-element ($el, $HTML_NS, 'p',, $token);
7825              $insert->($el);
7826              ## NOTE: Not inserted into |$self->{open_elements}|.
7827          }          }
7828        }  
     } elsif ($phase eq 'trailing end') {  
       ## states in the main stage is preserved yet # MUST  
         
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'after html:#DOCTYPE');  
         ## Ignore the token  
7829          !!!next-token;          !!!next-token;
7830          redo B;          next B;
7831        } elsif ($token->{type} eq 'comment') {        } elsif ({
7832          my $comment = $self->{document}->create_comment ($token->{data});                  a => 1,
7833          $self->{document}->append_child ($comment);                  b => 1, big => 1, em => 1, font => 1, i => 1,
7834                    nobr => 1, s => 1, small => 1, strike => 1,
7835                    strong => 1, tt => 1, u => 1,
7836                   }->{$token->{tag_name}}) {
7837            !!!cp ('t427');
7838            $formatting_end_tag->($token);
7839            next B;
7840          } elsif ($token->{tag_name} eq 'br') {
7841            !!!cp ('t428');
7842            !!!parse-error (type => 'unmatched end tag',
7843                            text => 'br', token => $token);
7844    
7845            ## As if <br>
7846            $reconstruct_active_formatting_elements->($insert_to_current);
7847            
7848            my $el;
7849            !!!create-element ($el, $HTML_NS, 'br',, $token);
7850            $insert->($el);
7851            
7852            ## Ignore the token.
7853          !!!next-token;          !!!next-token;
7854          redo B;          next B;
7855        } elsif ($token->{type} eq 'character') {        } elsif ({
7856          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                  caption => 1, col => 1, colgroup => 1, frame => 1,
7857            my $data = $1;                  frameset => 1, head => 1, option => 1, optgroup => 1,
7858            ## As if in the main phase.                  tbody => 1, td => 1, tfoot => 1, th => 1,
7859            ## NOTE: The insertion mode in the main phase                  thead => 1, tr => 1,
7860            ## just before the phase has been changed to the trailing                  area => 1, basefont => 1, bgsound => 1,
7861            ## end phase is either "after body" or "after frameset".                  embed => 1, hr => 1, iframe => 1, image => 1,
7862            $reconstruct_active_formatting_elements->($insert_to_current)                  img => 1, input => 1, isindex => 1, noembed => 1,
7863              if $phase eq 'main';                  noframes => 1, param => 1, select => 1, spacer => 1,
7864                    table => 1, textarea => 1, wbr => 1,
7865                    noscript => 0, ## TODO: if scripting is enabled
7866                   }->{$token->{tag_name}}) {
7867            !!!cp ('t429');
7868            !!!parse-error (type => 'unmatched end tag',
7869                            text => $token->{tag_name}, token => $token);
7870            ## Ignore the token
7871            !!!next-token;
7872            next B;
7873          } else {
7874            if ($token->{tag_name} eq 'sarcasm') {
7875              sleep 0.001; # take a deep breath
7876            }
7877    
7878            ## Step 1
7879            my $node_i = -1;
7880            my $node = $self->{open_elements}->[$node_i];
7881    
7882            ## Step 2
7883            S2: {
7884              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7885                ## Step 1
7886                ## generate implied end tags
7887                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7888                  !!!cp ('t430');
7889                  ## NOTE: |<ruby><rt></ruby>|.
7890                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7891                  ## which seems wrong.
7892                  pop @{$self->{open_elements}};
7893                  $node_i++;
7894                }
7895            
7896                ## Step 2
7897                if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7898                        ne $token->{tag_name}) {
7899                  !!!cp ('t431');
7900                  ## NOTE: <x><y></x>
7901                  !!!parse-error (type => 'not closed',
7902                                  text => $self->{open_elements}->[-1]->[0]
7903                                      ->manakai_local_name,
7904                                  token => $token);
7905                } else {
7906                  !!!cp ('t432');
7907                }
7908                
7909                ## Step 3
7910                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7911    
7912                !!!next-token;
7913                last S2;
7914              } else {
7915                ## Step 3
7916                if (not ($node->[1] & FORMATTING_EL) and
7917                    #not $phrasing_category->{$node->[1]} and
7918                    ($node->[1] & SPECIAL_EL or
7919                     $node->[1] & SCOPING_EL)) {
7920                  !!!cp ('t433');
7921                  !!!parse-error (type => 'unmatched end tag',
7922                                  text => $token->{tag_name}, token => $token);
7923                  ## Ignore the token
7924                  !!!next-token;
7925                  last S2;
7926    
7927                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7928                  ## 9.27, "a" is a child of <dd> (conforming).  In
7929                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7930                  ## "a" is a child of both <body> and <dd>.
7931                }
7932                
7933                !!!cp ('t434');
7934              }
7935                        
7936            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 4
7937              $node_i--;
7938              $node = $self->{open_elements}->[$node_i];
7939                        
7940            unless (length $token->{data}) {            ## Step 5;
7941              !!!next-token;            redo S2;
7942              redo B;          } # S2
7943            next B;
7944          }
7945        }
7946        next B;
7947      } continue { # B
7948        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7949          ## NOTE: The code below is executed in cases where it does not have
7950          ## to be, but it it is harmless even in those cases.
7951          ## has an element in scope
7952          INSCOPE: {
7953            for (reverse 0..$#{$self->{open_elements}}) {
7954              my $node = $self->{open_elements}->[$_];
7955              if ($node->[1] & FOREIGN_EL) {
7956                last INSCOPE;
7957              } elsif ($node->[1] & SCOPING_EL) {
7958                last;
7959            }            }
7960          }          }
7961            
7962          !!!parse-error (type => 'after html:#character');          ## NOTE: No foreign element in scope.
7963          $phase = 'main';          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7964          ## reprocess        } # INSCOPE
         redo B;  
       } elsif ($token->{type} eq 'start tag' or  
                $token->{type} eq 'end tag') {  
         !!!parse-error (type => 'after html:'.$token->{tag_name});  
         $phase = 'main';  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Stop parsing  
         last B;  
       } else {  
         die "$0: $token->{type}: Unknown token";  
       }  
7965      }      }
7966    } # B    } # B
7967    
# Line 4818  sub _tree_construction_main ($) { Line 7970  sub _tree_construction_main ($) {
7970    ## TODO: script stuffs    ## TODO: script stuffs
7971  } # _tree_construct_main  } # _tree_construct_main
7972    
7973  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7974    my $class = shift;    my $class = shift;
7975    my $node = shift;    my $node = shift;
7976    my $s = \$_[0];    #my $s = \$_[0];
7977    my $onerror = $_[1];    my $onerror = $_[1];
7978      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7979    
7980      ## ISSUE: Should {confident} be true?
7981    
7982    my $nt = $node->node_type;    my $nt = $node->node_type;
7983    if ($nt == 9) {    if ($nt == 9) {
# Line 4839  sub set_inner_html ($$$) { Line 7994  sub set_inner_html ($$$) {
7994      }      }
7995    
7996      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7997      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7998    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7999      ## TODO: If non-html element      ## TODO: If non-html element
8000    
8001      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8002    
8003    ## TODO: Support for $get_wrapper
8004    
8005      ## Step 1 # MUST      ## Step 1 # MUST
8006      my $doc = $node->owner_document->implementation->create_document;      my $this_doc = $node->owner_document;
8007      ## TODO: Mark as HTML document      my $doc = $this_doc->implementation->create_document;
8008        $doc->manakai_is_html (1);
8009      my $p = $class->new;      my $p = $class->new;
8010      $p->{document} = $doc;      $p->{document} = $doc;
8011    
8012      ## Step 9 # MUST      ## Step 8 # MUST
8013      my $i = 0;      my $i = 0;
8014      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8015      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8016      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8017        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8018        $input = $get_wrapper->($input);
8019        $p->{set_nc} = sub {
8020        my $self = shift;        my $self = shift;
8021        $self->{next_input_character} = -1 and return if $i >= length $$s;  
8022        $self->{next_input_character} = ord substr $$s, $i++, 1;        my $char = '';
8023        $column++;        if (defined $self->{next_nc}) {
8024            $char = $self->{next_nc};
8025        if ($self->{next_input_character} == 0x000A) { # LF          delete $self->{next_nc};
8026          $line++;          $self->{nc} = ord $char;
8027          $column = 0;        } else {
8028        } elsif ($self->{next_input_character} == 0x000D) { # CR          $self->{char_buffer} = '';
8029          if ($i >= length $$s) {          $self->{char_buffer_pos} = 0;
8030            #          
8031            my $count = $input->manakai_read_until
8032                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8033                 $self->{char_buffer_pos});
8034            if ($count) {
8035              $self->{line_prev} = $self->{line};
8036              $self->{column_prev} = $self->{column};
8037              $self->{column}++;
8038              $self->{nc}
8039                  = ord substr ($self->{char_buffer},
8040                                $self->{char_buffer_pos}++, 1);
8041              return;
8042            }
8043            
8044            if ($input->read ($char, 1)) {
8045              $self->{nc} = ord $char;
8046          } else {          } else {
8047            my $next_char = ord substr $$s, $i++, 1;            $self->{nc} = -1;
8048            if ($next_char == 0x000A) { # LF            return;
             #  
           } else {  
             push @{$self->{char}}, $next_char;  
           }  
8049          }          }
8050          $self->{next_input_character} = 0x000A; # LF # MUST        }
8051          $line++;  
8052          $column = 0;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8053        } elsif ($self->{next_input_character} > 0x10FFFF) {        $p->{column}++;
8054          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
8055        } elsif ($self->{next_input_character} == 0x0000) { # NULL        if ($self->{nc} == 0x000A) { # LF
8056          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $p->{line}++;
8057            $p->{column} = 0;
8058            !!!cp ('i1');
8059          } elsif ($self->{nc} == 0x000D) { # CR
8060    ## TODO: support for abort/streaming
8061            my $next = '';
8062            if ($input->read ($next, 1) and $next ne "\x0A") {
8063              $self->{next_nc} = $next;
8064            }
8065            $self->{nc} = 0x000A; # LF # MUST
8066            $p->{line}++;
8067            $p->{column} = 0;
8068            !!!cp ('i2');
8069          } elsif ($self->{nc} == 0x0000) { # NULL
8070            !!!cp ('i4');
8071            !!!parse-error (type => 'NULL');
8072            $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8073        }        }
8074      };      };
8075        
8076        $p->{read_until} = sub {
8077          #my ($scalar, $specials_range, $offset) = @_;
8078          return 0 if defined $p->{next_nc};
8079    
8080          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8081          my $offset = $_[2] || 0;
8082          
8083          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8084            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8085            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8086              substr ($_[0], $offset)
8087                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8088              my $count = $+[0] - $-[0];
8089              if ($count) {
8090                $p->{column} += $count;
8091                $p->{char_buffer_pos} += $count;
8092                $p->{line_prev} = $p->{line};
8093                $p->{column_prev} = $p->{column} - 1;
8094                $p->{nc} = -1;
8095              }
8096              return $count;
8097            } else {
8098              return 0;
8099            }
8100          } else {
8101            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8102            if ($count) {
8103              $p->{column} += $count;
8104              $p->{column_prev} += $count;
8105              $p->{nc} = -1;
8106            }
8107            return $count;
8108          }
8109        }; # $p->{read_until}
8110    
8111      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8112        my (%opt) = @_;        my (%opt) = @_;
8113        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8114          my $column = $opt{column};
8115          if (defined $opt{token} and defined $opt{token}->{line}) {
8116            $line = $opt{token}->{line};
8117            $column = $opt{token}->{column};
8118          }
8119          warn "Parse error ($opt{type}) at line $line column $column\n";
8120      };      };
8121      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8122        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8123      };      };
8124            
8125        my $char_onerror = sub {
8126          my (undef, $type, %opt) = @_;
8127          $ponerror->(layer => 'encode',
8128                      line => $p->{line}, column => $p->{column} + 1,
8129                      %opt, type => $type);
8130        }; # $char_onerror
8131        $input->onerror ($char_onerror);
8132    
8133      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8134      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8135    
8136      ## Step 2      ## Step 2
8137      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8138      $p->{content_model_flag} = {      $p->{content_model} = {
8139        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
8140        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
8141        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
8142        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
8143        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
8144        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
8145        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
8146        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
8147        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
8148        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
8149      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
8150         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
8151            unless defined $p->{content_model};
8152            ## ISSUE: What is "the name of the element"? local name?
8153    
8154      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8155          ## TODO: Foreign element OK?
8156    
8157      ## Step 4      ## Step 3
8158      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8159        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8160    
8161      ## Step 5 # MUST      ## Step 4 # MUST
8162      $doc->append_child ($root);      $doc->append_child ($root);
8163    
8164      ## Step 6 # MUST      ## Step 5 # MUST
8165      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8166    
8167      undef $p->{head_element};      undef $p->{head_element};
8168    
8169      ## Step 7 # MUST      ## Step 6 # MUST
8170      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8171    
8172      ## Step 8 # MUST      ## Step 7 # MUST
8173      my $anode = $node;      my $anode = $node;
8174      AN: while (defined $anode) {      AN: while (defined $anode) {
8175        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8176          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8177          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8178            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8179                !!!cp ('i5');
8180              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8181              last AN;              last AN;
8182            }            }
# Line 4944  sub set_inner_html ($$$) { Line 8185  sub set_inner_html ($$$) {
8185        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8186      } # AN      } # AN
8187            
8188      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8189      {      {
8190        my $self = $p;        my $self = $p;
8191        !!!next-token;        !!!next-token;
8192      }      }
8193      $p->_tree_construction_main;      $p->_tree_construction_main;
8194    
8195      ## Step 11 # MUST      ## Step 10 # MUST
8196      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8197      for (@cn) {      for (@cn) {
8198        $node->remove_child ($_);        $node->remove_child ($_);
8199      }      }
8200      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8201    
8202      ## Step 12 # MUST      ## Step 11 # MUST
8203      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8204      for (@cn) {      for (@cn) {
8205          $this_doc->adopt_node ($_);
8206        $node->append_child ($_);        $node->append_child ($_);
8207      }      }
8208      ## ISSUE: adopt_node? mutation events?      ## ISSUE: mutation events?
8209    
8210      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8211    
8212        delete $p->{parse_error}; # delete loop
8213    } else {    } else {
8214      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8215    }    }
# Line 4974  sub set_inner_html ($$$) { Line 8217  sub set_inner_html ($$$) {
8217    
8218  } # tree construction stage  } # tree construction stage
8219    
8220  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8221    my (undef, $node, $on_error) = @_;  push our @ISA, 'Error';
   
   ## Step 1  
   my $s = '';  
   
   my $in_cdata;  
   my $parent = $node;  
   while (defined $parent) {  
     if ($parent->node_type == 1 and  
         $parent->namespace_uri eq 'http://www.w3.org/1999/xhtml' and  
         {  
           style => 1, script => 1, xmp => 1, iframe => 1,  
           noembed => 1, noframes => 1, noscript => 1,  
         }->{$parent->local_name}) { ## TODO: case thingy  
       $in_cdata = 1;  
     }  
     $parent = $parent->parent_node;  
   }  
   
   ## Step 2  
   my @node = @{$node->child_nodes};  
   C: while (@node) {  
     my $child = shift @node;  
     unless (ref $child) {  
       if ($child eq 'cdata-out') {  
         $in_cdata = 0;  
       } else {  
         $s .= $child; # end tag  
       }  
       next C;  
     }  
       
     my $nt = $child->node_type;  
     if ($nt == 1) { # Element  
       my $tag_name = lc $child->tag_name; ## ISSUE: Definition of "lowercase"  
       $s .= '<' . $tag_name;  
   
       ## ISSUE: Non-html elements  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = lc $attr->name; ## ISSUE: Definition of "lowercase"  
         $s .= ' ' . $attr_name . '="';  
         my $attr_value = $attr->value;  
         ## escape  
         $attr_value =~ s/&/&amp;/g;  
         $attr_value =~ s/</&lt;/g;  
         $attr_value =~ s/>/&gt;/g;  
         $attr_value =~ s/"/&quot;/g;  
         $s .= $attr_value . '"';  
       }  
       $s .= '>';  
         
       next C if {  
         area => 1, base => 1, basefont => 1, bgsound => 1,  
         br => 1, col => 1, embed => 1, frame => 1, hr => 1,  
         img => 1, input => 1, link => 1, meta => 1, param => 1,  
         spacer => 1, wbr => 1,  
       }->{$tag_name};  
   
       if (not $in_cdata and {  
         style => 1, script => 1, xmp => 1, iframe => 1,  
         noembed => 1, noframes => 1, noscript => 1,  
       }->{$tag_name}) {  
         unshift @node, 'cdata-out';  
         $in_cdata = 1;  
       }  
   
       unshift @node, @{$child->child_nodes}, '</' . $tag_name . '>';  
     } elsif ($nt == 3 or $nt == 4) {  
       if ($in_cdata) {  
         $s .= $child->data;  
       } else {  
         my $value = $child->data;  
         $value =~ s/&/&amp;/g;  
         $value =~ s/</&lt;/g;  
         $value =~ s/>/&gt;/g;  
         $value =~ s/"/&quot;/g;  
         $s .= $value;  
       }  
     } elsif ($nt == 8) {  
       $s .= '<!--' . $child->data . '-->';  
     } elsif ($nt == 10) {  
       $s .= '<!DOCTYPE ' . $child->name . '>';  
     } elsif ($nt == 5) { # entrefs  
       push @node, @{$child->child_nodes};  
     } else {  
       $on_error->($child) if defined $on_error;  
     }  
     ## ISSUE: This code does not support PIs.  
   } # C  
     
   ## Step 3  
   return \$s;  
 } # get_inner_html  
8222    
8223  1;  1;
8224  # $Date$  # $Date$

Legend:
Removed from v.1.7  
changed lines
  Added in v.1.198

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24