/[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.2 by wakaba, Tue May 1 10:47:37 2007 UTC revision 1.194 by wakaba, Sat Oct 4 05:53:45 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}",  
257  };  };
258    
259  my $special_category = {  my $svg_attr_name = {
260    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,    attributename => 'attributeName',
261    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,    attributetype => 'attributeType',
262    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,    basefrequency => 'baseFrequency',
263    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    baseprofile => 'baseProfile',
264    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    calcmode => 'calcMode',
265    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    clippathunits => 'clipPathUnits',
266    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    contentscripttype => 'contentScriptType',
267    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,    contentstyletype => 'contentStyleType',
268    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,    diffuseconstant => 'diffuseConstant',
269    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,    edgemode => 'edgeMode',
270  };    externalresourcesrequired => 'externalResourcesRequired',
271  my $scoping_category = {    filterres => 'filterRes',
272    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    filterunits => 'filterUnits',
273    table => 1, td => 1, th => 1,    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    $self->{document} = $_[1];    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          ## ISSUE: Unsupported encoding is not ignored according to the spec.
492          require Whatpm::Charset::DecodeHandle;
493          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
494              ($byte_stream);
495          ($char_stream, $e_status) = $charset->get_decode_handle
496              ($buffer, allow_error_reporting => 1,
497               allow_fallback => 1, byte_buffer => \$byte_buffer);
498          if ($char_stream) {
499            $buffer->{buffer} = $byte_buffer;
500            !!!parse-error (type => 'sniffing:chardet',
501                            text => $charset_name,
502                            level => $self->{level}->{info},
503                            layer => 'encode',
504                            line => 1, column => 1);
505            $self->{confident} = 0;
506            last SNIFFING;
507          }
508        }
509    
510    my $i;      ## Step 7: default
511    my $i = 0;      ## TODO: Make this configurable.
512    $self->{set_next_input_character} = sub {      $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
513            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
514            ## detectable in the step 6.
515        require Whatpm::Charset::DecodeHandle;
516        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
517            ($byte_stream);
518        ($char_stream, $e_status)
519            = $charset->get_decode_handle ($buffer,
520                                           allow_error_reporting => 1,
521                                           allow_fallback => 1,
522                                           byte_buffer => \$byte_buffer);
523        $buffer->{buffer} = $byte_buffer;
524        !!!parse-error (type => 'sniffing:default',
525                        text => 'windows-1252',
526                        level => $self->{level}->{info},
527                        line => 1, column => 1,
528                        layer => 'encode');
529        $self->{confident} = 0;
530      } # SNIFFING
531    
532      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
533        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
534        !!!parse-error (type => 'chardecode:fallback',
535                        #text => $self->{input_encoding},
536                        level => $self->{level}->{uncertain},
537                        line => 1, column => 1,
538                        layer => 'encode');
539      } elsif (not ($e_status &
540                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
541        $self->{input_encoding} = $charset->get_iana_name;
542        !!!parse-error (type => 'chardecode:no error',
543                        text => $self->{input_encoding},
544                        level => $self->{level}->{uncertain},
545                        line => 1, column => 1,
546                        layer => 'encode');
547      } else {
548        $self->{input_encoding} = $charset->get_iana_name;
549      }
550    
551      $self->{change_encoding} = sub {
552      my $self = shift;      my $self = shift;
553      $self->{next_input_character} = -1 and return if $i >= length $$s;      $charset_name = shift;
554      $self->{next_input_character} = ord substr $$s, $i++, 1;      my $token = shift;
555    
556        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
557        ($char_stream, $e_status) = $charset->get_decode_handle
558            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
559             byte_buffer => \ $buffer->{buffer});
560            
561      if ($self->{next_input_character} == 0x000D) { # CR      if ($char_stream) { # if supported
562        if ($i >= length $$s) {        ## "Change the encoding" algorithm:
563          #  
564          ## Step 1    
565          if ($charset->{category} &
566              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
567            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
568            ($char_stream, $e_status) = $charset->get_decode_handle
569                ($byte_stream,
570                 byte_buffer => \ $buffer->{buffer});
571          }
572          $charset_name = $charset->get_iana_name;
573          
574          ## Step 2
575          if (defined $self->{input_encoding} and
576              $self->{input_encoding} eq $charset_name) {
577            !!!parse-error (type => 'charset label:matching',
578                            text => $charset_name,
579                            level => $self->{level}->{info});
580            $self->{confident} = 1;
581            return;
582          }
583    
584          !!!parse-error (type => 'charset label detected',
585                          text => $self->{input_encoding},
586                          value => $charset_name,
587                          level => $self->{level}->{warn},
588                          token => $token);
589          
590          ## Step 3
591          # if (can) {
592            ## change the encoding on the fly.
593            #$self->{confident} = 1;
594            #return;
595          # }
596          
597          ## Step 4
598          throw Whatpm::HTML::RestartParser ();
599        }
600      }; # $self->{change_encoding}
601    
602      my $char_onerror = sub {
603        my (undef, $type, %opt) = @_;
604        !!!parse-error (layer => 'encode',
605                        line => $self->{line}, column => $self->{column} + 1,
606                        %opt, type => $type);
607        if ($opt{octets}) {
608          ${$opt{octets}} = "\x{FFFD}"; # relacement character
609        }
610      };
611    
612      my $wrapped_char_stream = $get_wrapper->($char_stream);
613      $wrapped_char_stream->onerror ($char_onerror);
614    
615      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
616      my $return;
617      try {
618        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
619      } catch Whatpm::HTML::RestartParser with {
620        ## NOTE: Invoked after {change_encoding}.
621    
622        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
623          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
624          !!!parse-error (type => 'chardecode:fallback',
625                          level => $self->{level}->{uncertain},
626                          #text => $self->{input_encoding},
627                          line => 1, column => 1,
628                          layer => 'encode');
629        } elsif (not ($e_status &
630                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
631          $self->{input_encoding} = $charset->get_iana_name;
632          !!!parse-error (type => 'chardecode:no error',
633                          text => $self->{input_encoding},
634                          level => $self->{level}->{uncertain},
635                          line => 1, column => 1,
636                          layer => 'encode');
637        } else {
638          $self->{input_encoding} = $charset->get_iana_name;
639        }
640        $self->{confident} = 1;
641    
642        $wrapped_char_stream = $get_wrapper->($char_stream);
643        $wrapped_char_stream->onerror ($char_onerror);
644    
645        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
646      };
647      return $return;
648    } # parse_byte_stream
649    
650    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
651    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
652    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
653    ## because the core part of our HTML parser expects a string of character,
654    ## not a string of bytes or code units or anything which might contain a BOM.
655    ## Therefore, any parser interface that accepts a string of bytes,
656    ## such as |parse_byte_string| in this module, must ensure that it does
657    ## strip the BOM and never strip any ZWNBSP.
658    
659    sub parse_char_string ($$$;$$) {
660      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
661      my $self = shift;
662      my $s = ref $_[0] ? $_[0] : \($_[0]);
663      require Whatpm::Charset::DecodeHandle;
664      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
665      return $self->parse_char_stream ($input, @_[1..$#_]);
666    } # parse_char_string
667    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
668    
669    sub parse_char_stream ($$$;$$) {
670      my $self = ref $_[0] ? shift : shift->new;
671      my $input = $_[0];
672      $self->{document} = $_[1];
673      @{$self->{document}->child_nodes} = ();
674    
675      ## NOTE: |set_inner_html| copies most of this method's code
676    
677      $self->{confident} = 1 unless exists $self->{confident};
678      $self->{document}->input_encoding ($self->{input_encoding})
679          if defined $self->{input_encoding};
680    ## TODO: |{input_encoding}| is needless?
681    
682      $self->{line_prev} = $self->{line} = 1;
683      $self->{column_prev} = -1;
684      $self->{column} = 0;
685      $self->{set_nc} = sub {
686        my $self = shift;
687    
688        my $char = '';
689        if (defined $self->{next_nc}) {
690          $char = $self->{next_nc};
691          delete $self->{next_nc};
692          $self->{nc} = ord $char;
693        } else {
694          $self->{char_buffer} = '';
695          $self->{char_buffer_pos} = 0;
696    
697          my $count = $input->manakai_read_until
698             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
699          if ($count) {
700            $self->{line_prev} = $self->{line};
701            $self->{column_prev} = $self->{column};
702            $self->{column}++;
703            $self->{nc}
704                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
705            return;
706          }
707    
708          if ($input->read ($char, 1)) {
709            $self->{nc} = ord $char;
710        } else {        } else {
711          my $next_char = ord substr $$s, $i++, 1;          $self->{nc} = -1;
712          if ($next_char == 0x000A) { # LF          return;
           #  
         } else {  
           push @{$self->{char}}, $next_char;  
         }  
713        }        }
714        $self->{next_input_character} = 0x000A; # LF # MUST      }
715      } elsif ($self->{next_input_character} > 0x10FFFF) {  
716        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST      ($self->{line_prev}, $self->{column_prev})
717      } elsif ($self->{next_input_character} == 0x0000) { # NULL          = ($self->{line}, $self->{column});
718        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST      $self->{column}++;
719        
720        if ($self->{nc} == 0x000A) { # LF
721          !!!cp ('j1');
722          $self->{line}++;
723          $self->{column} = 0;
724        } elsif ($self->{nc} == 0x000D) { # CR
725          !!!cp ('j2');
726    ## TODO: support for abort/streaming
727          my $next = '';
728          if ($input->read ($next, 1) and $next ne "\x0A") {
729            $self->{next_nc} = $next;
730          }
731          $self->{nc} = 0x000A; # LF # MUST
732          $self->{line}++;
733          $self->{column} = 0;
734        } elsif ($self->{nc} == 0x0000) { # NULL
735          !!!cp ('j4');
736          !!!parse-error (type => 'NULL');
737          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
738      }      }
739    };    };
740    
741    $self->{parse_error} = $_[2] || sub {    $self->{read_until} = sub {
742      warn "Parse error at character $i\n"; ## TODO: Report (line, column) pair      #my ($scalar, $specials_range, $offset) = @_;
743        return 0 if defined $self->{next_nc};
744    
745        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
746        my $offset = $_[2] || 0;
747    
748        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
749          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
750          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
751            substr ($_[0], $offset)
752                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
753            my $count = $+[0] - $-[0];
754            if ($count) {
755              $self->{column} += $count;
756              $self->{char_buffer_pos} += $count;
757              $self->{line_prev} = $self->{line};
758              $self->{column_prev} = $self->{column} - 1;
759              $self->{nc} = -1;
760            }
761            return $count;
762          } else {
763            return 0;
764          }
765        } else {
766          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
767          if ($count) {
768            $self->{column} += $count;
769            $self->{line_prev} = $self->{line};
770            $self->{column_prev} = $self->{column} - 1;
771            $self->{nc} = -1;
772          }
773          return $count;
774        }
775      }; # $self->{read_until}
776    
777      my $onerror = $_[2] || sub {
778        my (%opt) = @_;
779        my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
780        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
781        warn "Parse error ($opt{type}) at line $line column $column\n";
782      };
783      $self->{parse_error} = sub {
784        $onerror->(line => $self->{line}, column => $self->{column}, @_);
785    };    };
786    
787      my $char_onerror = sub {
788        my (undef, $type, %opt) = @_;
789        !!!parse-error (layer => 'encode',
790                        line => $self->{line}, column => $self->{column} + 1,
791                        %opt, type => $type);
792      }; # $char_onerror
793    
794      if ($_[3]) {
795        $input = $_[3]->($input);
796        $input->onerror ($char_onerror);
797      } else {
798        $input->onerror ($char_onerror) unless defined $input->onerror;
799      }
800    
801    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
802    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
803    $self->_construct_tree;    $self->_construct_tree;
804    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
805    
806      delete $self->{parse_error}; # remove loop
807    
808    return $self->{document};    return $self->{document};
809  } # parse_string  } # parse_char_stream
810    
811  sub new ($) {  sub new ($) {
812    my $class = shift;    my $class = shift;
813    my $self = bless {}, $class;    my $self = bless {
814    $self->{set_next_input_character} = sub {      level => {must => 'm',
815      $self->{next_input_character} = -1;                should => 's',
816                  warn => 'w',
817                  info => 'i',
818                  uncertain => 'u'},
819      }, $class;
820      $self->{set_nc} = sub {
821        $self->{nc} = -1;
822    };    };
823    $self->{parse_error} = sub {    $self->{parse_error} = sub {
824      #      #
825    };    };
826      $self->{change_encoding} = sub {
827        # if ($_[0] is a supported encoding) {
828        #   run "change the encoding" algorithm;
829        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
830        # }
831      };
832      $self->{application_cache_selection} = sub {
833        #
834      };
835    return $self;    return $self;
836  } # new  } # new
837    
838    sub CM_ENTITY () { 0b001 } # & markup in data
839    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
840    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
841    
842    sub PLAINTEXT_CONTENT_MODEL () { 0 }
843    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
844    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
845    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
846    
847    sub DATA_STATE () { 0 }
848    #sub ENTITY_DATA_STATE () { 1 }
849    sub TAG_OPEN_STATE () { 2 }
850    sub CLOSE_TAG_OPEN_STATE () { 3 }
851    sub TAG_NAME_STATE () { 4 }
852    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
853    sub ATTRIBUTE_NAME_STATE () { 6 }
854    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
855    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
856    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
857    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
858    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
859    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
860    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
861    sub COMMENT_START_STATE () { 14 }
862    sub COMMENT_START_DASH_STATE () { 15 }
863    sub COMMENT_STATE () { 16 }
864    sub COMMENT_END_STATE () { 17 }
865    sub COMMENT_END_DASH_STATE () { 18 }
866    sub BOGUS_COMMENT_STATE () { 19 }
867    sub DOCTYPE_STATE () { 20 }
868    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
869    sub DOCTYPE_NAME_STATE () { 22 }
870    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
871    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
872    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
873    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
874    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
875    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
876    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
877    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
878    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
879    sub BOGUS_DOCTYPE_STATE () { 32 }
880    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
881    sub SELF_CLOSING_START_TAG_STATE () { 34 }
882    sub CDATA_SECTION_STATE () { 35 }
883    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
884    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
885    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
886    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
887    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
888    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
889    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
890    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
891    ## NOTE: "Entity data state", "entity in attribute value state", and
892    ## "consume a character reference" algorithm are jointly implemented
893    ## using the following six states:
894    sub ENTITY_STATE () { 44 }
895    sub ENTITY_HASH_STATE () { 45 }
896    sub NCR_NUM_STATE () { 46 }
897    sub HEXREF_X_STATE () { 47 }
898    sub HEXREF_HEX_STATE () { 48 }
899    sub ENTITY_NAME_STATE () { 49 }
900    sub PCDATA_STATE () { 50 } # "data state" in the spec
901    
902    sub DOCTYPE_TOKEN () { 1 }
903    sub COMMENT_TOKEN () { 2 }
904    sub START_TAG_TOKEN () { 3 }
905    sub END_TAG_TOKEN () { 4 }
906    sub END_OF_FILE_TOKEN () { 5 }
907    sub CHARACTER_TOKEN () { 6 }
908    
909    sub AFTER_HTML_IMS () { 0b100 }
910    sub HEAD_IMS ()       { 0b1000 }
911    sub BODY_IMS ()       { 0b10000 }
912    sub BODY_TABLE_IMS () { 0b100000 }
913    sub TABLE_IMS ()      { 0b1000000 }
914    sub ROW_IMS ()        { 0b10000000 }
915    sub BODY_AFTER_IMS () { 0b100000000 }
916    sub FRAME_IMS ()      { 0b1000000000 }
917    sub SELECT_IMS ()     { 0b10000000000 }
918    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
919        ## NOTE: "in foreign content" insertion mode is special; it is combined
920        ## with the secondary insertion mode.  In this parser, they are stored
921        ## together in the bit-or'ed form.
922    
923    ## NOTE: "initial" and "before html" insertion modes have no constants.
924    
925    ## NOTE: "after after body" insertion mode.
926    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
927    
928    ## NOTE: "after after frameset" insertion mode.
929    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
930    
931    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
932    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
933    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
934    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
935    sub IN_BODY_IM () { BODY_IMS }
936    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
937    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
938    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
939    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
940    sub IN_TABLE_IM () { TABLE_IMS }
941    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
942    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
943    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
944    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
945    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
946    sub IN_COLUMN_GROUP_IM () { 0b10 }
947    
948  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
949    
950  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
951    my $self = shift;    my $self = shift;
952    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
953    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{s_kwd}; # state keyword - initialized when used
954    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    #$self->{entity__value}; # initialized when used
955    undef $self->{current_attribute};    #$self->{entity__match}; # initialized when used
956    undef $self->{last_emitted_start_tag_name};    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
957    undef $self->{last_attribute_value_state};    undef $self->{ct}; # current token
958    $self->{char} = [];    undef $self->{ca}; # current attribute
959    # $self->{next_input_character}    undef $self->{last_stag_name}; # last emitted start tag name
960      #$self->{prev_state}; # initialized when used
961      delete $self->{self_closing};
962      $self->{char_buffer} = '';
963      $self->{char_buffer_pos} = 0;
964      $self->{nc} = -1; # next input character
965      #$self->{next_nc}
966    !!!next-input-character;    !!!next-input-character;
967    $self->{token} = [];    $self->{token} = [];
968      # $self->{escape}
969  } # _initialize_tokenizer  } # _initialize_tokenizer
970    
971  ## A token has:  ## A token has:
972  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
973  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
974  ##   ->{name} (DOCTYPE, start tag (tagname), end tag (tagname))  ##   ->{name} (DOCTYPE_TOKEN)
975      ## ISSUE: the spec need s/tagname/tag name/  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
976  ##   ->{error} == 1 or 0 (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
977  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{sysid} (DOCTYPE_TOKEN)
978  ##   ->{data} (comment, character)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
979    ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
980  ## Macros  ##        ->{name}
981  ##   Macros MUST be preceded by three EXCLAMATION MARKs.  ##        ->{value}
982  ##   emit ($token)  ##        ->{has_reference} == 1 or 0
983  ##     Emits the specified token.  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
984    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
985    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
986    ##     while the token is pushed back to the stack.
987    
988  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
989    
# Line 395  sub _initialize_tokenizer ($) { Line 993  sub _initialize_tokenizer ($) {
993  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
994  ## and removed from the list.  ## and removed from the list.
995    
996    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
997    ## (This requirement was dropped from HTML5 spec, unfortunately.)
998    
999    my $is_space = {
1000      0x0009 => 1, # CHARACTER TABULATION (HT)
1001      0x000A => 1, # LINE FEED (LF)
1002      #0x000B => 0, # LINE TABULATION (VT)
1003      0x000C => 1, # FORM FEED (FF)
1004      #0x000D => 1, # CARRIAGE RETURN (CR)
1005      0x0020 => 1, # SPACE (SP)
1006    };
1007    
1008  sub _get_next_token ($) {  sub _get_next_token ($) {
1009    my $self = shift;    my $self = shift;
1010    
1011      if ($self->{self_closing}) {
1012        !!!parse-error (type => 'nestc', token => $self->{ct});
1013        ## NOTE: The |self_closing| flag is only set by start tag token.
1014        ## In addition, when a start tag token is emitted, it is always set to
1015        ## |ct|.
1016        delete $self->{self_closing};
1017      }
1018    
1019    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1020        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1021      return shift @{$self->{token}};      return shift @{$self->{token}};
1022    }    }
1023    
1024    A: {    A: {
1025      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1026        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1027          if ($self->{content_model_flag} eq 'PCDATA' or  
1028              $self->{content_model_flag} eq 'RCDATA') {        if ($self->{nc} == 0x0026) { # &
1029            $self->{state} = 'entity data';          !!!cp (0.1);
1030            ## NOTE: In the spec, the tokenizer is switched to the
1031            ## "entity data state".  In this implementation, the tokenizer
1032            ## is switched to the |ENTITY_STATE|, which is an implementation
1033            ## of the "consume a character reference" algorithm.
1034            $self->{entity_add} = -1;
1035            $self->{prev_state} = DATA_STATE;
1036            $self->{state} = ENTITY_STATE;
1037            !!!next-input-character;
1038            redo A;
1039          } elsif ($self->{nc} == 0x003C) { # <
1040            !!!cp (0.2);
1041            $self->{state} = TAG_OPEN_STATE;
1042            !!!next-input-character;
1043            redo A;
1044          } elsif ($self->{nc} == -1) {
1045            !!!cp (0.3);
1046            !!!emit ({type => END_OF_FILE_TOKEN,
1047                      line => $self->{line}, column => $self->{column}});
1048            last A; ## TODO: ok?
1049          } else {
1050            !!!cp (0.4);
1051            #
1052          }
1053    
1054          # Anything else
1055          my $token = {type => CHARACTER_TOKEN,
1056                       data => chr $self->{nc},
1057                       line => $self->{line}, column => $self->{column},
1058                      };
1059          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1060    
1061          ## Stay in the state.
1062          !!!next-input-character;
1063          !!!emit ($token);
1064          redo A;
1065        } elsif ($self->{state} == DATA_STATE) {
1066          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1067          if ($self->{nc} == 0x0026) { # &
1068            $self->{s_kwd} = '';
1069            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1070                not $self->{escape}) {
1071              !!!cp (1);
1072              ## NOTE: In the spec, the tokenizer is switched to the
1073              ## "entity data state".  In this implementation, the tokenizer
1074              ## is switched to the |ENTITY_STATE|, which is an implementation
1075              ## of the "consume a character reference" algorithm.
1076              $self->{entity_add} = -1;
1077              $self->{prev_state} = DATA_STATE;
1078              $self->{state} = ENTITY_STATE;
1079            !!!next-input-character;            !!!next-input-character;
1080            redo A;            redo A;
1081          } else {          } else {
1082              !!!cp (2);
1083            #            #
1084          }          }
1085        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x002D) { # -
1086          if ($self->{content_model_flag} ne 'PLAINTEXT') {          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1087            $self->{state} = 'tag open';            $self->{s_kwd} .= '-';
1088              
1089              if ($self->{s_kwd} eq '<!--') {
1090                !!!cp (3);
1091                $self->{escape} = 1; # unless $self->{escape};
1092                $self->{s_kwd} = '--';
1093                #
1094              } elsif ($self->{s_kwd} eq '---') {
1095                !!!cp (4);
1096                $self->{s_kwd} = '--';
1097                #
1098              } else {
1099                !!!cp (5);
1100                #
1101              }
1102            }
1103            
1104            #
1105          } elsif ($self->{nc} == 0x0021) { # !
1106            if (length $self->{s_kwd}) {
1107              !!!cp (5.1);
1108              $self->{s_kwd} .= '!';
1109              #
1110            } else {
1111              !!!cp (5.2);
1112              #$self->{s_kwd} = '';
1113              #
1114            }
1115            #
1116          } elsif ($self->{nc} == 0x003C) { # <
1117            if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1118                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1119                 not $self->{escape})) {
1120              !!!cp (6);
1121              $self->{state} = TAG_OPEN_STATE;
1122            !!!next-input-character;            !!!next-input-character;
1123            redo A;            redo A;
1124          } else {          } else {
1125              !!!cp (7);
1126              $self->{s_kwd} = '';
1127            #            #
1128          }          }
1129        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
1130          !!!emit ({type => 'end-of-file'});          if ($self->{escape} and
1131                ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1132              if ($self->{s_kwd} eq '--') {
1133                !!!cp (8);
1134                delete $self->{escape};
1135              } else {
1136                !!!cp (9);
1137              }
1138            } else {
1139              !!!cp (10);
1140            }
1141            
1142            $self->{s_kwd} = '';
1143            #
1144          } elsif ($self->{nc} == -1) {
1145            !!!cp (11);
1146            $self->{s_kwd} = '';
1147            !!!emit ({type => END_OF_FILE_TOKEN,
1148                      line => $self->{line}, column => $self->{column}});
1149          last A; ## TODO: ok?          last A; ## TODO: ok?
1150          } else {
1151            !!!cp (12);
1152            $self->{s_kwd} = '';
1153            #
1154        }        }
       # 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;  
1155    
1156        $self->{state} = 'data';        # Anything else
1157        # next-input-character is already done        my $token = {type => CHARACTER_TOKEN,
1158                       data => chr $self->{nc},
1159                       line => $self->{line}, column => $self->{column},
1160                      };
1161          if ($self->{read_until}->($token->{data}, q[-!<>&],
1162                                    length $token->{data})) {
1163            $self->{s_kwd} = '';
1164          }
1165    
1166        unless (defined $token) {        ## Stay in the data state.
1167          !!!emit ({type => 'character', data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1168            !!!cp (13);
1169            $self->{state} = PCDATA_STATE;
1170        } else {        } else {
1171          !!!emit ($token);          !!!cp (14);
1172            ## Stay in the state.
1173        }        }
1174          !!!next-input-character;
1175          !!!emit ($token);
1176        redo A;        redo A;
1177      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1178        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1179            $self->{content_model_flag} eq 'CDATA') {          if ($self->{nc} == 0x002F) { # /
1180          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1181            !!!next-input-character;            !!!next-input-character;
1182            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1183            redo A;            redo A;
1184            } elsif ($self->{nc} == 0x0021) { # !
1185              !!!cp (15.1);
1186              $self->{s_kwd} = '<' unless $self->{escape};
1187              #
1188          } else {          } else {
1189            ## reconsume            !!!cp (16);
1190            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1191          }          }
1192        } elsif ($self->{content_model_flag} eq 'PCDATA') {  
1193          if ($self->{next_input_character} == 0x0021) { # !          ## reconsume
1194            $self->{state} = 'markup declaration open';          $self->{state} = DATA_STATE;
1195            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1196                      line => $self->{line_prev},
1197                      column => $self->{column_prev},
1198                     });
1199            redo A;
1200          } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1201            if ($self->{nc} == 0x0021) { # !
1202              !!!cp (17);
1203              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1204            !!!next-input-character;            !!!next-input-character;
1205            redo A;            redo A;
1206          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1207            $self->{state} = 'close tag open';            !!!cp (18);
1208              $self->{state} = CLOSE_TAG_OPEN_STATE;
1209            !!!next-input-character;            !!!next-input-character;
1210            redo A;            redo A;
1211          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1212                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1213            $self->{current_token}            !!!cp (19);
1214              = {type => 'start tag',            $self->{ct}
1215                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1216            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1217                   line => $self->{line_prev},
1218                   column => $self->{column_prev}};
1219              $self->{state} = TAG_NAME_STATE;
1220            !!!next-input-character;            !!!next-input-character;
1221            redo A;            redo A;
1222          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1223                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1224            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1225                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1226            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1227                                        line => $self->{line_prev},
1228                                        column => $self->{column_prev}};
1229              $self->{state} = TAG_NAME_STATE;
1230            !!!next-input-character;            !!!next-input-character;
1231            redo A;            redo A;
1232          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1233            !!!parse-error;            !!!cp (21);
1234            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1235                              line => $self->{line_prev},
1236                              column => $self->{column_prev});
1237              $self->{state} = DATA_STATE;
1238            !!!next-input-character;            !!!next-input-character;
1239    
1240            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1241                        line => $self->{line_prev},
1242                        column => $self->{column_prev},
1243                       });
1244    
1245            redo A;            redo A;
1246          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1247            !!!parse-error;            !!!cp (22);
1248            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1249            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1250                              column => $self->{column_prev});
1251              $self->{state} = BOGUS_COMMENT_STATE;
1252              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1253                                        line => $self->{line_prev},
1254                                        column => $self->{column_prev},
1255                                       };
1256              ## $self->{nc} is intentionally left as is
1257            redo A;            redo A;
1258          } else {          } else {
1259            !!!parse-error;            !!!cp (23);
1260            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1261                              line => $self->{line_prev},
1262                              column => $self->{column_prev});
1263              $self->{state} = DATA_STATE;
1264            ## reconsume            ## reconsume
1265    
1266            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1267                        line => $self->{line_prev},
1268                        column => $self->{column_prev},
1269                       });
1270    
1271            redo A;            redo A;
1272          }          }
1273        } else {        } else {
1274          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1275        }        }
1276      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1277        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1278            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1279          my @next_char;  
1280          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"</"
1281            push @next_char, $self->{next_input_character};        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1282            my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);          if (defined $self->{last_stag_name}) {
1283            my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1284            if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {            $self->{s_kwd} = '';
1285              !!!next-input-character;            ## Reconsume.
             next TAGNAME;  
           } else {  
             !!!parse-error;  
             $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;  
           $self->{next_input_character} = shift @next_char; # reconsume  
           !!!back-next-input-character (@next_char);  
           $self->{state} = 'data';  
   
           !!!emit ({type => 'character', data => '</'});  
   
1286            redo A;            redo A;
1287          } else {          } else {
1288            $self->{next_input_character} = shift @next_char;            ## No start tag token has ever been emitted
1289            !!!back-next-input-character (@next_char);            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1290            # and consume...            !!!cp (28);
1291              $self->{state} = DATA_STATE;
1292              ## Reconsume.
1293              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1294                        line => $l, column => $c,
1295                       });
1296              redo A;
1297          }          }
1298        }        }
1299          
1300        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1301            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1302          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1303                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1304          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1305          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1306          redo A;                 line => $l, column => $c};
1307        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1308                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1309          $self->{current_token} = {type => 'end tag',          redo A;
1310                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1311          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1312          !!!next-input-character;          !!!cp (30);
1313          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1314        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1315          !!!parse-error;                                    line => $l, column => $c};
1316          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1317          !!!next-input-character;          !!!next-input-character;
1318          redo A;          redo A;
1319        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
1320          !!!parse-error;          !!!cp (31);
1321          $self->{state} = 'data';          !!!parse-error (type => 'empty end tag',
1322                            line => $self->{line_prev}, ## "<" in "</>"
1323                            column => $self->{column_prev} - 1);
1324            $self->{state} = DATA_STATE;
1325            !!!next-input-character;
1326            redo A;
1327          } elsif ($self->{nc} == -1) {
1328            !!!cp (32);
1329            !!!parse-error (type => 'bare etago');
1330            $self->{state} = DATA_STATE;
1331          # reconsume          # reconsume
1332    
1333          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1334                      line => $l, column => $c,
1335                     });
1336    
1337          redo A;          redo A;
1338        } else {        } else {
1339          !!!parse-error;          !!!cp (33);
1340          $self->{state} = 'bogus comment';          !!!parse-error (type => 'bogus end tag');
1341          ## $self->{next_input_character} is intentionally left as is          $self->{state} = BOGUS_COMMENT_STATE;
1342          redo A;          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1343        }                                    line => $self->{line_prev}, # "<" of "</"
1344      } elsif ($self->{state} eq 'tag name') {                                    column => $self->{column_prev} - 1,
1345        if ($self->{next_input_character} == 0x0009 or # HT                                   };
1346            $self->{next_input_character} == 0x000A or # LF          ## NOTE: $self->{nc} is intentionally left as is.
1347            $self->{next_input_character} == 0x000B or # VT          ## Although the "anything else" case of the spec not explicitly
1348            $self->{next_input_character} == 0x000C or # FF          ## states that the next input character is to be reconsumed,
1349            $self->{next_input_character} == 0x0020) { # SP          ## it will be included to the |data| of the comment token
1350          $self->{state} = 'before attribute name';          ## generated from the bogus end tag, as defined in the
1351          !!!next-input-character;          ## "bogus comment state" entry.
1352          redo A;          redo A;
1353        } elsif ($self->{next_input_character} == 0x003E) { # >        }
1354          if ($self->{current_token}->{type} eq 'start tag') {      } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1355            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};        my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1356          } elsif ($self->{current_token}->{type} eq 'end tag') {        if (length $ch) {
1357            $self->{content_model_flag} = 'PCDATA'; # MUST          my $CH = $ch;
1358            if ($self->{current_token}->{attributes}) {          $ch =~ tr/a-z/A-Z/;
1359              !!!parse-error;          my $nch = chr $self->{nc};
1360            }          if ($nch eq $ch or $nch eq $CH) {
1361              !!!cp (24);
1362              ## Stay in the state.
1363              $self->{s_kwd} .= $nch;
1364              !!!next-input-character;
1365              redo A;
1366          } else {          } else {
1367            die "$0: $self->{current_token}->{type}: Unknown token type";            !!!cp (25);
1368              $self->{state} = DATA_STATE;
1369              ## Reconsume.
1370              !!!emit ({type => CHARACTER_TOKEN,
1371                        data => '</' . $self->{s_kwd},
1372                        line => $self->{line_prev},
1373                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1374                       });
1375              redo A;
1376          }          }
1377          $self->{state} = 'data';        } else { # after "<{tag-name}"
1378          !!!next-input-character;          unless ($is_space->{$self->{nc}} or
1379                    {
1380          !!!emit ($self->{current_token}); # start tag or end tag                   0x003E => 1, # >
1381          undef $self->{current_token};                   0x002F => 1, # /
1382                     -1 => 1, # EOF
1383          redo A;                  }->{$self->{nc}}) {
1384        } elsif (0x0041 <= $self->{next_input_character} and            !!!cp (26);
1385                 $self->{next_input_character} <= 0x005A) { # A..Z            ## Reconsume.
1386          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);            $self->{state} = DATA_STATE;
1387              !!!emit ({type => CHARACTER_TOKEN,
1388                        data => '</' . $self->{s_kwd},
1389                        line => $self->{line_prev},
1390                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1391                       });
1392              redo A;
1393            } else {
1394              !!!cp (27);
1395              $self->{ct}
1396                  = {type => END_TAG_TOKEN,
1397                     tag_name => $self->{last_stag_name},
1398                     line => $self->{line_prev},
1399                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1400              $self->{state} = TAG_NAME_STATE;
1401              ## Reconsume.
1402              redo A;
1403            }
1404          }
1405        } elsif ($self->{state} == TAG_NAME_STATE) {
1406          if ($is_space->{$self->{nc}}) {
1407            !!!cp (34);
1408            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1409            !!!next-input-character;
1410            redo A;
1411          } elsif ($self->{nc} == 0x003E) { # >
1412            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1413              !!!cp (35);
1414              $self->{last_stag_name} = $self->{ct}->{tag_name};
1415            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1416              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1417              #if ($self->{ct}->{attributes}) {
1418              #  ## NOTE: This should never be reached.
1419              #  !!! cp (36);
1420              #  !!! parse-error (type => 'end tag attribute');
1421              #} else {
1422                !!!cp (37);
1423              #}
1424            } else {
1425              die "$0: $self->{ct}->{type}: Unknown token type";
1426            }
1427            $self->{state} = DATA_STATE;
1428            !!!next-input-character;
1429    
1430            !!!emit ($self->{ct}); # start tag or end tag
1431    
1432            redo A;
1433          } elsif (0x0041 <= $self->{nc} and
1434                   $self->{nc} <= 0x005A) { # A..Z
1435            !!!cp (38);
1436            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1437            # start tag or end tag            # start tag or end tag
1438          ## Stay in this state          ## Stay in this state
1439          !!!next-input-character;          !!!next-input-character;
1440          redo A;          redo A;
1441        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
1442                 $self->{next_input_character} == -1) {          !!!parse-error (type => 'unclosed tag');
1443          !!!parse-error;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1444          if ($self->{current_token}->{type} eq 'start tag') {            !!!cp (39);
1445            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1446          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1447            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1448            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1449              !!!parse-error;            #  ## NOTE: This state should never be reached.
1450            }            #  !!! cp (40);
1451              #  !!! parse-error (type => 'end tag attribute');
1452              #} else {
1453                !!!cp (41);
1454              #}
1455          } else {          } else {
1456            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1457          }          }
1458          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1459          # reconsume          # reconsume
1460    
1461          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1462    
1463          redo A;          redo A;
1464        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1465            !!!cp (42);
1466            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1467          !!!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;  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1468          redo A;          redo A;
1469        } else {        } else {
1470          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1471            $self->{ct}->{tag_name} .= chr $self->{nc};
1472            # start tag or end tag            # start tag or end tag
1473          ## Stay in the state          ## Stay in the state
1474          !!!next-input-character;          !!!next-input-character;
1475          redo A;          redo A;
1476        }        }
1477      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1478        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1479            $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  
1480          ## Stay in the state          ## Stay in the state
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1484          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1485            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (46);
1486          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1487            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1488            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1489              !!!parse-error;            if ($self->{ct}->{attributes}) {
1490                !!!cp (47);
1491                !!!parse-error (type => 'end tag attribute');
1492              } else {
1493                !!!cp (48);
1494            }            }
1495          } else {          } else {
1496            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1497          }          }
1498          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1499          !!!next-input-character;          !!!next-input-character;
1500    
1501          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1502    
1503          redo A;          redo A;
1504        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1505                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1506          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1507                                value => ''};          $self->{ca}
1508          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1509                   value => '',
1510                   line => $self->{line}, column => $self->{column}};
1511            $self->{state} = ATTRIBUTE_NAME_STATE;
1512          !!!next-input-character;          !!!next-input-character;
1513          redo A;          redo A;
1514        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1515            !!!cp (50);
1516            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1517          !!!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;  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1518          redo A;          redo A;
1519        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
1520                 $self->{next_input_character} == -1) {          !!!parse-error (type => 'unclosed tag');
1521          !!!parse-error;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1522          if ($self->{current_token}->{type} eq 'start tag') {            !!!cp (52);
1523            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1524          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1525            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1526            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1527              !!!parse-error;              !!!cp (53);
1528                !!!parse-error (type => 'end tag attribute');
1529              } else {
1530                !!!cp (54);
1531            }            }
1532          } else {          } else {
1533            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1534          }          }
1535          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1536          # reconsume          # reconsume
1537    
1538          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1539    
1540          redo A;          redo A;
1541        } else {        } else {
1542          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1543                                value => ''};               0x0022 => 1, # "
1544          $self->{state} = 'attribute name';               0x0027 => 1, # '
1545                 0x003D => 1, # =
1546                }->{$self->{nc}}) {
1547              !!!cp (55);
1548              !!!parse-error (type => 'bad attribute name');
1549            } else {
1550              !!!cp (56);
1551            }
1552            $self->{ca}
1553                = {name => chr ($self->{nc}),
1554                   value => '',
1555                   line => $self->{line}, column => $self->{column}};
1556            $self->{state} = ATTRIBUTE_NAME_STATE;
1557          !!!next-input-character;          !!!next-input-character;
1558          redo A;          redo A;
1559        }        }
1560      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1561        my $before_leave = sub {        my $before_leave = sub {
1562          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1563              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1564            !!!parse-error;            !!!cp (57);
1565            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1566          } else {            ## Discard $self->{ca} # MUST
1567            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1568              = $self->{current_attribute};            !!!cp (58);
1569              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1570                = $self->{ca};
1571          }          }
1572        }; # $before_leave        }; # $before_leave
1573    
1574        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1575            $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  
1576          $before_leave->();          $before_leave->();
1577          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1578          !!!next-input-character;          !!!next-input-character;
1579          redo A;          redo A;
1580        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1581            !!!cp (60);
1582          $before_leave->();          $before_leave->();
1583          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1584          !!!next-input-character;          !!!next-input-character;
1585          redo A;          redo A;
1586        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1587          $before_leave->();          $before_leave->();
1588          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1589            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (61);
1590          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1591            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1592            if ($self->{current_token}->{attributes}) {            !!!cp (62);
1593              !!!parse-error;            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1594              if ($self->{ct}->{attributes}) {
1595                !!!parse-error (type => 'end tag attribute');
1596            }            }
1597          } else {          } else {
1598            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1599          }          }
1600          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1601          !!!next-input-character;          !!!next-input-character;
1602    
1603          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1604    
1605          redo A;          redo A;
1606        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1607                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1608          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1609            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1610          ## Stay in the state          ## Stay in the state
1611          !!!next-input-character;          !!!next-input-character;
1612          redo A;          redo A;
1613        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1614            !!!cp (64);
1615          $before_leave->();          $before_leave->();
1616            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1617          !!!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;  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1618          redo A;          redo A;
1619        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
1620                 $self->{next_input_character} == -1) {          !!!parse-error (type => 'unclosed tag');
         !!!parse-error;  
1621          $before_leave->();          $before_leave->();
1622          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1623            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (66);
1624          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1625            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1626            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1627              !!!parse-error;            if ($self->{ct}->{attributes}) {
1628                !!!cp (67);
1629                !!!parse-error (type => 'end tag attribute');
1630              } else {
1631                ## NOTE: This state should never be reached.
1632                !!!cp (68);
1633            }            }
1634          } else {          } else {
1635            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1636          }          }
1637          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1638          # reconsume          # reconsume
1639    
1640          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1641    
1642          redo A;          redo A;
1643        } else {        } else {
1644          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1645                $self->{nc} == 0x0027) { # '
1646              !!!cp (69);
1647              !!!parse-error (type => 'bad attribute name');
1648            } else {
1649              !!!cp (70);
1650            }
1651            $self->{ca}->{name} .= chr ($self->{nc});
1652          ## Stay in the state          ## Stay in the state
1653          !!!next-input-character;          !!!next-input-character;
1654          redo A;          redo A;
1655        }        }
1656      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1657        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1658            $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  
1659          ## Stay in the state          ## Stay in the state
1660          !!!next-input-character;          !!!next-input-character;
1661          redo A;          redo A;
1662        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1663          $self->{state} = 'before attribute value';          !!!cp (72);
1664            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1665          !!!next-input-character;          !!!next-input-character;
1666          redo A;          redo A;
1667        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1668          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1669            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (73);
1670          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1671            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1672            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1673              !!!parse-error;            if ($self->{ct}->{attributes}) {
1674                !!!cp (74);
1675                !!!parse-error (type => 'end tag attribute');
1676              } else {
1677                ## NOTE: This state should never be reached.
1678                !!!cp (75);
1679            }            }
1680          } else {          } else {
1681            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1682          }          }
1683          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1684          !!!next-input-character;          !!!next-input-character;
1685    
1686          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1687    
1688          redo A;          redo A;
1689        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1690                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1691          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1692                                value => ''};          $self->{ca}
1693          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1694                   value => '',
1695                   line => $self->{line}, column => $self->{column}};
1696            $self->{state} = ATTRIBUTE_NAME_STATE;
1697          !!!next-input-character;          !!!next-input-character;
1698          redo A;          redo A;
1699        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1700            !!!cp (77);
1701            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1702          !!!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;  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1703          redo A;          redo A;
1704        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
1705                 $self->{next_input_character} == -1) {          !!!parse-error (type => 'unclosed tag');
1706          !!!parse-error;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1707          if ($self->{current_token}->{type} eq 'start tag') {            !!!cp (79);
1708            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1709          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1710            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1711            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1712              !!!parse-error;              !!!cp (80);
1713                !!!parse-error (type => 'end tag attribute');
1714              } else {
1715                ## NOTE: This state should never be reached.
1716                !!!cp (81);
1717            }            }
1718          } else {          } else {
1719            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1720          }          }
1721          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1722          # reconsume          # reconsume
1723    
1724          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1725    
1726          redo A;          redo A;
1727        } else {        } else {
1728          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1729                                value => ''};              $self->{nc} == 0x0027) { # '
1730          $self->{state} = 'attribute name';            !!!cp (78);
1731              !!!parse-error (type => 'bad attribute name');
1732            } else {
1733              !!!cp (82);
1734            }
1735            $self->{ca}
1736                = {name => chr ($self->{nc}),
1737                   value => '',
1738                   line => $self->{line}, column => $self->{column}};
1739            $self->{state} = ATTRIBUTE_NAME_STATE;
1740          !!!next-input-character;          !!!next-input-character;
1741          redo A;                  redo A;        
1742        }        }
1743      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1744        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1745            $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        
1746          ## Stay in the state          ## Stay in the state
1747          !!!next-input-character;          !!!next-input-character;
1748          redo A;          redo A;
1749        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1750          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1751            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1752          !!!next-input-character;          !!!next-input-character;
1753          redo A;          redo A;
1754        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1755          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1756            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1757          ## reconsume          ## reconsume
1758          redo A;          redo A;
1759        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1760          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1761            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1762          !!!next-input-character;          !!!next-input-character;
1763          redo A;          redo A;
1764        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1765          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1766            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1767          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (87);
1768            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
1769            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1770              !!!parse-error;            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1771              if ($self->{ct}->{attributes}) {
1772                !!!cp (88);
1773                !!!parse-error (type => 'end tag attribute');
1774              } else {
1775                ## NOTE: This state should never be reached.
1776                !!!cp (89);
1777            }            }
1778          } else {          } else {
1779            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1780          }          }
1781          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1782          !!!next-input-character;          !!!next-input-character;
1783    
1784          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1785    
1786          redo A;          redo A;
1787        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
1788                 $self->{next_input_character} == -1) {          !!!parse-error (type => 'unclosed tag');
1789          !!!parse-error;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1790          if ($self->{current_token}->{type} eq 'start tag') {            !!!cp (90);
1791            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1792          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1793            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1794            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1795              !!!parse-error;              !!!cp (91);
1796                !!!parse-error (type => 'end tag attribute');
1797              } else {
1798                ## NOTE: This state should never be reached.
1799                !!!cp (92);
1800            }            }
1801          } else {          } else {
1802            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1803          }          }
1804          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1805          ## reconsume          ## reconsume
1806    
1807          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1808    
1809          redo A;          redo A;
1810        } else {        } else {
1811          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1812          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1813              !!!parse-error (type => 'bad attribute value');
1814            } else {
1815              !!!cp (94);
1816            }
1817            $self->{ca}->{value} .= chr ($self->{nc});
1818            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1819          !!!next-input-character;          !!!next-input-character;
1820          redo A;          redo A;
1821        }        }
1822      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1823        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1824          $self->{state} = 'before attribute name';          !!!cp (95);
1825          !!!next-input-character;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1826            !!!next-input-character;
1827            redo A;
1828          } elsif ($self->{nc} == 0x0026) { # &
1829            !!!cp (96);
1830            ## NOTE: In the spec, the tokenizer is switched to the
1831            ## "entity in attribute value state".  In this implementation, the
1832            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1833            ## implementation of the "consume a character reference" algorithm.
1834            $self->{prev_state} = $self->{state};
1835            $self->{entity_add} = 0x0022; # "
1836            $self->{state} = ENTITY_STATE;
1837            !!!next-input-character;
1838            redo A;
1839          } elsif ($self->{nc} == -1) {
1840            !!!parse-error (type => 'unclosed attribute value');
1841            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1842              !!!cp (97);
1843              $self->{last_stag_name} = $self->{ct}->{tag_name};
1844            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1845              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1846              if ($self->{ct}->{attributes}) {
1847                !!!cp (98);
1848                !!!parse-error (type => 'end tag attribute');
1849              } else {
1850                ## NOTE: This state should never be reached.
1851                !!!cp (99);
1852              }
1853            } else {
1854              die "$0: $self->{ct}->{type}: Unknown token type";
1855            }
1856            $self->{state} = DATA_STATE;
1857            ## reconsume
1858    
1859            !!!emit ($self->{ct}); # start tag or end tag
1860    
1861          redo A;          redo A;
1862        } elsif ($self->{next_input_character} == 0x0026) { # &        } else {
1863          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (100);
1864          $self->{state} = 'entity in attribute value';          $self->{ca}->{value} .= chr ($self->{nc});
1865            $self->{read_until}->($self->{ca}->{value},
1866                                  q["&],
1867                                  length $self->{ca}->{value});
1868    
1869            ## Stay in the state
1870          !!!next-input-character;          !!!next-input-character;
1871          redo A;          redo A;
1872        } elsif ($self->{next_input_character} == -1) {        }
1873          !!!parse-error;      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1874          if ($self->{current_token}->{type} eq 'start tag') {        if ($self->{nc} == 0x0027) { # '
1875            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          !!!cp (101);
1876          } elsif ($self->{current_token}->{type} eq 'end tag') {          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1877            $self->{content_model_flag} = 'PCDATA'; # MUST          !!!next-input-character;
1878            if ($self->{current_token}->{attributes}) {          redo A;
1879              !!!parse-error;        } elsif ($self->{nc} == 0x0026) { # &
1880            !!!cp (102);
1881            ## NOTE: In the spec, the tokenizer is switched to the
1882            ## "entity in attribute value state".  In this implementation, the
1883            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1884            ## implementation of the "consume a character reference" algorithm.
1885            $self->{entity_add} = 0x0027; # '
1886            $self->{prev_state} = $self->{state};
1887            $self->{state} = ENTITY_STATE;
1888            !!!next-input-character;
1889            redo A;
1890          } elsif ($self->{nc} == -1) {
1891            !!!parse-error (type => 'unclosed attribute value');
1892            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1893              !!!cp (103);
1894              $self->{last_stag_name} = $self->{ct}->{tag_name};
1895            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1896              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1897              if ($self->{ct}->{attributes}) {
1898                !!!cp (104);
1899                !!!parse-error (type => 'end tag attribute');
1900              } else {
1901                ## NOTE: This state should never be reached.
1902                !!!cp (105);
1903            }            }
1904          } else {          } else {
1905            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1906          }          }
1907          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1908          ## reconsume          ## reconsume
1909    
1910          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1911    
1912          redo A;          redo A;
1913        } else {        } else {
1914          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1915            $self->{ca}->{value} .= chr ($self->{nc});
1916            $self->{read_until}->($self->{ca}->{value},
1917                                  q['&],
1918                                  length $self->{ca}->{value});
1919    
1920          ## Stay in the state          ## Stay in the state
1921          !!!next-input-character;          !!!next-input-character;
1922          redo A;          redo A;
1923        }        }
1924      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1925        if ($self->{next_input_character} == 0x0027) { # '        if ($is_space->{$self->{nc}}) {
1926          $self->{state} = 'before attribute name';          !!!cp (107);
1927          !!!next-input-character;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1928          redo A;          !!!next-input-character;
1929        } elsif ($self->{next_input_character} == 0x0026) { # &          redo A;
1930          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';        } elsif ($self->{nc} == 0x0026) { # &
1931          $self->{state} = 'entity in attribute value';          !!!cp (108);
1932            ## NOTE: In the spec, the tokenizer is switched to the
1933            ## "entity in attribute value state".  In this implementation, the
1934            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1935            ## implementation of the "consume a character reference" algorithm.
1936            $self->{entity_add} = -1;
1937            $self->{prev_state} = $self->{state};
1938            $self->{state} = ENTITY_STATE;
1939            !!!next-input-character;
1940            redo A;
1941          } elsif ($self->{nc} == 0x003E) { # >
1942            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1943              !!!cp (109);
1944              $self->{last_stag_name} = $self->{ct}->{tag_name};
1945            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1946              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1947              if ($self->{ct}->{attributes}) {
1948                !!!cp (110);
1949                !!!parse-error (type => 'end tag attribute');
1950              } else {
1951                ## NOTE: This state should never be reached.
1952                !!!cp (111);
1953              }
1954            } else {
1955              die "$0: $self->{ct}->{type}: Unknown token type";
1956            }
1957            $self->{state} = DATA_STATE;
1958          !!!next-input-character;          !!!next-input-character;
1959    
1960            !!!emit ($self->{ct}); # start tag or end tag
1961    
1962          redo A;          redo A;
1963        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1964          !!!parse-error;          !!!parse-error (type => 'unclosed tag');
1965          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1966            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (112);
1967          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1968            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1969            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1970              !!!parse-error;            if ($self->{ct}->{attributes}) {
1971                !!!cp (113);
1972                !!!parse-error (type => 'end tag attribute');
1973              } else {
1974                ## NOTE: This state should never be reached.
1975                !!!cp (114);
1976            }            }
1977          } else {          } else {
1978            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1979          }          }
1980          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1981          ## reconsume          ## reconsume
1982    
1983          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1984    
1985          redo A;          redo A;
1986        } else {        } else {
1987          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1988                 0x0022 => 1, # "
1989                 0x0027 => 1, # '
1990                 0x003D => 1, # =
1991                }->{$self->{nc}}) {
1992              !!!cp (115);
1993              !!!parse-error (type => 'bad attribute value');
1994            } else {
1995              !!!cp (116);
1996            }
1997            $self->{ca}->{value} .= chr ($self->{nc});
1998            $self->{read_until}->($self->{ca}->{value},
1999                                  q["'=& >],
2000                                  length $self->{ca}->{value});
2001    
2002          ## Stay in the state          ## Stay in the state
2003          !!!next-input-character;          !!!next-input-character;
2004          redo A;          redo A;
2005        }        }
2006      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2007        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2008            $self->{next_input_character} == 0x000A or # LF          !!!cp (118);
2009            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2010            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
2011            $self->{next_input_character} == 0x0020) { # SP          redo A;
2012          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
2013            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2014              !!!cp (119);
2015              $self->{last_stag_name} = $self->{ct}->{tag_name};
2016            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2017              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2018              if ($self->{ct}->{attributes}) {
2019                !!!cp (120);
2020                !!!parse-error (type => 'end tag attribute');
2021              } else {
2022                ## NOTE: This state should never be reached.
2023                !!!cp (121);
2024              }
2025            } else {
2026              die "$0: $self->{ct}->{type}: Unknown token type";
2027            }
2028            $self->{state} = DATA_STATE;
2029          !!!next-input-character;          !!!next-input-character;
2030    
2031            !!!emit ($self->{ct}); # start tag or end tag
2032    
2033          redo A;          redo A;
2034        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x002F) { # /
2035          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          !!!cp (122);
2036          $self->{state} = 'entity in attribute value';          $self->{state} = SELF_CLOSING_START_TAG_STATE;
2037          !!!next-input-character;          !!!next-input-character;
2038          redo A;          redo A;
2039        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == -1) {
2040          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'unclosed tag');
2041            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2042          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (122.3);
2043            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
2044            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2045              !!!parse-error;            if ($self->{ct}->{attributes}) {
2046                !!!cp (122.1);
2047                !!!parse-error (type => 'end tag attribute');
2048              } else {
2049                ## NOTE: This state should never be reached.
2050                !!!cp (122.2);
2051            }            }
2052          } else {          } else {
2053            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2054          }          }
2055          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2056            ## Reconsume.
2057            !!!emit ($self->{ct}); # start tag or end tag
2058            redo A;
2059          } else {
2060            !!!cp ('124.1');
2061            !!!parse-error (type => 'no space between attributes');
2062            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2063            ## reconsume
2064            redo A;
2065          }
2066        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2067          if ($self->{nc} == 0x003E) { # >
2068            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2069              !!!cp ('124.2');
2070              !!!parse-error (type => 'nestc', token => $self->{ct});
2071              ## TODO: Different type than slash in start tag
2072              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2073              if ($self->{ct}->{attributes}) {
2074                !!!cp ('124.4');
2075                !!!parse-error (type => 'end tag attribute');
2076              } else {
2077                !!!cp ('124.5');
2078              }
2079              ## TODO: Test |<title></title/>|
2080            } else {
2081              !!!cp ('124.3');
2082              $self->{self_closing} = 1;
2083            }
2084    
2085            $self->{state} = DATA_STATE;
2086          !!!next-input-character;          !!!next-input-character;
2087    
2088          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
2089    
2090          redo A;          redo A;
2091        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
2092                 $self->{next_input_character} == -1) {          !!!parse-error (type => 'unclosed tag');
2093          !!!parse-error;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
2094          if ($self->{current_token}->{type} eq 'start tag') {            !!!cp (124.7);
2095            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
2096          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2097            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
2098            if ($self->{current_token}->{attributes}) {              !!!cp (124.5);
2099              !!!parse-error;              !!!parse-error (type => 'end tag attribute');
2100              } else {
2101                ## NOTE: This state should never be reached.
2102                !!!cp (124.6);
2103            }            }
2104          } else {          } else {
2105            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
2106          }          }
2107          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2108          ## reconsume          ## Reconsume.
2109            !!!emit ($self->{ct}); # start tag or end tag
2110            redo A;
2111          } else {
2112            !!!cp ('124.4');
2113            !!!parse-error (type => 'nestc');
2114            ## TODO: This error type is wrong.
2115            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2116            ## Reconsume.
2117            redo A;
2118          }
2119        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2120          ## (only happen if PCDATA state)
2121    
2122          ## NOTE: Unlike spec's "bogus comment state", this implementation
2123          ## consumes characters one-by-one basis.
2124          
2125          if ($self->{nc} == 0x003E) { # >
2126            !!!cp (124);
2127            $self->{state} = DATA_STATE;
2128            !!!next-input-character;
2129    
2130          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # comment
2131          undef $self->{current_token};          redo A;
2132          } elsif ($self->{nc} == -1) {
2133            !!!cp (125);
2134            $self->{state} = DATA_STATE;
2135            ## reconsume
2136    
2137            !!!emit ($self->{ct}); # comment
2138          redo A;          redo A;
2139        } else {        } else {
2140          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (126);
2141          ## Stay in the state          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2142            $self->{read_until}->($self->{ct}->{data},
2143                                  q[>],
2144                                  length $self->{ct}->{data});
2145    
2146            ## Stay in the state.
2147          !!!next-input-character;          !!!next-input-character;
2148          redo A;          redo A;
2149        }        }
2150      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2151        my $token = $self->_tokenize_attempt_to_consume_an_entity;        ## (only happen if PCDATA state)
2152          
2153        unless (defined $token) {        if ($self->{nc} == 0x002D) { # -
2154          $self->{current_attribute}->{value} .= '&';          !!!cp (133);
2155            $self->{state} = MD_HYPHEN_STATE;
2156            !!!next-input-character;
2157            redo A;
2158          } elsif ($self->{nc} == 0x0044 or # D
2159                   $self->{nc} == 0x0064) { # d
2160            ## ASCII case-insensitive.
2161            !!!cp (130);
2162            $self->{state} = MD_DOCTYPE_STATE;
2163            $self->{s_kwd} = chr $self->{nc};
2164            !!!next-input-character;
2165            redo A;
2166          } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2167                   $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2168                   $self->{nc} == 0x005B) { # [
2169            !!!cp (135.4);                
2170            $self->{state} = MD_CDATA_STATE;
2171            $self->{s_kwd} = '[';
2172            !!!next-input-character;
2173            redo A;
2174        } else {        } else {
2175          $self->{current_attribute}->{value} .= $token->{data};          !!!cp (136);
         ## ISSUE: spec says "append the returned character token to the current attribute's value"  
2176        }        }
2177    
2178        $self->{state} = $self->{last_attribute_value_state};        !!!parse-error (type => 'bogus comment',
2179        # next-input-character is already done                        line => $self->{line_prev},
2180                          column => $self->{column_prev} - 1);
2181          ## Reconsume.
2182          $self->{state} = BOGUS_COMMENT_STATE;
2183          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2184                                    line => $self->{line_prev},
2185                                    column => $self->{column_prev} - 1,
2186                                   };
2187        redo A;        redo A;
2188      } elsif ($self->{state} eq 'bogus comment') {      } elsif ($self->{state} == MD_HYPHEN_STATE) {
2189        ## (only happen if PCDATA state)        if ($self->{nc} == 0x002D) { # -
2190                  !!!cp (127);
2191        my $token = {type => 'comment', data => ''};          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2192                                      line => $self->{line_prev},
2193                                      column => $self->{column_prev} - 2,
2194                                     };
2195            $self->{state} = COMMENT_START_STATE;
2196            !!!next-input-character;
2197            redo A;
2198          } else {
2199            !!!cp (128);
2200            !!!parse-error (type => 'bogus comment',
2201                            line => $self->{line_prev},
2202                            column => $self->{column_prev} - 2);
2203            $self->{state} = BOGUS_COMMENT_STATE;
2204            ## Reconsume.
2205            $self->{ct} = {type => COMMENT_TOKEN,
2206                                      data => '-',
2207                                      line => $self->{line_prev},
2208                                      column => $self->{column_prev} - 2,
2209                                     };
2210            redo A;
2211          }
2212        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2213          ## ASCII case-insensitive.
2214          if ($self->{nc} == [
2215                undef,
2216                0x004F, # O
2217                0x0043, # C
2218                0x0054, # T
2219                0x0059, # Y
2220                0x0050, # P
2221              ]->[length $self->{s_kwd}] or
2222              $self->{nc} == [
2223                undef,
2224                0x006F, # o
2225                0x0063, # c
2226                0x0074, # t
2227                0x0079, # y
2228                0x0070, # p
2229              ]->[length $self->{s_kwd}]) {
2230            !!!cp (131);
2231            ## Stay in the state.
2232            $self->{s_kwd} .= chr $self->{nc};
2233            !!!next-input-character;
2234            redo A;
2235          } elsif ((length $self->{s_kwd}) == 6 and
2236                   ($self->{nc} == 0x0045 or # E
2237                    $self->{nc} == 0x0065)) { # e
2238            !!!cp (129);
2239            $self->{state} = DOCTYPE_STATE;
2240            $self->{ct} = {type => DOCTYPE_TOKEN,
2241                                      quirks => 1,
2242                                      line => $self->{line_prev},
2243                                      column => $self->{column_prev} - 7,
2244                                     };
2245            !!!next-input-character;
2246            redo A;
2247          } else {
2248            !!!cp (132);        
2249            !!!parse-error (type => 'bogus comment',
2250                            line => $self->{line_prev},
2251                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2252            $self->{state} = BOGUS_COMMENT_STATE;
2253            ## Reconsume.
2254            $self->{ct} = {type => COMMENT_TOKEN,
2255                                      data => $self->{s_kwd},
2256                                      line => $self->{line_prev},
2257                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2258                                     };
2259            redo A;
2260          }
2261        } elsif ($self->{state} == MD_CDATA_STATE) {
2262          if ($self->{nc} == {
2263                '[' => 0x0043, # C
2264                '[C' => 0x0044, # D
2265                '[CD' => 0x0041, # A
2266                '[CDA' => 0x0054, # T
2267                '[CDAT' => 0x0041, # A
2268              }->{$self->{s_kwd}}) {
2269            !!!cp (135.1);
2270            ## Stay in the state.
2271            $self->{s_kwd} .= chr $self->{nc};
2272            !!!next-input-character;
2273            redo A;
2274          } elsif ($self->{s_kwd} eq '[CDATA' and
2275                   $self->{nc} == 0x005B) { # [
2276            !!!cp (135.2);
2277            $self->{ct} = {type => CHARACTER_TOKEN,
2278                                      data => '',
2279                                      line => $self->{line_prev},
2280                                      column => $self->{column_prev} - 7};
2281            $self->{state} = CDATA_SECTION_STATE;
2282            !!!next-input-character;
2283            redo A;
2284          } else {
2285            !!!cp (135.3);
2286            !!!parse-error (type => 'bogus comment',
2287                            line => $self->{line_prev},
2288                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2289            $self->{state} = BOGUS_COMMENT_STATE;
2290            ## Reconsume.
2291            $self->{ct} = {type => COMMENT_TOKEN,
2292                                      data => $self->{s_kwd},
2293                                      line => $self->{line_prev},
2294                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2295                                     };
2296            redo A;
2297          }
2298        } elsif ($self->{state} == COMMENT_START_STATE) {
2299          if ($self->{nc} == 0x002D) { # -
2300            !!!cp (137);
2301            $self->{state} = COMMENT_START_DASH_STATE;
2302            !!!next-input-character;
2303            redo A;
2304          } elsif ($self->{nc} == 0x003E) { # >
2305            !!!cp (138);
2306            !!!parse-error (type => 'bogus comment');
2307            $self->{state} = DATA_STATE;
2308            !!!next-input-character;
2309    
2310            !!!emit ($self->{ct}); # comment
2311    
2312            redo A;
2313          } elsif ($self->{nc} == -1) {
2314            !!!cp (139);
2315            !!!parse-error (type => 'unclosed comment');
2316            $self->{state} = DATA_STATE;
2317            ## reconsume
2318    
2319        BC: {          !!!emit ($self->{ct}); # comment
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = 'data';  
           !!!next-input-character;  
2320    
2321            !!!emit ($token);          redo A;
2322          } else {
2323            !!!cp (140);
2324            $self->{ct}->{data} # comment
2325                .= chr ($self->{nc});
2326            $self->{state} = COMMENT_STATE;
2327            !!!next-input-character;
2328            redo A;
2329          }
2330        } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2331          if ($self->{nc} == 0x002D) { # -
2332            !!!cp (141);
2333            $self->{state} = COMMENT_END_STATE;
2334            !!!next-input-character;
2335            redo A;
2336          } elsif ($self->{nc} == 0x003E) { # >
2337            !!!cp (142);
2338            !!!parse-error (type => 'bogus comment');
2339            $self->{state} = DATA_STATE;
2340            !!!next-input-character;
2341    
2342            redo A;          !!!emit ($self->{ct}); # comment
         } elsif ($self->{next_input_character} == -1) {  
           $self->{state} = 'data';  
           ## reconsume  
2343    
2344            !!!emit ($token);          redo A;
2345          } elsif ($self->{nc} == -1) {
2346            !!!cp (143);
2347            !!!parse-error (type => 'unclosed comment');
2348            $self->{state} = DATA_STATE;
2349            ## reconsume
2350    
2351            redo A;          !!!emit ($self->{ct}); # comment
         } else {  
           $token->{data} .= chr ($self->{next_input_character});  
           !!!next-input-character;  
           redo BC;  
         }  
       } # BC  
     } elsif ($self->{state} eq 'markup declaration open') {  
       ## (only happen if PCDATA state)  
2352    
2353        my @next_char;          redo A;
2354        push @next_char, $self->{next_input_character};        } else {
2355                  !!!cp (144);
2356        if ($self->{next_input_character} == 0x002D) { # -          $self->{ct}->{data} # comment
2357                .= '-' . chr ($self->{nc});
2358            $self->{state} = COMMENT_STATE;
2359          !!!next-input-character;          !!!next-input-character;
2360          push @next_char, $self->{next_input_character};          redo A;
2361          if ($self->{next_input_character} == 0x002D) { # -        }
2362            $self->{current_token} = {type => 'comment', data => ''};      } elsif ($self->{state} == COMMENT_STATE) {
2363            $self->{state} = 'comment';        if ($self->{nc} == 0x002D) { # -
2364            !!!next-input-character;          !!!cp (145);
2365            redo A;          $self->{state} = COMMENT_END_DASH_STATE;
         }  
       } elsif ($self->{next_input_character} == 0x0044 or # D  
                $self->{next_input_character} == 0x0064) { # d  
2366          !!!next-input-character;          !!!next-input-character;
2367          push @next_char, $self->{next_input_character};          redo A;
2368          if ($self->{next_input_character} == 0x004F or # O        } elsif ($self->{nc} == -1) {
2369              $self->{next_input_character} == 0x006F) { # o          !!!cp (146);
2370            !!!next-input-character;          !!!parse-error (type => 'unclosed comment');
2371            push @next_char, $self->{next_input_character};          $self->{state} = DATA_STATE;
2372            if ($self->{next_input_character} == 0x0043 or # C          ## reconsume
2373                $self->{next_input_character} == 0x0063) { # c  
2374              !!!next-input-character;          !!!emit ($self->{ct}); # comment
2375              push @next_char, $self->{next_input_character};  
2376              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2377                  $self->{next_input_character} == 0x0074) { # t        } else {
2378                !!!next-input-character;          !!!cp (147);
2379                push @next_char, $self->{next_input_character};          $self->{ct}->{data} .= chr ($self->{nc}); # comment
2380                if ($self->{next_input_character} == 0x0059 or # Y          $self->{read_until}->($self->{ct}->{data},
2381                    $self->{next_input_character} == 0x0079) { # y                                q[-],
2382                  !!!next-input-character;                                length $self->{ct}->{data});
2383                  push @next_char, $self->{next_input_character};  
2384                  if ($self->{next_input_character} == 0x0050 or # P          ## Stay in the state
2385                      $self->{next_input_character} == 0x0070) { # p          !!!next-input-character;
2386                    !!!next-input-character;          redo A;
                   push @next_char, $self->{next_input_character};  
                   if ($self->{next_input_character} == 0x0045 or # E  
                       $self->{next_input_character} == 0x0065) { # e  
                     ## ISSUE: What a stupid code this is!  
                     $self->{state} = 'DOCTYPE';  
                     !!!next-input-character;  
                     redo A;  
                   }  
                 }  
               }  
             }  
           }  
         }  
2387        }        }
2388        } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2389          if ($self->{nc} == 0x002D) { # -
2390            !!!cp (148);
2391            $self->{state} = COMMENT_END_STATE;
2392            !!!next-input-character;
2393            redo A;
2394          } elsif ($self->{nc} == -1) {
2395            !!!cp (149);
2396            !!!parse-error (type => 'unclosed comment');
2397            $self->{state} = DATA_STATE;
2398            ## reconsume
2399    
2400        !!!parse-error;          !!!emit ($self->{ct}); # comment
2401        $self->{next_input_character} = shift @next_char;  
2402        !!!back-next-input-character (@next_char);          redo A;
2403        $self->{state} = 'bogus comment';        } else {
2404        redo A;          !!!cp (150);
2405                  $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2406        ## ISSUE: typos in spec: chacacters, is is a parse error          $self->{state} = COMMENT_STATE;
       ## 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?  
     } elsif ($self->{state} eq 'comment') {  
       if ($self->{next_input_character} == 0x002D) { # -  
         $self->{state} = 'comment dash';  
2407          !!!next-input-character;          !!!next-input-character;
2408          redo A;          redo A;
2409        } elsif ($self->{next_input_character} == -1) {        }
2410          !!!parse-error;      } elsif ($self->{state} == COMMENT_END_STATE) {
2411          $self->{state} = 'data';        if ($self->{nc} == 0x003E) { # >
2412            !!!cp (151);
2413            $self->{state} = DATA_STATE;
2414            !!!next-input-character;
2415    
2416            !!!emit ($self->{ct}); # comment
2417    
2418            redo A;
2419          } elsif ($self->{nc} == 0x002D) { # -
2420            !!!cp (152);
2421            !!!parse-error (type => 'dash in comment',
2422                            line => $self->{line_prev},
2423                            column => $self->{column_prev});
2424            $self->{ct}->{data} .= '-'; # comment
2425            ## Stay in the state
2426            !!!next-input-character;
2427            redo A;
2428          } elsif ($self->{nc} == -1) {
2429            !!!cp (153);
2430            !!!parse-error (type => 'unclosed comment');
2431            $self->{state} = DATA_STATE;
2432          ## reconsume          ## reconsume
2433    
2434          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2435    
2436          redo A;          redo A;
2437        } else {        } else {
2438          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (154);
2439            !!!parse-error (type => 'dash in comment',
2440                            line => $self->{line_prev},
2441                            column => $self->{column_prev});
2442            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2443            $self->{state} = COMMENT_STATE;
2444            !!!next-input-character;
2445            redo A;
2446          }
2447        } elsif ($self->{state} == DOCTYPE_STATE) {
2448          if ($is_space->{$self->{nc}}) {
2449            !!!cp (155);
2450            $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2451            !!!next-input-character;
2452            redo A;
2453          } else {
2454            !!!cp (156);
2455            !!!parse-error (type => 'no space before DOCTYPE name');
2456            $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2457            ## reconsume
2458            redo A;
2459          }
2460        } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2461          if ($is_space->{$self->{nc}}) {
2462            !!!cp (157);
2463          ## Stay in the state          ## Stay in the state
2464          !!!next-input-character;          !!!next-input-character;
2465          redo A;          redo A;
2466          } elsif ($self->{nc} == 0x003E) { # >
2467            !!!cp (158);
2468            !!!parse-error (type => 'no DOCTYPE name');
2469            $self->{state} = DATA_STATE;
2470            !!!next-input-character;
2471    
2472            !!!emit ($self->{ct}); # DOCTYPE (quirks)
2473    
2474            redo A;
2475          } elsif ($self->{nc} == -1) {
2476            !!!cp (159);
2477            !!!parse-error (type => 'no DOCTYPE name');
2478            $self->{state} = DATA_STATE;
2479            ## reconsume
2480    
2481            !!!emit ($self->{ct}); # DOCTYPE (quirks)
2482    
2483            redo A;
2484          } else {
2485            !!!cp (160);
2486            $self->{ct}->{name} = chr $self->{nc};
2487            delete $self->{ct}->{quirks};
2488    ## ISSUE: "Set the token's name name to the" in the spec
2489            $self->{state} = DOCTYPE_NAME_STATE;
2490            !!!next-input-character;
2491            redo A;
2492        }        }
2493      } elsif ($self->{state} eq 'comment dash') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2494        if ($self->{next_input_character} == 0x002D) { # -  ## ISSUE: Redundant "First," in the spec.
2495          $self->{state} = 'comment end';        if ($is_space->{$self->{nc}}) {
2496            !!!cp (161);
2497            $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2498            !!!next-input-character;
2499            redo A;
2500          } elsif ($self->{nc} == 0x003E) { # >
2501            !!!cp (162);
2502            $self->{state} = DATA_STATE;
2503          !!!next-input-character;          !!!next-input-character;
2504    
2505            !!!emit ($self->{ct}); # DOCTYPE
2506    
2507          redo A;          redo A;
2508        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2509          !!!parse-error;          !!!cp (163);
2510          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
2511            $self->{state} = DATA_STATE;
2512          ## reconsume          ## reconsume
2513    
2514          !!!emit ($self->{current_token}); # comment          $self->{ct}->{quirks} = 1;
2515          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2516    
2517          redo A;          redo A;
2518        } else {        } else {
2519          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (164);
2520          $self->{state} = 'comment';          $self->{ct}->{name}
2521              .= chr ($self->{nc}); # DOCTYPE
2522            ## Stay in the state
2523          !!!next-input-character;          !!!next-input-character;
2524          redo A;          redo A;
2525        }        }
2526      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2527        if ($self->{next_input_character} == 0x003E) { # >        if ($is_space->{$self->{nc}}) {
2528          $self->{state} = 'data';          !!!cp (165);
2529            ## Stay in the state
2530          !!!next-input-character;          !!!next-input-character;
2531            redo A;
2532          } elsif ($self->{nc} == 0x003E) { # >
2533            !!!cp (166);
2534            $self->{state} = DATA_STATE;
2535            !!!next-input-character;
2536    
2537            !!!emit ($self->{ct}); # DOCTYPE
2538    
2539            redo A;
2540          } elsif ($self->{nc} == -1) {
2541            !!!cp (167);
2542            !!!parse-error (type => 'unclosed DOCTYPE');
2543            $self->{state} = DATA_STATE;
2544            ## reconsume
2545    
2546          !!!emit ($self->{current_token}); # comment          $self->{ct}->{quirks} = 1;
2547          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2548    
2549          redo A;          redo A;
2550        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x0050 or # P
2551          !!!parse-error;                 $self->{nc} == 0x0070) { # p
2552          $self->{current_token}->{data} .= '-'; # comment          $self->{state} = PUBLIC_STATE;
2553            $self->{s_kwd} = chr $self->{nc};
2554            !!!next-input-character;
2555            redo A;
2556          } elsif ($self->{nc} == 0x0053 or # S
2557                   $self->{nc} == 0x0073) { # s
2558            $self->{state} = SYSTEM_STATE;
2559            $self->{s_kwd} = chr $self->{nc};
2560            !!!next-input-character;
2561            redo A;
2562          } else {
2563            !!!cp (180);
2564            !!!parse-error (type => 'string after DOCTYPE name');
2565            $self->{ct}->{quirks} = 1;
2566    
2567            $self->{state} = BOGUS_DOCTYPE_STATE;
2568            !!!next-input-character;
2569            redo A;
2570          }
2571        } elsif ($self->{state} == PUBLIC_STATE) {
2572          ## ASCII case-insensitive
2573          if ($self->{nc} == [
2574                undef,
2575                0x0055, # U
2576                0x0042, # B
2577                0x004C, # L
2578                0x0049, # I
2579              ]->[length $self->{s_kwd}] or
2580              $self->{nc} == [
2581                undef,
2582                0x0075, # u
2583                0x0062, # b
2584                0x006C, # l
2585                0x0069, # i
2586              ]->[length $self->{s_kwd}]) {
2587            !!!cp (175);
2588            ## Stay in the state.
2589            $self->{s_kwd} .= chr $self->{nc};
2590            !!!next-input-character;
2591            redo A;
2592          } elsif ((length $self->{s_kwd}) == 5 and
2593                   ($self->{nc} == 0x0043 or # C
2594                    $self->{nc} == 0x0063)) { # c
2595            !!!cp (168);
2596            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2597            !!!next-input-character;
2598            redo A;
2599          } else {
2600            !!!cp (169);
2601            !!!parse-error (type => 'string after DOCTYPE name',
2602                            line => $self->{line_prev},
2603                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2604            $self->{ct}->{quirks} = 1;
2605    
2606            $self->{state} = BOGUS_DOCTYPE_STATE;
2607            ## Reconsume.
2608            redo A;
2609          }
2610        } elsif ($self->{state} == SYSTEM_STATE) {
2611          ## ASCII case-insensitive
2612          if ($self->{nc} == [
2613                undef,
2614                0x0059, # Y
2615                0x0053, # S
2616                0x0054, # T
2617                0x0045, # E
2618              ]->[length $self->{s_kwd}] or
2619              $self->{nc} == [
2620                undef,
2621                0x0079, # y
2622                0x0073, # s
2623                0x0074, # t
2624                0x0065, # e
2625              ]->[length $self->{s_kwd}]) {
2626            !!!cp (170);
2627            ## Stay in the state.
2628            $self->{s_kwd} .= chr $self->{nc};
2629            !!!next-input-character;
2630            redo A;
2631          } elsif ((length $self->{s_kwd}) == 5 and
2632                   ($self->{nc} == 0x004D or # M
2633                    $self->{nc} == 0x006D)) { # m
2634            !!!cp (171);
2635            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2636            !!!next-input-character;
2637            redo A;
2638          } else {
2639            !!!cp (172);
2640            !!!parse-error (type => 'string after DOCTYPE name',
2641                            line => $self->{line_prev},
2642                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2643            $self->{ct}->{quirks} = 1;
2644    
2645            $self->{state} = BOGUS_DOCTYPE_STATE;
2646            ## Reconsume.
2647            redo A;
2648          }
2649        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2650          if ($is_space->{$self->{nc}}) {
2651            !!!cp (181);
2652          ## Stay in the state          ## Stay in the state
2653          !!!next-input-character;          !!!next-input-character;
2654          redo A;          redo A;
2655        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} eq 0x0022) { # "
2656          !!!parse-error;          !!!cp (182);
2657          $self->{state} = 'data';          $self->{ct}->{pubid} = ''; # DOCTYPE
2658            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2659            !!!next-input-character;
2660            redo A;
2661          } elsif ($self->{nc} eq 0x0027) { # '
2662            !!!cp (183);
2663            $self->{ct}->{pubid} = ''; # DOCTYPE
2664            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2665            !!!next-input-character;
2666            redo A;
2667          } elsif ($self->{nc} eq 0x003E) { # >
2668            !!!cp (184);
2669            !!!parse-error (type => 'no PUBLIC literal');
2670    
2671            $self->{state} = DATA_STATE;
2672            !!!next-input-character;
2673    
2674            $self->{ct}->{quirks} = 1;
2675            !!!emit ($self->{ct}); # DOCTYPE
2676    
2677            redo A;
2678          } elsif ($self->{nc} == -1) {
2679            !!!cp (185);
2680            !!!parse-error (type => 'unclosed DOCTYPE');
2681    
2682            $self->{state} = DATA_STATE;
2683          ## reconsume          ## reconsume
2684    
2685          !!!emit ($self->{current_token}); # comment          $self->{ct}->{quirks} = 1;
2686          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2687    
2688          redo A;          redo A;
2689        } else {        } else {
2690          !!!parse-error;          !!!cp (186);
2691          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'string after PUBLIC');
2692          $self->{state} = 'comment';          $self->{ct}->{quirks} = 1;
2693    
2694            $self->{state} = BOGUS_DOCTYPE_STATE;
2695          !!!next-input-character;          !!!next-input-character;
2696          redo A;          redo A;
2697        }        }
2698      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2699        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{nc} == 0x0022) { # "
2700            $self->{next_input_character} == 0x000A or # LF          !!!cp (187);
2701            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2702            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
2703            $self->{next_input_character} == 0x0020) { # SP          redo A;
2704          $self->{state} = 'before DOCTYPE name';        } elsif ($self->{nc} == 0x003E) { # >
2705            !!!cp (188);
2706            !!!parse-error (type => 'unclosed PUBLIC literal');
2707    
2708            $self->{state} = DATA_STATE;
2709            !!!next-input-character;
2710    
2711            $self->{ct}->{quirks} = 1;
2712            !!!emit ($self->{ct}); # DOCTYPE
2713    
2714            redo A;
2715          } elsif ($self->{nc} == -1) {
2716            !!!cp (189);
2717            !!!parse-error (type => 'unclosed PUBLIC literal');
2718    
2719            $self->{state} = DATA_STATE;
2720            ## reconsume
2721    
2722            $self->{ct}->{quirks} = 1;
2723            !!!emit ($self->{ct}); # DOCTYPE
2724    
2725            redo A;
2726          } else {
2727            !!!cp (190);
2728            $self->{ct}->{pubid} # DOCTYPE
2729                .= chr $self->{nc};
2730            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2731                                  length $self->{ct}->{pubid});
2732    
2733            ## Stay in the state
2734          !!!next-input-character;          !!!next-input-character;
2735          redo A;          redo A;
2736          }
2737        } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2738          if ($self->{nc} == 0x0027) { # '
2739            !!!cp (191);
2740            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2741            !!!next-input-character;
2742            redo A;
2743          } elsif ($self->{nc} == 0x003E) { # >
2744            !!!cp (192);
2745            !!!parse-error (type => 'unclosed PUBLIC literal');
2746    
2747            $self->{state} = DATA_STATE;
2748            !!!next-input-character;
2749    
2750            $self->{ct}->{quirks} = 1;
2751            !!!emit ($self->{ct}); # DOCTYPE
2752    
2753            redo A;
2754          } elsif ($self->{nc} == -1) {
2755            !!!cp (193);
2756            !!!parse-error (type => 'unclosed PUBLIC literal');
2757    
2758            $self->{state} = DATA_STATE;
2759            ## reconsume
2760    
2761            $self->{ct}->{quirks} = 1;
2762            !!!emit ($self->{ct}); # DOCTYPE
2763    
2764            redo A;
2765        } else {        } else {
2766          !!!parse-error;          !!!cp (194);
2767          $self->{state} = 'before DOCTYPE name';          $self->{ct}->{pubid} # DOCTYPE
2768                .= chr $self->{nc};
2769            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2770                                  length $self->{ct}->{pubid});
2771    
2772            ## Stay in the state
2773            !!!next-input-character;
2774            redo A;
2775          }
2776        } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2777          if ($is_space->{$self->{nc}}) {
2778            !!!cp (195);
2779            ## Stay in the state
2780            !!!next-input-character;
2781            redo A;
2782          } elsif ($self->{nc} == 0x0022) { # "
2783            !!!cp (196);
2784            $self->{ct}->{sysid} = ''; # DOCTYPE
2785            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2786            !!!next-input-character;
2787            redo A;
2788          } elsif ($self->{nc} == 0x0027) { # '
2789            !!!cp (197);
2790            $self->{ct}->{sysid} = ''; # DOCTYPE
2791            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2792            !!!next-input-character;
2793            redo A;
2794          } elsif ($self->{nc} == 0x003E) { # >
2795            !!!cp (198);
2796            $self->{state} = DATA_STATE;
2797            !!!next-input-character;
2798    
2799            !!!emit ($self->{ct}); # DOCTYPE
2800    
2801            redo A;
2802          } elsif ($self->{nc} == -1) {
2803            !!!cp (199);
2804            !!!parse-error (type => 'unclosed DOCTYPE');
2805    
2806            $self->{state} = DATA_STATE;
2807          ## reconsume          ## reconsume
2808    
2809            $self->{ct}->{quirks} = 1;
2810            !!!emit ($self->{ct}); # DOCTYPE
2811    
2812            redo A;
2813          } else {
2814            !!!cp (200);
2815            !!!parse-error (type => 'string after PUBLIC literal');
2816            $self->{ct}->{quirks} = 1;
2817    
2818            $self->{state} = BOGUS_DOCTYPE_STATE;
2819            !!!next-input-character;
2820          redo A;          redo A;
2821        }        }
2822      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2823        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2824            $self->{next_input_character} == 0x000A or # LF          !!!cp (201);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2825          ## Stay in the state          ## Stay in the state
2826          !!!next-input-character;          !!!next-input-character;
2827          redo A;          redo A;
2828        } elsif (0x0061 <= $self->{next_input_character} and        } elsif ($self->{nc} == 0x0022) { # "
2829                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (202);
2830          $self->{current_token} = {type => 'DOCTYPE',          $self->{ct}->{sysid} = ''; # DOCTYPE
2831                            name => chr ($self->{next_input_character} - 0x0020),          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
                           error => 1};  
         $self->{state} = 'DOCTYPE name';  
2832          !!!next-input-character;          !!!next-input-character;
2833          redo A;          redo A;
2834        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x0027) { # '
2835          !!!parse-error;          !!!cp (203);
2836          $self->{state} = 'data';          $self->{ct}->{sysid} = ''; # DOCTYPE
2837            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2838            !!!next-input-character;
2839            redo A;
2840          } elsif ($self->{nc} == 0x003E) { # >
2841            !!!cp (204);
2842            !!!parse-error (type => 'no SYSTEM literal');
2843            $self->{state} = DATA_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845    
2846          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          $self->{ct}->{quirks} = 1;
2847            !!!emit ($self->{ct}); # DOCTYPE
2848    
2849          redo A;          redo A;
2850        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2851          !!!parse-error;          !!!cp (205);
2852          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
2853    
2854            $self->{state} = DATA_STATE;
2855          ## reconsume          ## reconsume
2856    
2857          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          $self->{ct}->{quirks} = 1;
2858            !!!emit ($self->{ct}); # DOCTYPE
2859    
2860          redo A;          redo A;
2861        } else {        } else {
2862          $self->{current_token} = {type => 'DOCTYPE',          !!!cp (206);
2863                            name => chr ($self->{next_input_character}),          !!!parse-error (type => 'string after SYSTEM');
2864                            error => 1};          $self->{ct}->{quirks} = 1;
2865          $self->{state} = 'DOCTYPE name';  
2866            $self->{state} = BOGUS_DOCTYPE_STATE;
2867          !!!next-input-character;          !!!next-input-character;
2868          redo A;          redo A;
2869        }        }
2870      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2871        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{nc} == 0x0022) { # "
2872            $self->{next_input_character} == 0x000A or # LF          !!!cp (207);
2873            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_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';  
2874          !!!next-input-character;          !!!next-input-character;
2875          redo A;          redo A;
2876        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2877          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE          !!!cp (208);
2878          $self->{state} = 'data';          !!!parse-error (type => 'unclosed SYSTEM literal');
2879    
2880            $self->{state} = DATA_STATE;
2881          !!!next-input-character;          !!!next-input-character;
2882    
2883          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2884          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2885    
2886          redo A;          redo A;
2887        } elsif (0x0061 <= $self->{next_input_character} and        } elsif ($self->{nc} == -1) {
2888                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (209);
2889          $self->{current_token}->{name} .= chr ($self->{next_input_character} - 0x0020); # DOCTYPE          !!!parse-error (type => 'unclosed SYSTEM literal');
2890          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');  
2891            $self->{state} = DATA_STATE;
2892            ## reconsume
2893    
2894            $self->{ct}->{quirks} = 1;
2895            !!!emit ($self->{ct}); # DOCTYPE
2896    
2897            redo A;
2898          } else {
2899            !!!cp (210);
2900            $self->{ct}->{sysid} # DOCTYPE
2901                .= chr $self->{nc};
2902            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2903                                  length $self->{ct}->{sysid});
2904    
2905          ## Stay in the state          ## Stay in the state
2906          !!!next-input-character;          !!!next-input-character;
2907          redo A;          redo A;
2908        } elsif ($self->{next_input_character} == -1) {        }
2909          !!!parse-error;      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2910          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE        if ($self->{nc} == 0x0027) { # '
2911          $self->{state} = 'data';          !!!cp (211);
2912            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2913            !!!next-input-character;
2914            redo A;
2915          } elsif ($self->{nc} == 0x003E) { # >
2916            !!!cp (212);
2917            !!!parse-error (type => 'unclosed SYSTEM literal');
2918    
2919            $self->{state} = DATA_STATE;
2920            !!!next-input-character;
2921    
2922            $self->{ct}->{quirks} = 1;
2923            !!!emit ($self->{ct}); # DOCTYPE
2924    
2925            redo A;
2926          } elsif ($self->{nc} == -1) {
2927            !!!cp (213);
2928            !!!parse-error (type => 'unclosed SYSTEM literal');
2929    
2930            $self->{state} = DATA_STATE;
2931          ## reconsume          ## reconsume
2932    
2933          !!!emit ($self->{current_token});          $self->{ct}->{quirks} = 1;
2934          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2935    
2936          redo A;          redo A;
2937        } else {        } else {
2938          $self->{current_token}->{name}          !!!cp (214);
2939            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{sysid} # DOCTYPE
2940          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');              .= chr $self->{nc};
2941            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2942                                  length $self->{ct}->{sysid});
2943    
2944          ## Stay in the state          ## Stay in the state
2945          !!!next-input-character;          !!!next-input-character;
2946          redo A;          redo A;
2947        }        }
2948      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2949        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2950            $self->{next_input_character} == 0x000A or # LF          !!!cp (215);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2951          ## Stay in the state          ## Stay in the state
2952          !!!next-input-character;          !!!next-input-character;
2953          redo A;          redo A;
2954        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2955          $self->{state} = 'data';          !!!cp (216);
2956            $self->{state} = DATA_STATE;
2957          !!!next-input-character;          !!!next-input-character;
2958    
2959          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
         undef $self->{current_token};  
2960    
2961          redo A;          redo A;
2962        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2963          !!!parse-error;          !!!cp (217);
2964          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
2965            $self->{state} = DATA_STATE;
2966          ## reconsume          ## reconsume
2967    
2968          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2969          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2970    
2971          redo A;          redo A;
2972        } else {        } else {
2973          !!!parse-error;          !!!cp (218);
2974          $self->{current_token}->{error} = 1; # DOCTYPE          !!!parse-error (type => 'string after SYSTEM literal');
2975          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2976    
2977            $self->{state} = BOGUS_DOCTYPE_STATE;
2978          !!!next-input-character;          !!!next-input-character;
2979          redo A;          redo A;
2980        }        }
2981      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2982        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2983          $self->{state} = 'data';          !!!cp (219);
2984            $self->{state} = DATA_STATE;
2985          !!!next-input-character;          !!!next-input-character;
2986    
2987          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
         undef $self->{current_token};  
2988    
2989          redo A;          redo A;
2990        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2991          !!!parse-error;          !!!cp (220);
2992          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
2993            $self->{state} = DATA_STATE;
2994          ## reconsume          ## reconsume
2995    
2996          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
         undef $self->{current_token};  
2997    
2998          redo A;          redo A;
2999        } else {        } else {
3000            !!!cp (221);
3001            my $s = '';
3002            $self->{read_until}->($s, q[>], 0);
3003    
3004          ## Stay in the state          ## Stay in the state
3005          !!!next-input-character;          !!!next-input-character;
3006          redo A;          redo A;
3007        }        }
3008      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3009        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3010      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3011    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3012          
3013          if ($self->{nc} == 0x005D) { # ]
3014            !!!cp (221.1);
3015            $self->{state} = CDATA_SECTION_MSE1_STATE;
3016            !!!next-input-character;
3017            redo A;
3018          } elsif ($self->{nc} == -1) {
3019            $self->{state} = DATA_STATE;
3020            !!!next-input-character;
3021            if (length $self->{ct}->{data}) { # character
3022              !!!cp (221.2);
3023              !!!emit ($self->{ct}); # character
3024            } else {
3025              !!!cp (221.3);
3026              ## No token to emit. $self->{ct} is discarded.
3027            }        
3028            redo A;
3029          } else {
3030            !!!cp (221.4);
3031            $self->{ct}->{data} .= chr $self->{nc};
3032            $self->{read_until}->($self->{ct}->{data},
3033                                  q<]>,
3034                                  length $self->{ct}->{data});
3035    
3036    die "$0: _get_next_token: unexpected case";          ## Stay in the state.
3037  } # _get_next_token          !!!next-input-character;
3038            redo A;
3039          }
3040    
3041  sub _tokenize_attempt_to_consume_an_entity ($) {        ## ISSUE: "text tokens" in spec.
3042    my $self = shift;      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3043            if ($self->{nc} == 0x005D) { # ]
3044    if ($self->{next_input_character} == 0x0023) { # #          !!!cp (221.5);
3045      !!!next-input-character;          $self->{state} = CDATA_SECTION_MSE2_STATE;
3046      my $num;          !!!next-input-character;
3047      if ($self->{next_input_character} == 0x0078 or # x          redo A;
3048          $self->{next_input_character} == 0x0058) { # X        } else {
3049        X: {          !!!cp (221.6);
3050          my $x_char = $self->{next_input_character};          $self->{ct}->{data} .= ']';
3051          !!!next-input-character;          $self->{state} = CDATA_SECTION_STATE;
3052          if (0x0030 <= $self->{next_input_character} and          ## Reconsume.
3053              $self->{next_input_character} <= 0x0039) { # 0..9          redo A;
3054            $num ||= 0;        }
3055            $num *= 0x10;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3056            $num += $self->{next_input_character} - 0x0030;        if ($self->{nc} == 0x003E) { # >
3057            redo X;          $self->{state} = DATA_STATE;
3058          } elsif (0x0061 <= $self->{next_input_character} and          !!!next-input-character;
3059                   $self->{next_input_character} <= 0x0066) { # a..f          if (length $self->{ct}->{data}) { # character
3060            ## ISSUE: the spec says U+0078, which is apparently incorrect            !!!cp (221.7);
3061            $num ||= 0;            !!!emit ($self->{ct}); # character
           $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;  
           $self->{next_input_character} = 0x0023; # #  
           !!!back-next-input-character ($x_char);  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3062          } else {          } else {
3063            !!!parse-error;            !!!cp (221.8);
3064              ## No token to emit. $self->{ct} is discarded.
3065          }          }
3066            redo A;
3067          } elsif ($self->{nc} == 0x005D) { # ]
3068            !!!cp (221.9); # character
3069            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3070            ## Stay in the state.
3071            !!!next-input-character;
3072            redo A;
3073          } else {
3074            !!!cp (221.11);
3075            $self->{ct}->{data} .= ']]'; # character
3076            $self->{state} = CDATA_SECTION_STATE;
3077            ## Reconsume.
3078            redo A;
3079          }
3080        } elsif ($self->{state} == ENTITY_STATE) {
3081          if ($is_space->{$self->{nc}} or
3082              {
3083                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3084                $self->{entity_add} => 1,
3085              }->{$self->{nc}}) {
3086            !!!cp (1001);
3087            ## Don't consume
3088            ## No error
3089            ## Return nothing.
3090            #
3091          } elsif ($self->{nc} == 0x0023) { # #
3092            !!!cp (999);
3093            $self->{state} = ENTITY_HASH_STATE;
3094            $self->{s_kwd} = '#';
3095            !!!next-input-character;
3096            redo A;
3097          } elsif ((0x0041 <= $self->{nc} and
3098                    $self->{nc} <= 0x005A) or # A..Z
3099                   (0x0061 <= $self->{nc} and
3100                    $self->{nc} <= 0x007A)) { # a..z
3101            !!!cp (998);
3102            require Whatpm::_NamedEntityList;
3103            $self->{state} = ENTITY_NAME_STATE;
3104            $self->{s_kwd} = chr $self->{nc};
3105            $self->{entity__value} = $self->{s_kwd};
3106            $self->{entity__match} = 0;
3107            !!!next-input-character;
3108            redo A;
3109          } else {
3110            !!!cp (1027);
3111            !!!parse-error (type => 'bare ero');
3112            ## Return nothing.
3113            #
3114          }
3115    
3116          ## TODO: check the definition for |a valid Unicode character|.        ## NOTE: No character is consumed by the "consume a character
3117          if ($num > 1114111 or $num == 0) {        ## reference" algorithm.  In other word, there is an "&" character
3118            $num = 0xFFFD; # REPLACEMENT CHARACTER        ## that does not introduce a character reference, which would be
3119            ## ISSUE: Why this is not an error?        ## appended to the parent element or the attribute value in later
3120          ## process of the tokenizer.
3121    
3122          if ($self->{prev_state} == DATA_STATE) {
3123            !!!cp (997);
3124            $self->{state} = $self->{prev_state};
3125            ## Reconsume.
3126            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3127                      line => $self->{line_prev},
3128                      column => $self->{column_prev},
3129                     });
3130            redo A;
3131          } else {
3132            !!!cp (996);
3133            $self->{ca}->{value} .= '&';
3134            $self->{state} = $self->{prev_state};
3135            ## Reconsume.
3136            redo A;
3137          }
3138        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3139          if ($self->{nc} == 0x0078 or # x
3140              $self->{nc} == 0x0058) { # X
3141            !!!cp (995);
3142            $self->{state} = HEXREF_X_STATE;
3143            $self->{s_kwd} .= chr $self->{nc};
3144            !!!next-input-character;
3145            redo A;
3146          } elsif (0x0030 <= $self->{nc} and
3147                   $self->{nc} <= 0x0039) { # 0..9
3148            !!!cp (994);
3149            $self->{state} = NCR_NUM_STATE;
3150            $self->{s_kwd} = $self->{nc} - 0x0030;
3151            !!!next-input-character;
3152            redo A;
3153          } else {
3154            !!!parse-error (type => 'bare nero',
3155                            line => $self->{line_prev},
3156                            column => $self->{column_prev} - 1);
3157    
3158            ## NOTE: According to the spec algorithm, nothing is returned,
3159            ## and then "&#" is appended to the parent element or the attribute
3160            ## value in the later processing.
3161    
3162            if ($self->{prev_state} == DATA_STATE) {
3163              !!!cp (1019);
3164              $self->{state} = $self->{prev_state};
3165              ## Reconsume.
3166              !!!emit ({type => CHARACTER_TOKEN,
3167                        data => '&#',
3168                        line => $self->{line_prev},
3169                        column => $self->{column_prev} - 1,
3170                       });
3171              redo A;
3172            } else {
3173              !!!cp (993);
3174              $self->{ca}->{value} .= '&#';
3175              $self->{state} = $self->{prev_state};
3176              ## Reconsume.
3177              redo A;
3178          }          }
3179          }
3180          return {type => 'character', data => chr $num};      } elsif ($self->{state} == NCR_NUM_STATE) {
3181        } # X        if (0x0030 <= $self->{nc} and
3182      } elsif (0x0030 <= $self->{next_input_character} and            $self->{nc} <= 0x0039) { # 0..9
3183               $self->{next_input_character} <= 0x0039) { # 0..9          !!!cp (1012);
3184        my $code = $self->{next_input_character} - 0x0030;          $self->{s_kwd} *= 10;
3185        !!!next-input-character;          $self->{s_kwd} += $self->{nc} - 0x0030;
         
       while (0x0030 <= $self->{next_input_character} and  
                 $self->{next_input_character} <= 0x0039) { # 0..9  
         $code *= 10;  
         $code += $self->{next_input_character} - 0x0030;  
3186                    
3187            ## Stay in the state.
3188            !!!next-input-character;
3189            redo A;
3190          } elsif ($self->{nc} == 0x003B) { # ;
3191            !!!cp (1013);
3192          !!!next-input-character;          !!!next-input-character;
3193            #
3194          } else {
3195            !!!cp (1014);
3196            !!!parse-error (type => 'no refc');
3197            ## Reconsume.
3198            #
3199        }        }
3200    
3201        if ($self->{next_input_character} == 0x003B) { # ;        my $code = $self->{s_kwd};
3202          my $l = $self->{line_prev};
3203          my $c = $self->{column_prev};
3204          if ($charref_map->{$code}) {
3205            !!!cp (1015);
3206            !!!parse-error (type => 'invalid character reference',
3207                            text => (sprintf 'U+%04X', $code),
3208                            line => $l, column => $c);
3209            $code = $charref_map->{$code};
3210          } elsif ($code > 0x10FFFF) {
3211            !!!cp (1016);
3212            !!!parse-error (type => 'invalid character reference',
3213                            text => (sprintf 'U-%08X', $code),
3214                            line => $l, column => $c);
3215            $code = 0xFFFD;
3216          }
3217    
3218          if ($self->{prev_state} == DATA_STATE) {
3219            !!!cp (992);
3220            $self->{state} = $self->{prev_state};
3221            ## Reconsume.
3222            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3223                      line => $l, column => $c,
3224                     });
3225            redo A;
3226          } else {
3227            !!!cp (991);
3228            $self->{ca}->{value} .= chr $code;
3229            $self->{ca}->{has_reference} = 1;
3230            $self->{state} = $self->{prev_state};
3231            ## Reconsume.
3232            redo A;
3233          }
3234        } elsif ($self->{state} == HEXREF_X_STATE) {
3235          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3236              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3237              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3238            # 0..9, A..F, a..f
3239            !!!cp (990);
3240            $self->{state} = HEXREF_HEX_STATE;
3241            $self->{s_kwd} = 0;
3242            ## Reconsume.
3243            redo A;
3244          } else {
3245            !!!parse-error (type => 'bare hcro',
3246                            line => $self->{line_prev},
3247                            column => $self->{column_prev} - 2);
3248    
3249            ## NOTE: According to the spec algorithm, nothing is returned,
3250            ## and then "&#" followed by "X" or "x" is appended to the parent
3251            ## element or the attribute value in the later processing.
3252    
3253            if ($self->{prev_state} == DATA_STATE) {
3254              !!!cp (1005);
3255              $self->{state} = $self->{prev_state};
3256              ## Reconsume.
3257              !!!emit ({type => CHARACTER_TOKEN,
3258                        data => '&' . $self->{s_kwd},
3259                        line => $self->{line_prev},
3260                        column => $self->{column_prev} - length $self->{s_kwd},
3261                       });
3262              redo A;
3263            } else {
3264              !!!cp (989);
3265              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3266              $self->{state} = $self->{prev_state};
3267              ## Reconsume.
3268              redo A;
3269            }
3270          }
3271        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3272          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3273            # 0..9
3274            !!!cp (1002);
3275            $self->{s_kwd} *= 0x10;
3276            $self->{s_kwd} += $self->{nc} - 0x0030;
3277            ## Stay in the state.
3278            !!!next-input-character;
3279            redo A;
3280          } elsif (0x0061 <= $self->{nc} and
3281                   $self->{nc} <= 0x0066) { # a..f
3282            !!!cp (1003);
3283            $self->{s_kwd} *= 0x10;
3284            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3285            ## Stay in the state.
3286            !!!next-input-character;
3287            redo A;
3288          } elsif (0x0041 <= $self->{nc} and
3289                   $self->{nc} <= 0x0046) { # A..F
3290            !!!cp (1004);
3291            $self->{s_kwd} *= 0x10;
3292            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3293            ## Stay in the state.
3294          !!!next-input-character;          !!!next-input-character;
3295            redo A;
3296          } elsif ($self->{nc} == 0x003B) { # ;
3297            !!!cp (1006);
3298            !!!next-input-character;
3299            #
3300        } else {        } else {
3301          !!!parse-error;          !!!cp (1007);
3302            !!!parse-error (type => 'no refc',
3303                            line => $self->{line},
3304                            column => $self->{column});
3305            ## Reconsume.
3306            #
3307        }        }
3308    
3309        ## TODO: check the definition for |a valid Unicode character|.        my $code = $self->{s_kwd};
3310        if ($code > 1114111 or $code == 0) {        my $l = $self->{line_prev};
3311          $code = 0xFFFD; # REPLACEMENT CHARACTER        my $c = $self->{column_prev};
3312          ## ISSUE: Why this is not an error?        if ($charref_map->{$code}) {
3313            !!!cp (1008);
3314            !!!parse-error (type => 'invalid character reference',
3315                            text => (sprintf 'U+%04X', $code),
3316                            line => $l, column => $c);
3317            $code = $charref_map->{$code};
3318          } elsif ($code > 0x10FFFF) {
3319            !!!cp (1009);
3320            !!!parse-error (type => 'invalid character reference',
3321                            text => (sprintf 'U-%08X', $code),
3322                            line => $l, column => $c);
3323            $code = 0xFFFD;
3324          }
3325    
3326          if ($self->{prev_state} == DATA_STATE) {
3327            !!!cp (988);
3328            $self->{state} = $self->{prev_state};
3329            ## Reconsume.
3330            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3331                      line => $l, column => $c,
3332                     });
3333            redo A;
3334          } else {
3335            !!!cp (987);
3336            $self->{ca}->{value} .= chr $code;
3337            $self->{ca}->{has_reference} = 1;
3338            $self->{state} = $self->{prev_state};
3339            ## Reconsume.
3340            redo A;
3341          }
3342        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3343          if (length $self->{s_kwd} < 30 and
3344              ## NOTE: Some number greater than the maximum length of entity name
3345              ((0x0041 <= $self->{nc} and # a
3346                $self->{nc} <= 0x005A) or # x
3347               (0x0061 <= $self->{nc} and # a
3348                $self->{nc} <= 0x007A) or # z
3349               (0x0030 <= $self->{nc} and # 0
3350                $self->{nc} <= 0x0039) or # 9
3351               $self->{nc} == 0x003B)) { # ;
3352            our $EntityChar;
3353            $self->{s_kwd} .= chr $self->{nc};
3354            if (defined $EntityChar->{$self->{s_kwd}}) {
3355              if ($self->{nc} == 0x003B) { # ;
3356                !!!cp (1020);
3357                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3358                $self->{entity__match} = 1;
3359                !!!next-input-character;
3360                #
3361              } else {
3362                !!!cp (1021);
3363                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3364                $self->{entity__match} = -1;
3365                ## Stay in the state.
3366                !!!next-input-character;
3367                redo A;
3368              }
3369            } else {
3370              !!!cp (1022);
3371              $self->{entity__value} .= chr $self->{nc};
3372              $self->{entity__match} *= 2;
3373              ## Stay in the state.
3374              !!!next-input-character;
3375              redo A;
3376            }
3377        }        }
3378          
3379        return {type => 'character', data => chr $code};        my $data;
3380      } else {        my $has_ref;
3381        !!!parse-error;        if ($self->{entity__match} > 0) {
3382        !!!back-next-input-character ($self->{next_input_character});          !!!cp (1023);
3383        $self->{next_input_character} = 0x0023; # #          $data = $self->{entity__value};
3384        return undef;          $has_ref = 1;
3385      }          #
3386    } elsif ((0x0041 <= $self->{next_input_character} and        } elsif ($self->{entity__match} < 0) {
3387              $self->{next_input_character} <= 0x005A) or          !!!parse-error (type => 'no refc');
3388             (0x0061 <= $self->{next_input_character} and          if ($self->{prev_state} != DATA_STATE and # in attribute
3389              $self->{next_input_character} <= 0x007A)) {              $self->{entity__match} < -1) {
3390      my $entity_name = chr $self->{next_input_character};            !!!cp (1024);
3391      !!!next-input-character;            $data = '&' . $self->{s_kwd};
3392              #
3393      my $value = $entity_name;          } else {
3394      my $match;            !!!cp (1025);
3395              $data = $self->{entity__value};
3396      while (length $entity_name < 10 and            $has_ref = 1;
3397             ## NOTE: Some number greater than the maximum length of entity name            #
3398             ((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;  
3399        } else {        } else {
3400          $value .= chr $self->{next_input_character};          !!!cp (1026);
3401            !!!parse-error (type => 'bare ero',
3402                            line => $self->{line_prev},
3403                            column => $self->{column_prev} - length $self->{s_kwd});
3404            $data = '&' . $self->{s_kwd};
3405            #
3406        }        }
3407        !!!next-input-character;    
3408      }        ## NOTE: In these cases, when a character reference is found,
3409              ## it is consumed and a character token is returned, or, otherwise,
3410      if ($match) {        ## nothing is consumed and returned, according to the spec algorithm.
3411        if ($self->{next_input_character} == 0x003B) { # ;        ## In this implementation, anything that has been examined by the
3412          !!!next-input-character;        ## tokenizer is appended to the parent element or the attribute value
3413          ## as string, either literal string when no character reference or
3414          ## entity-replaced string otherwise, in this stage, since any characters
3415          ## that would not be consumed are appended in the data state or in an
3416          ## appropriate attribute value state anyway.
3417    
3418          if ($self->{prev_state} == DATA_STATE) {
3419            !!!cp (986);
3420            $self->{state} = $self->{prev_state};
3421            ## Reconsume.
3422            !!!emit ({type => CHARACTER_TOKEN,
3423                      data => $data,
3424                      line => $self->{line_prev},
3425                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3426                     });
3427            redo A;
3428        } else {        } else {
3429          !!!parse-error;          !!!cp (985);
3430            $self->{ca}->{value} .= $data;
3431            $self->{ca}->{has_reference} = 1 if $has_ref;
3432            $self->{state} = $self->{prev_state};
3433            ## Reconsume.
3434            redo A;
3435        }        }
   
       return {type => 'character', data => $value};  
3436      } else {      } else {
3437        !!!parse-error;        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       !!!back-token ({type => 'character', data => $value});  
       return undef;  
3438      }      }
3439    } else {    } # A  
3440      ## no characters are consumed  
3441      !!!parse-error;    die "$0: _get_next_token: unexpected case";
3442      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3443    
3444  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3445    my $self = shift;    my $self = shift;
# Line 1576  sub _initialize_tree_constructor ($) { Line 3447  sub _initialize_tree_constructor ($) {
3447    $self->{document}->strict_error_checking (0);    $self->{document}->strict_error_checking (0);
3448    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3449    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3450    ## TODO: Mark the Document as an HTML document # MUST    $self->{document}->manakai_is_html (1); # MUST
3451      $self->{document}->set_user_data (manakai_source_line => 1);
3452      $self->{document}->set_user_data (manakai_source_column => 1);
3453  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3454    
3455  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1587  sub _terminate_tree_constructor ($) { Line 3460  sub _terminate_tree_constructor ($) {
3460    
3461  ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?  ## ISSUE: Should append_child (for example) in script executed in tree construction stage fire mutation events?
3462    
3463    { # tree construction stage
3464      my $token;
3465    
3466  sub _construct_tree ($) {  sub _construct_tree ($) {
3467    my ($self) = @_;    my ($self) = @_;
3468    
# Line 1598  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474    ## characters and insert one Text node whose data is concatenation    ## characters and insert one Text node whose data is concatenation
3475    ## of all those characters. # MUST    ## of all those characters. # MUST
3476        
   my $token;  
3477    !!!next-token;    !!!next-token;
3478    
3479    my $phase = 'initial'; # MUST    undef $self->{form_element};
3480      undef $self->{head_element};
3481      $self->{open_elements} = [];
3482      undef $self->{inner_html_node};
3483    
3484      ## NOTE: The "initial" insertion mode.
3485      $self->_tree_construction_initial; # MUST
3486    
3487      ## NOTE: The "before html" insertion mode.
3488      $self->_tree_construction_root_element;
3489      $self->{insertion_mode} = BEFORE_HEAD_IM;
3490    
3491      ## NOTE: The "before head" insertion mode and so on.
3492      $self->_tree_construction_main;
3493    } # _construct_tree
3494    
3495    sub _tree_construction_initial ($) {
3496      my $self = shift;
3497    
3498      ## NOTE: "initial" insertion mode
3499    
3500      INITIAL: {
3501        if ($token->{type} == DOCTYPE_TOKEN) {
3502          ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3503          ## error, switch to a conformance checking mode for another
3504          ## language.
3505          my $doctype_name = $token->{name};
3506          $doctype_name = '' unless defined $doctype_name;
3507          $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3508          if (not defined $token->{name} or # <!DOCTYPE>
3509              defined $token->{sysid}) {
3510            !!!cp ('t1');
3511            !!!parse-error (type => 'not HTML5', token => $token);
3512          } elsif ($doctype_name ne 'HTML') {
3513            !!!cp ('t2');
3514            !!!parse-error (type => 'not HTML5', token => $token);
3515          } elsif (defined $token->{pubid}) {
3516            if ($token->{pubid} eq 'XSLT-compat') {
3517              !!!cp ('t1.2');
3518              !!!parse-error (type => 'XSLT-compat', token => $token,
3519                              level => $self->{level}->{should});
3520            } else {
3521              !!!parse-error (type => 'not HTML5', token => $token);
3522            }
3523          } else {
3524            !!!cp ('t3');
3525            #
3526          }
3527          
3528          my $doctype = $self->{document}->create_document_type_definition
3529            ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3530          ## NOTE: Default value for both |public_id| and |system_id| attributes
3531          ## are empty strings, so that we don't set any value in missing cases.
3532          $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3533          $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3534          ## NOTE: Other DocumentType attributes are null or empty lists.
3535          ## ISSUE: internalSubset = null??
3536          $self->{document}->append_child ($doctype);
3537          
3538          if ($token->{quirks} or $doctype_name ne 'HTML') {
3539            !!!cp ('t4');
3540            $self->{document}->manakai_compat_mode ('quirks');
3541          } elsif (defined $token->{pubid}) {
3542            my $pubid = $token->{pubid};
3543            $pubid =~ tr/a-z/A-z/;
3544            my $prefix = [
3545              "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3546              "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3547              "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3548              "-//IETF//DTD HTML 2.0 LEVEL 1//",
3549              "-//IETF//DTD HTML 2.0 LEVEL 2//",
3550              "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3551              "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3552              "-//IETF//DTD HTML 2.0 STRICT//",
3553              "-//IETF//DTD HTML 2.0//",
3554              "-//IETF//DTD HTML 2.1E//",
3555              "-//IETF//DTD HTML 3.0//",
3556              "-//IETF//DTD HTML 3.2 FINAL//",
3557              "-//IETF//DTD HTML 3.2//",
3558              "-//IETF//DTD HTML 3//",
3559              "-//IETF//DTD HTML LEVEL 0//",
3560              "-//IETF//DTD HTML LEVEL 1//",
3561              "-//IETF//DTD HTML LEVEL 2//",
3562              "-//IETF//DTD HTML LEVEL 3//",
3563              "-//IETF//DTD HTML STRICT LEVEL 0//",
3564              "-//IETF//DTD HTML STRICT LEVEL 1//",
3565              "-//IETF//DTD HTML STRICT LEVEL 2//",
3566              "-//IETF//DTD HTML STRICT LEVEL 3//",
3567              "-//IETF//DTD HTML STRICT//",
3568              "-//IETF//DTD HTML//",
3569              "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3570              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3571              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3572              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3573              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3574              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3575              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3576              "-//NETSCAPE COMM. CORP.//DTD HTML//",
3577              "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3578              "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3579              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3580              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3581              "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3582              "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3583              "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3584              "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3585              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3586              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3587              "-//W3C//DTD HTML 3 1995-03-24//",
3588              "-//W3C//DTD HTML 3.2 DRAFT//",
3589              "-//W3C//DTD HTML 3.2 FINAL//",
3590              "-//W3C//DTD HTML 3.2//",
3591              "-//W3C//DTD HTML 3.2S DRAFT//",
3592              "-//W3C//DTD HTML 4.0 FRAMESET//",
3593              "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3594              "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3595              "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3596              "-//W3C//DTD W3 HTML//",
3597              "-//W3O//DTD W3 HTML 3.0//",
3598              "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3599              "-//WEBTECHS//DTD MOZILLA HTML//",
3600            ]; # $prefix
3601            my $match;
3602            for (@$prefix) {
3603              if (substr ($prefix, 0, length $_) eq $_) {
3604                $match = 1;
3605                last;
3606              }
3607            }
3608            if ($match or
3609                $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3610                $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3611                $pubid eq "HTML") {
3612              !!!cp ('t5');
3613              $self->{document}->manakai_compat_mode ('quirks');
3614            } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3615                     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3616              if (defined $token->{sysid}) {
3617                !!!cp ('t6');
3618                $self->{document}->manakai_compat_mode ('quirks');
3619              } else {
3620                !!!cp ('t7');
3621                $self->{document}->manakai_compat_mode ('limited quirks');
3622              }
3623            } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3624                     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3625              !!!cp ('t8');
3626              $self->{document}->manakai_compat_mode ('limited quirks');
3627            } else {
3628              !!!cp ('t9');
3629            }
3630          } else {
3631            !!!cp ('t10');
3632          }
3633          if (defined $token->{sysid}) {
3634            my $sysid = $token->{sysid};
3635            $sysid =~ tr/A-Z/a-z/;
3636            if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3637              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3638              ## marked as quirks.
3639              $self->{document}->manakai_compat_mode ('quirks');
3640              !!!cp ('t11');
3641            } else {
3642              !!!cp ('t12');
3643            }
3644          } else {
3645            !!!cp ('t13');
3646          }
3647          
3648          ## Go to the "before html" insertion mode.
3649          !!!next-token;
3650          return;
3651        } elsif ({
3652                  START_TAG_TOKEN, 1,
3653                  END_TAG_TOKEN, 1,
3654                  END_OF_FILE_TOKEN, 1,
3655                 }->{$token->{type}}) {
3656          !!!cp ('t14');
3657          !!!parse-error (type => 'no DOCTYPE', token => $token);
3658          $self->{document}->manakai_compat_mode ('quirks');
3659          ## Go to the "before html" insertion mode.
3660          ## reprocess
3661          !!!ack-later;
3662          return;
3663        } elsif ($token->{type} == CHARACTER_TOKEN) {
3664          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3665            ## Ignore the token
3666    
3667            unless (length $token->{data}) {
3668              !!!cp ('t15');
3669              ## Stay in the insertion mode.
3670              !!!next-token;
3671              redo INITIAL;
3672            } else {
3673              !!!cp ('t16');
3674            }
3675          } else {
3676            !!!cp ('t17');
3677          }
3678    
3679          !!!parse-error (type => 'no DOCTYPE', token => $token);
3680          $self->{document}->manakai_compat_mode ('quirks');
3681          ## Go to the "before html" insertion mode.
3682          ## reprocess
3683          return;
3684        } elsif ($token->{type} == COMMENT_TOKEN) {
3685          !!!cp ('t18');
3686          my $comment = $self->{document}->create_comment ($token->{data});
3687          $self->{document}->append_child ($comment);
3688          
3689          ## Stay in the insertion mode.
3690          !!!next-token;
3691          redo INITIAL;
3692        } else {
3693          die "$0: $token->{type}: Unknown token type";
3694        }
3695      } # INITIAL
3696    
3697      die "$0: _tree_construction_initial: This should be never reached";
3698    } # _tree_construction_initial
3699    
3700    sub _tree_construction_root_element ($) {
3701      my $self = shift;
3702    
3703      ## NOTE: "before html" insertion mode.
3704      
3705      B: {
3706          if ($token->{type} == DOCTYPE_TOKEN) {
3707            !!!cp ('t19');
3708            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3709            ## Ignore the token
3710            ## Stay in the insertion mode.
3711            !!!next-token;
3712            redo B;
3713          } elsif ($token->{type} == COMMENT_TOKEN) {
3714            !!!cp ('t20');
3715            my $comment = $self->{document}->create_comment ($token->{data});
3716            $self->{document}->append_child ($comment);
3717            ## Stay in the insertion mode.
3718            !!!next-token;
3719            redo B;
3720          } elsif ($token->{type} == CHARACTER_TOKEN) {
3721            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3722              ## Ignore the token.
3723    
3724              unless (length $token->{data}) {
3725                !!!cp ('t21');
3726                ## Stay in the insertion mode.
3727                !!!next-token;
3728                redo B;
3729              } else {
3730                !!!cp ('t22');
3731              }
3732            } else {
3733              !!!cp ('t23');
3734            }
3735    
3736            $self->{application_cache_selection}->(undef);
3737    
3738            #
3739          } elsif ($token->{type} == START_TAG_TOKEN) {
3740            if ($token->{tag_name} eq 'html') {
3741              my $root_element;
3742              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3743              $self->{document}->append_child ($root_element);
3744              push @{$self->{open_elements}},
3745                  [$root_element, $el_category->{html}];
3746    
3747              if ($token->{attributes}->{manifest}) {
3748                !!!cp ('t24');
3749                $self->{application_cache_selection}
3750                    ->($token->{attributes}->{manifest}->{value});
3751                ## ISSUE: Spec is unclear on relative references.
3752                ## According to Hixie (#whatwg 2008-03-19), it should be
3753                ## resolved against the base URI of the document in HTML
3754                ## or xml:base of the element in XHTML.
3755              } else {
3756                !!!cp ('t25');
3757                $self->{application_cache_selection}->(undef);
3758              }
3759    
3760              !!!nack ('t25c');
3761    
3762              !!!next-token;
3763              return; ## Go to the "before head" insertion mode.
3764            } else {
3765              !!!cp ('t25.1');
3766              #
3767            }
3768          } elsif ({
3769                    END_TAG_TOKEN, 1,
3770                    END_OF_FILE_TOKEN, 1,
3771                   }->{$token->{type}}) {
3772            !!!cp ('t26');
3773            #
3774          } else {
3775            die "$0: $token->{type}: Unknown token type";
3776          }
3777    
3778        my $root_element;
3779        !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3780        $self->{document}->append_child ($root_element);
3781        push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3782    
3783        $self->{application_cache_selection}->(undef);
3784    
3785        ## NOTE: Reprocess the token.
3786        !!!ack-later;
3787        return; ## Go to the "before head" insertion mode.
3788    
3789        ## ISSUE: There is an issue in the spec
3790      } # B
3791    
3792      die "$0: _tree_construction_root_element: This should never be reached";
3793    } # _tree_construction_root_element
3794    
3795    sub _reset_insertion_mode ($) {
3796      my $self = shift;
3797    
3798        ## Step 1
3799        my $last;
3800        
3801        ## Step 2
3802        my $i = -1;
3803        my $node = $self->{open_elements}->[$i];
3804        
3805        ## Step 3
3806        S3: {
3807          if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3808            $last = 1;
3809            if (defined $self->{inner_html_node}) {
3810              !!!cp ('t28');
3811              $node = $self->{inner_html_node};
3812            } else {
3813              die "_reset_insertion_mode: t27";
3814            }
3815          }
3816          
3817          ## Step 4..14
3818          my $new_mode;
3819          if ($node->[1] & FOREIGN_EL) {
3820            !!!cp ('t28.1');
3821            ## NOTE: Strictly spaking, the line below only applies to MathML and
3822            ## SVG elements.  Currently the HTML syntax supports only MathML and
3823            ## SVG elements as foreigners.
3824            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3825          } elsif ($node->[1] & TABLE_CELL_EL) {
3826            if ($last) {
3827              !!!cp ('t28.2');
3828              #
3829            } else {
3830              !!!cp ('t28.3');
3831              $new_mode = IN_CELL_IM;
3832            }
3833          } else {
3834            !!!cp ('t28.4');
3835            $new_mode = {
3836                          select => IN_SELECT_IM,
3837                          ## NOTE: |option| and |optgroup| do not set
3838                          ## insertion mode to "in select" by themselves.
3839                          tr => IN_ROW_IM,
3840                          tbody => IN_TABLE_BODY_IM,
3841                          thead => IN_TABLE_BODY_IM,
3842                          tfoot => IN_TABLE_BODY_IM,
3843                          caption => IN_CAPTION_IM,
3844                          colgroup => IN_COLUMN_GROUP_IM,
3845                          table => IN_TABLE_IM,
3846                          head => IN_BODY_IM, # not in head!
3847                          body => IN_BODY_IM,
3848                          frameset => IN_FRAMESET_IM,
3849                         }->{$node->[0]->manakai_local_name};
3850          }
3851          $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3852          
3853          ## Step 15
3854          if ($node->[1] & HTML_EL) {
3855            unless (defined $self->{head_element}) {
3856              !!!cp ('t29');
3857              $self->{insertion_mode} = BEFORE_HEAD_IM;
3858            } else {
3859              ## ISSUE: Can this state be reached?
3860              !!!cp ('t30');
3861              $self->{insertion_mode} = AFTER_HEAD_IM;
3862            }
3863            return;
3864          } else {
3865            !!!cp ('t31');
3866          }
3867          
3868          ## Step 16
3869          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3870          
3871          ## Step 17
3872          $i--;
3873          $node = $self->{open_elements}->[$i];
3874          
3875          ## Step 18
3876          redo S3;
3877        } # S3
3878    
3879      die "$0: _reset_insertion_mode: This line should never be reached";
3880    } # _reset_insertion_mode
3881    
3882    sub _tree_construction_main ($) {
3883      my $self = shift;
3884    
   my $open_elements = [];  
3885    my $active_formatting_elements = [];    my $active_formatting_elements = [];
   my $head_element;  
   my $form_element;  
   my $insertion_mode = 'before head';  
3886    
3887    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
3888      my $insert = shift;      my $insert = shift;
# Line 1621  sub _construct_tree ($) { Line 3896  sub _construct_tree ($) {
3896    
3897      ## Step 2      ## Step 2
3898      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3899      for (@$open_elements) {      for (@{$self->{open_elements}}) {
3900        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3901            !!!cp ('t32');
3902          return;          return;
3903        }        }
3904      }      }
# Line 1637  sub _construct_tree ($) { Line 3913  sub _construct_tree ($) {
3913    
3914        ## Step 6        ## Step 6
3915        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3916            !!!cp ('t33_1');
3917          #          #
3918        } else {        } else {
3919          my $in_open_elements;          my $in_open_elements;
3920          OE: for (@$open_elements) {          OE: for (@{$self->{open_elements}}) {
3921            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3922                !!!cp ('t33');
3923              $in_open_elements = 1;              $in_open_elements = 1;
3924              last OE;              last OE;
3925            }            }
3926          }          }
3927          if ($in_open_elements) {          if ($in_open_elements) {
3928              !!!cp ('t34');
3929            #            #
3930          } else {          } else {
3931              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3932              !!!cp ('t35');
3933            redo S4;            redo S4;
3934          }          }
3935        }        }
# Line 1664  sub _construct_tree ($) { Line 3945  sub _construct_tree ($) {
3945            
3946        ## Step 9        ## Step 9
3947        $insert->($clone->[0]);        $insert->($clone->[0]);
3948        push @$open_elements, $clone;        push @{$self->{open_elements}}, $clone;
3949                
3950        ## Step 10        ## Step 10
3951        $active_formatting_elements->[$i] = $open_elements->[-1];        $active_formatting_elements->[$i] = $self->{open_elements}->[-1];
3952    
3953        ## Step 11        ## Step 11
3954        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3955            !!!cp ('t36');
3956          ## Step 7'          ## Step 7'
3957          $i++;          $i++;
3958          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3959                    
3960          redo S7;          redo S7;
3961        }        }
3962    
3963          !!!cp ('t37');
3964      } # S7      } # S7
3965    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3966    
3967    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3968      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3969        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3970            !!!cp ('t38');
3971          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3972          return;          return;
3973        }        }
3974      }      }
3975    
3976        !!!cp ('t39');
3977    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3978    
3979    my $reset_insertion_mode = sub {    my $insert;
3980    
3981      my $parse_rcdata = sub ($) {
3982        my ($content_model_flag) = @_;
3983    
3984      ## Step 1      ## Step 1
3985      my $last;      my $start_tag_name = $token->{tag_name};
3986            my $el;
3987        !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3988    
3989      ## Step 2      ## Step 2
3990      my $i = -1;      $insert->($el);
3991      my $node = $open_elements->[$i];  
       
3992      ## Step 3      ## Step 3
3993      S3: {      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3994        $last = 1 if $open_elements->[0]->[0] eq $node->[0];      delete $self->{escape}; # MUST
       ## TODO: the element whose inner_html is set is neither td nor th, then $node = the element  
       
       ## 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]};  
       $insertion_mode = $new_mode and return if defined $new_mode;  
         
       ## Step 14  
       if ($node->[1] eq 'html') {  
         unless (defined $head_element) {  
           $insertion_mode = 'before head';  
         } else {  
           $insertion_mode = 'after head';  
         }  
         return;  
       }  
         
       ## Step 15  
       $insertion_mode = 'in body' and return if $last;  
         
       ## Step 16  
       $i--;  
       $node = $open_elements->[$i];  
         
       ## Step 17  
       redo S3;  
     } # S3  
   }; # $reset_insertion_mode  
3995    
3996    my $style_start_tag = sub {      ## Step 4
     my $style_el; !!!create-element ($style_el, 'style');  
     ## $insertion_mode eq 'in head' and ... (always true)  
     (($insertion_mode eq 'in head' and defined $head_element)  
      ? $head_element : $open_elements->[-1]->[0])  
       ->append_child ($style_el);  
     $self->{content_model_flag} = 'CDATA';  
                 
3997      my $text = '';      my $text = '';
3998        !!!nack ('t40.1');
3999      !!!next-token;      !!!next-token;
4000      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
4001          !!!cp ('t40');
4002        $text .= $token->{data};        $text .= $token->{data};
4003        !!!next-token;        !!!next-token;
4004      } # stop if non-character token or tokenizer stops tokenising      }
4005    
4006        ## Step 5
4007      if (length $text) {      if (length $text) {
4008        $style_el->manakai_append_text ($text);        !!!cp ('t41');
4009          my $text = $self->{document}->create_text_node ($text);
4010          $el->append_child ($text);
4011      }      }
4012        
4013      $self->{content_model_flag} = 'PCDATA';      ## Step 6
4014                      $self->{content_model} = PCDATA_CONTENT_MODEL;
4015      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {  
4016        ## Step 7
4017        if ($token->{type} == END_TAG_TOKEN and
4018            $token->{tag_name} eq $start_tag_name) {
4019          !!!cp ('t42');
4020        ## Ignore the token        ## Ignore the token
4021      } else {      } else {
4022        !!!parse-error;        ## NOTE: An end-of-file token.
4023        ## ISSUE: And ignore?        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4024            !!!cp ('t43');
4025            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4026          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4027            !!!cp ('t44');
4028            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4029          } else {
4030            die "$0: $content_model_flag in parse_rcdata";
4031          }
4032      }      }
4033      !!!next-token;      !!!next-token;
4034    }; # $style_start_tag    }; # $parse_rcdata
4035    
4036    my $script_start_tag = sub {    my $script_start_tag = sub () {
4037      my $script_el;      my $script_el;
4038      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4039      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4040    
4041      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
4042        delete $self->{escape}; # MUST
4043            
4044      my $text = '';      my $text = '';
4045        !!!nack ('t45.1');
4046      !!!next-token;      !!!next-token;
4047      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
4048          !!!cp ('t45');
4049        $text .= $token->{data};        $text .= $token->{data};
4050        !!!next-token;        !!!next-token;
4051      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4052      if (length $text) {      if (length $text) {
4053          !!!cp ('t46');
4054        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4055      }      }
4056                                
4057      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
4058    
4059      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
4060          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4061          !!!cp ('t47');
4062        ## Ignore the token        ## Ignore the token
4063      } else {      } else {
4064        !!!parse-error;        !!!cp ('t48');
4065          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4066        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4067        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4068      }      }
4069            
4070      ## TODO: inner_html mode then mark as "already executed" and skip      if (defined $self->{inner_html_node}) {
4071      if (1) {        !!!cp ('t49');
4072          ## TODO: mark as "already executed"
4073        } else {
4074          !!!cp ('t50');
4075        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4076        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4077          
4078        (($insertion_mode eq 'in head' and defined $head_element)        $insert->($script_el);
        ? $head_element : $open_elements->[-1]->[0])->append_child ($script_el);  
4079                
4080        ## TODO: insertion point = $old_insertion_point (might be "undefined")        ## TODO: insertion point = $old_insertion_point (might be "undefined")
4081                
# Line 1815  sub _construct_tree ($) { Line 4085  sub _construct_tree ($) {
4085      !!!next-token;      !!!next-token;
4086    }; # $script_start_tag    }; # $script_start_tag
4087    
4088      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4089      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4090      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4091    
4092    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4093      my $tag_name = shift;      my $end_tag_token = shift;
4094        my $tag_name = $end_tag_token->{tag_name};
4095    
4096        ## NOTE: The adoption agency algorithm (AAA).
4097    
4098      FET: {      FET: {
4099        ## Step 1        ## Step 1
4100        my $formatting_element;        my $formatting_element;
4101        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4102        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4103          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4104              !!!cp ('t52');
4105              last AFE;
4106            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4107                         eq $tag_name) {
4108              !!!cp ('t51');
4109            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4110            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4111            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4112          }          }
4113        } # AFE        } # AFE
4114        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4115          !!!parse-error;          !!!cp ('t53');
4116            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4117          ## Ignore the token          ## Ignore the token
4118          !!!next-token;          !!!next-token;
4119          return;          return;
# Line 1840  sub _construct_tree ($) { Line 4121  sub _construct_tree ($) {
4121        ## has an element in scope        ## has an element in scope
4122        my $in_scope = 1;        my $in_scope = 1;
4123        my $formatting_element_i_in_open;          my $formatting_element_i_in_open;  
4124        INSCOPE: for (reverse 0..$#$open_elements) {        INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
4125          my $node = $open_elements->[$_];          my $node = $self->{open_elements}->[$_];
4126          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4127            if ($in_scope) {            if ($in_scope) {
4128                !!!cp ('t54');
4129              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4130              last INSCOPE;              last INSCOPE;
4131            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4132              !!!parse-error;              !!!cp ('t55');
4133                !!!parse-error (type => 'unmatched end tag',
4134                                text => $token->{tag_name},
4135                                token => $end_tag_token);
4136              ## Ignore the token              ## Ignore the token
4137              !!!next-token;              !!!next-token;
4138              return;              return;
4139            }            }
4140          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4141                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4142            $in_scope = 0;            $in_scope = 0;
4143          }          }
4144        } # INSCOPE        } # INSCOPE
4145        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4146          !!!parse-error;          !!!cp ('t57');
4147            !!!parse-error (type => 'unmatched end tag',
4148                            text => $token->{tag_name},
4149                            token => $end_tag_token);
4150          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4151          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4152          return;          return;
4153        }        }
4154        if (not $open_elements->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4155          !!!parse-error;          !!!cp ('t58');
4156            !!!parse-error (type => 'not closed',
4157                            text => $self->{open_elements}->[-1]->[0]
4158                                ->manakai_local_name,
4159                            token => $end_tag_token);
4160        }        }
4161                
4162        ## Step 2        ## Step 2
4163        my $furthest_block;        my $furthest_block;
4164        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4165        OE: for (reverse 0..$#$open_elements) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4166          my $node = $open_elements->[$_];          my $node = $self->{open_elements}->[$_];
4167          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4168              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4169              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4170               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4171              !!!cp ('t59');
4172            $furthest_block = $node;            $furthest_block = $node;
4173            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4174          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4175              !!!cp ('t60');
4176            last OE;            last OE;
4177          }          }
4178        } # OE        } # OE
4179                
4180        ## Step 3        ## Step 3
4181        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4182          splice @$open_elements, $formatting_element_i_in_open;          !!!cp ('t61');
4183            splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4184          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4185          !!!next-token;          !!!next-token;
4186          return;          return;
4187        }        }
4188                
4189        ## Step 4        ## Step 4
4190        my $common_ancestor_node = $open_elements->[$formatting_element_i_in_open - 1];        my $common_ancestor_node = $self->{open_elements}->[$formatting_element_i_in_open - 1];
4191                
4192        ## Step 5        ## Step 5
4193        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4194        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4195            !!!cp ('t62');
4196          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4197        }        }
4198                
# Line 1914  sub _construct_tree ($) { Line 4208  sub _construct_tree ($) {
4208        S7: {        S7: {
4209          ## Step 1          ## Step 1
4210          $node_i_in_open--;          $node_i_in_open--;
4211          $node = $open_elements->[$node_i_in_open];          $node = $self->{open_elements}->[$node_i_in_open];
4212                    
4213          ## Step 2          ## Step 2
4214          my $node_i_in_active;          my $node_i_in_active;
4215          S7S2: {          S7S2: {
4216            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4217              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4218                  !!!cp ('t63');
4219                $node_i_in_active = $_;                $node_i_in_active = $_;
4220                last S7S2;                last S7S2;
4221              }              }
4222            }            }
4223            splice @$open_elements, $node_i_in_open, 1;            splice @{$self->{open_elements}}, $node_i_in_open, 1;
4224            redo S7;            redo S7;
4225          } # S7S2          } # S7S2
4226                    
# Line 1934  sub _construct_tree ($) { Line 4229  sub _construct_tree ($) {
4229                    
4230          ## Step 4          ## Step 4
4231          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4232              !!!cp ('t64');
4233            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4234          }          }
4235                    
4236          ## Step 5          ## Step 5
4237          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4238              !!!cp ('t65');
4239            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4240            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4241            $open_elements->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
4242            $node = $clone;            $node = $clone;
4243          }          }
4244                    
# Line 1956  sub _construct_tree ($) { Line 4253  sub _construct_tree ($) {
4253        } # S7          } # S7  
4254                
4255        ## Step 8        ## Step 8
4256        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4257            my $foster_parent_element;
4258            my $next_sibling;
4259            OE: for (reverse 0..$#{$self->{open_elements}}) {
4260              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4261                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4262                                 if (defined $parent and $parent->node_type == 1) {
4263                                   !!!cp ('t65.1');
4264                                   $foster_parent_element = $parent;
4265                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4266                                 } else {
4267                                   !!!cp ('t65.2');
4268                                   $foster_parent_element
4269                                     = $self->{open_elements}->[$_ - 1]->[0];
4270                                 }
4271                                 last OE;
4272                               }
4273                             } # OE
4274                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4275                               unless defined $foster_parent_element;
4276            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4277            $open_tables->[-1]->[1] = 1; # tainted
4278          } else {
4279            !!!cp ('t65.3');
4280            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4281          }
4282                
4283        ## Step 9        ## Step 9
4284        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 1973  sub _construct_tree ($) { Line 4295  sub _construct_tree ($) {
4295        my $i;        my $i;
4296        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4297          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4298              !!!cp ('t66');
4299            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4300            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4301          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4302              !!!cp ('t67');
4303            $i = $_;            $i = $_;
4304          }          }
4305        } # AFE        } # AFE
# Line 1983  sub _construct_tree ($) { Line 4307  sub _construct_tree ($) {
4307                
4308        ## Step 13        ## Step 13
4309        undef $i;        undef $i;
4310        OE: for (reverse 0..$#$open_elements) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4311          if ($open_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4312            splice @$open_elements, $_, 1;            !!!cp ('t68');
4313              splice @{$self->{open_elements}}, $_, 1;
4314            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4315          } elsif ($open_elements->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4316              !!!cp ('t69');
4317            $i = $_;            $i = $_;
4318          }          }
4319        } # OE        } # OE
4320        splice @$open_elements, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 1, $clone;
4321                
4322        ## Step 14        ## Step 14
4323        redo FET;        redo FET;
4324      } # FET      } # FET
4325    }; # $formatting_end_tag    }; # $formatting_end_tag
4326    
4327    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4328      $open_elements->[-1]->[0]->append_child (shift);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4329    }; # $insert_to_current    }; # $insert_to_current
4330    
4331    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4332                         my $child = shift;      my $child = shift;
4333                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4334                              table => 1, tbody => 1, tfoot => 1,        # MUST
4335                              thead => 1, tr => 1,        my $foster_parent_element;
4336                             }->{$open_elements->[-1]->[1]}) {        my $next_sibling;
4337                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4338                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4339                           my $next_sibling;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
                          OE: for (reverse 0..$#$open_elements) {  
                            if ($open_elements->[$_]->[1] eq 'table') {  
                              my $parent = $open_elements->[$_]->[0]->parent_node;  
4340                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4341                                   !!!cp ('t70');
4342                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4343                                 $next_sibling = $open_elements->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4344                               } else {                               } else {
4345                                   !!!cp ('t71');
4346                                 $foster_parent_element                                 $foster_parent_element
4347                                   = $open_elements->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4348                               }                               }
4349                               last OE;                               last OE;
4350                             }                             }
4351                           } # OE                           } # OE
4352                           $foster_parent_element = $open_elements->[0]->[0]                           $foster_parent_element = $self->{open_elements}->[0]->[0]
4353                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4354                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4355                             ($child, $next_sibling);                             ($child, $next_sibling);
4356                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4357                           $open_elements->[-1]->[0]->append_child ($child);      } else {
4358                         }        !!!cp ('t72');
4359          $self->{open_elements}->[-1]->[0]->append_child ($child);
4360        }
4361    }; # $insert_to_foster    }; # $insert_to_foster
4362    
4363    my $in_body = sub {    B: while (1) {
4364      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
4365      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
4366        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4367          $script_start_tag->();        ## Ignore the token
4368          return;        ## Stay in the phase
4369        } elsif ($token->{tag_name} eq 'style') {        !!!next-token;
4370          $style_start_tag->();        next B;
4371          return;      } elsif ($token->{type} == START_TAG_TOKEN and
4372        } elsif ({               $token->{tag_name} eq 'html') {
4373                  base => 1, link => 1, meta => 1,        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4374                 }->{$token->{tag_name}}) {          !!!cp ('t79');
4375          !!!parse-error ($token->{tag_name}.' in body');          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          ## NOTE: This is an "as if in head" code clone          $self->{insertion_mode} = AFTER_BODY_IM;
4377          my $el;        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4378          !!!create-element ($el, $token->{tag_name}, $token->{attributes});          !!!cp ('t80');
4379          if (defined $head_element) {          !!!parse-error (type => 'after html', text => 'html', token => $token);
4380            $head_element->append_child ($el);          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4381          } else {        } else {
4382            $insert->($el);          !!!cp ('t81');
4383          }        }
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'title') {  
         !!!parse-error ('title in body');  
         ## NOTE: There is an "as if in head" code clone  
         my $title_el;  
         !!!create-element ($title_el, 'title', $token->{attributes});  
         (defined $head_element ? $head_element : $open_elements->[-1]->[0])  
           ->append_child ($title_el);  
         $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;  
           ## ISSUE: And ignore?  
         }  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error;  
                 
         if (@$open_elements == 1 or  
             $open_elements->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $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 @$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 $form_element) {  
           !!!parse-error;  
           ## Ignore the token  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @$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});  
           $form_element = $open_elements->[-1]->[0];  
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'li') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @$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 = $open_elements->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             splice @$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 = $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 @$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 = $open_elements->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             splice @$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 = $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 @$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..$#$open_elements) {  
           my $node = $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..$#$open_elements) {  
           my $node = $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;  
           splice @$open_elements, $i;  
         }  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'a') {  
         AFE: for my $i (reverse 0..$#$active_formatting_elements) {  
           my $node = $active_formatting_elements->[$i];  
           if ($node->[1] eq 'a') {  
             !!!parse-error ('a in a');  
               
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'a'};  
             $formatting_end_tag->($token->{tag_name});  
               
             AFE2: for (reverse 0..$#$active_formatting_elements) {  
               if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$active_formatting_elements, $_, 1;  
                 last AFE2;  
               }  
             } # AFE2  
             OE: for (reverse 0..$#$open_elements) {  
               if ($open_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$open_elements, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
   
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $open_elements->[-1];  
   
         !!!next-token;  
         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, $open_elements->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#$open_elements) {  
           my $node = $open_elements->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error;  
             !!!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', ''];  
4384    
4385          !!!next-token;        !!!cp ('t82');
4386          return;        !!!parse-error (type => 'not first start tag', token => $token);
4387        } elsif ($token->{tag_name} eq 'marquee' or        my $top_el = $self->{open_elements}->[0]->[0];
4388                 $token->{tag_name} eq 'object') {        for my $attr_name (keys %{$token->{attributes}}) {
4389          $reconstruct_active_formatting_elements->($insert_to_current);          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4390                      !!!cp ('t84');
4391          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            $top_el->set_attribute_ns
4392          push @$active_formatting_elements, ['#marker', ''];              (undef, [undef, $attr_name],
4393                         $token->{attributes}->{$attr_name}->{value});
         !!!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 @$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});  
             
         $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;  
           $token->{tag_name} = 'img';  
4394          }          }
4395                  }
4396          $reconstruct_active_formatting_elements->($insert_to_current);        !!!nack ('t84.1');
4397                  !!!next-token;
4398          !!!insert-element-t ($token->{tag_name}, $token->{attributes});        next B;
4399          pop @$open_elements;      } elsif ($token->{type} == COMMENT_TOKEN) {
4400                  my $comment = $self->{document}->create_comment ($token->{data});
4401          !!!next-token;        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4402          return;          !!!cp ('t85');
4403        } elsif ($token->{tag_name} eq 'hr') {          $self->{document}->append_child ($comment);
4404          ## has a p element in scope        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4405          INSCOPE: for (reverse @$open_elements) {          !!!cp ('t86');
4406            if ($_->[1] eq 'p') {          $self->{open_elements}->[0]->[0]->append_child ($comment);
             !!!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 @$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 $form_element if defined  
         pop @$open_elements;  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error;  
           
         if (defined $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,  
                 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: 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 {  
           !!!parse-error;  
           ## ISSUE: And ignore?  
         }  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $insertion_mode = 'in select';  
         !!!next-token;  
         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,  
                }->{$token->{tag_name}}) {  
         !!!parse-error;  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
4407        } else {        } else {
4408          $reconstruct_active_formatting_elements->($insert_to_current);          !!!cp ('t87');
4409                    $self->{open_elements}->[-1]->[0]->append_child ($comment);
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
4410        }        }
4411      } elsif ($token->{type} eq 'end tag') {        !!!next-token;
4412        if ($token->{tag_name} eq 'body') {        next B;
4413          if (@$open_elements > 1 and $open_elements->[1]->[1] eq 'body') {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4414            ## ISSUE: There is an issue in the spec.        if ($token->{type} == CHARACTER_TOKEN) {
4415            if ($open_elements->[-1]->[1] ne 'body') {          !!!cp ('t87.1');
4416              !!!parse-error;          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
           }  
           $insertion_mode = 'after body';  
           !!!next-token;  
           return;  
         } else {  
           !!!parse-error;  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'html') {  
         if (@$open_elements > 1 and $open_elements->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($open_elements->[-1]->[1] ne 'body') {  
             !!!parse-error;  
           }  
           $insertion_mode = 'after body';  
           ## reprocess  
           return;  
         } else {  
           !!!parse-error;  
           ## 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..$#$open_elements) {  
           my $node = $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,  
                 }->{$open_elements->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $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;  
           }  
         } # INSCOPE  
           
         if ($open_elements->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error;  
         }  
           
         splice @$open_elements, $i if defined $i;  
         undef $form_element if $token->{tag_name} eq 'form';  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
4417          !!!next-token;          !!!next-token;
4418          return;          next B;
4419        } elsif ({        } elsif ($token->{type} == START_TAG_TOKEN) {
4420                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4421                 }->{$token->{tag_name}}) {               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4422          ## has an element in scope              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4423          my $i;              ($token->{tag_name} eq 'svg' and
4424          INSCOPE: for (reverse 0..$#$open_elements) {               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4425            my $node = $open_elements->[$_];            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4426            if ({            !!!cp ('t87.2');
4427                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,            #
4428                }->{$node->[1]}) {          } elsif ({
4429              ## generate implied end tags                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4430              if ({                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4431                   dd => 1, dt => 1, li => 1, p => 1,                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4432                   td => 1, th => 1, tr => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4433                  }->{$open_elements->[-1]->[1]}) {                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4434                !!!back-token;                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4435                $token = {type => 'end tag',                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4436                          tag_name => $open_elements->[-1]->[1]}; # MUST                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4437                return;                   }->{$token->{tag_name}}) {
4438              }            !!!cp ('t87.2');
4439              $i = $_;            !!!parse-error (type => 'not closed',
4440              last INSCOPE;                            text => $self->{open_elements}->[-1]->[0]
4441            } elsif ({                                ->manakai_local_name,
4442                      table => 1, caption => 1, td => 1, th => 1,                            token => $token);
4443                      button => 1, marquee => 1, object => 1, html => 1,  
4444                     }->{$node->[1]}) {            pop @{$self->{open_elements}}
4445              last INSCOPE;                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4446    
4447              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4448              ## Reprocess.
4449              next B;
4450            } else {
4451              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4452              my $tag_name = $token->{tag_name};
4453              if ($nsuri eq $SVG_NS) {
4454                $tag_name = {
4455                   altglyph => 'altGlyph',
4456                   altglyphdef => 'altGlyphDef',
4457                   altglyphitem => 'altGlyphItem',
4458                   animatecolor => 'animateColor',
4459                   animatemotion => 'animateMotion',
4460                   animatetransform => 'animateTransform',
4461                   clippath => 'clipPath',
4462                   feblend => 'feBlend',
4463                   fecolormatrix => 'feColorMatrix',
4464                   fecomponenttransfer => 'feComponentTransfer',
4465                   fecomposite => 'feComposite',
4466                   feconvolvematrix => 'feConvolveMatrix',
4467                   fediffuselighting => 'feDiffuseLighting',
4468                   fedisplacementmap => 'feDisplacementMap',
4469                   fedistantlight => 'feDistantLight',
4470                   feflood => 'feFlood',
4471                   fefunca => 'feFuncA',
4472                   fefuncb => 'feFuncB',
4473                   fefuncg => 'feFuncG',
4474                   fefuncr => 'feFuncR',
4475                   fegaussianblur => 'feGaussianBlur',
4476                   feimage => 'feImage',
4477                   femerge => 'feMerge',
4478                   femergenode => 'feMergeNode',
4479                   femorphology => 'feMorphology',
4480                   feoffset => 'feOffset',
4481                   fepointlight => 'fePointLight',
4482                   fespecularlighting => 'feSpecularLighting',
4483                   fespotlight => 'feSpotLight',
4484                   fetile => 'feTile',
4485                   feturbulence => 'feTurbulence',
4486                   foreignobject => 'foreignObject',
4487                   glyphref => 'glyphRef',
4488                   lineargradient => 'linearGradient',
4489                   radialgradient => 'radialGradient',
4490                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4491                   textpath => 'textPath',  
4492                }->{$tag_name} || $tag_name;
4493              }
4494    
4495              ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4496    
4497              ## "adjust foreign attributes" - done in insert-element-f
4498    
4499              !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4500    
4501              if ($self->{self_closing}) {
4502                pop @{$self->{open_elements}};
4503                !!!ack ('t87.3');
4504              } else {
4505                !!!cp ('t87.4');
4506            }            }
4507          } # INSCOPE  
4508                      !!!next-token;
4509          if ($open_elements->[-1]->[1] ne $token->{tag_name}) {            next B;
           !!!parse-error;  
4510          }          }
4511                  } elsif ($token->{type} == END_TAG_TOKEN) {
4512          splice @$open_elements, $i if defined $i;          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4513          !!!next-token;          !!!cp ('t87.5');
4514          return;          #
4515        } elsif ({        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4516                  a => 1,          !!!cp ('t87.6');
4517                  b => 1, big => 1, em => 1, font => 1, i => 1,          !!!parse-error (type => 'not closed',
4518                  nobr => 1, s => 1, small => 1, strile => 1,                          text => $self->{open_elements}->[-1]->[0]
4519                  strong => 1, tt => 1, u => 1,                              ->manakai_local_name,
4520                 }->{$token->{tag_name}}) {                          token => $token);
4521          $formatting_end_tag->($token->{tag_name});  
4522          return;          pop @{$self->{open_elements}}
4523        } elsif ({              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4524                  caption => 1, col => 1, colgroup => 1, frame => 1,  
4525                  frameset => 1, head => 1, option => 1, optgroup => 1,          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4526                  tbody => 1, td => 1, tfoot => 1, th => 1,          ## Reprocess.
4527                  thead => 1, tr => 1,          next B;
                 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;  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
4528        } else {        } else {
4529          ## Step 1          die "$0: $token->{type}: Unknown token type";        
         my $node_i = -1;  
         my $node = $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,  
                 }->{$open_elements->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $open_elements->[-1]->[1]}; # MUST  
               return;  
             }  
           
             ## Step 2  
             if ($token->{tag_name} ne $open_elements->[-1]->[1]) {  
               !!!parse-error;  
             }  
               
             ## Step 3  
             splice @$open_elements, $node_i;  
             last S2;  
           } else {  
             ## Step 3  
             if (not $formatting_category->{$node->[1]} and  
                 #not $phrasing_category->{$node->[1]} and  
                 ($special_category->{$node->[1]} or  
                  $scoping_category->{$node->[1]})) {  
               !!!parse-error;  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
           }  
             
           ## Step 4  
           $node_i--;  
           $node = $open_elements->[$node_i];  
             
           ## Step 5;  
           redo S2;  
         } # S2  
4530        }        }
4531      }      }
   }; # $in_body  
4532    
4533    B: {      if ($self->{insertion_mode} & HEAD_IMS) {
4534      if ($phase eq 'initial') {        if ($token->{type} == CHARACTER_TOKEN) {
4535        if ($token->{type} eq 'DOCTYPE') {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4536          if ($token->{error}) {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4537            ## ISSUE: Spec currently left this case undefined.              !!!cp ('t88.2');
4538            !!!parse-error ('bogus DOCTYPE');              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4539          }              #
4540          my $doctype = $self->{document}->create_document_type_definition            } else {
4541            ($token->{name});              !!!cp ('t88.1');
4542          $self->{document}->append_child ($doctype);              ## Ignore the token.
4543          $phase = 'root element';              #
         !!!next-token;  
         redo B;  
       } elsif ({  
                 comment => 1,  
                 'start tag' => 1,  
                 'end tag' => 1,  
                 'end-of-file' => 1,  
                }->{$token->{type}}) {  
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error ('missing DOCTYPE');  
         $phase = 'root element';  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'character') {  
         if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
           $self->{document}->manakai_append_text ($1);  
           ## ISSUE: DOM3 Core does not allow Document > Text  
           unless (length $token->{data}) {  
             ## Stay in the phase  
             !!!next-token;  
             redo B;  
4544            }            }
         }  
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error ('missing DOCTYPE');  
         $phase = 'root element';  
         ## reprocess  
         redo B;  
       } else {  
         die "$0: $token->{type}: Unknown token";  
       }  
     } elsif ($phase eq 'root element') {  
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error;  
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'comment') {  
         my $comment = $self->{document}->create_comment ($token->{data});  
         $self->{document}->append_child ($comment);  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'character') {  
         if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
           $self->{document}->manakai_append_text ($1);  
           ## ISSUE: DOM3 Core does not allow Document > Text  
4545            unless (length $token->{data}) {            unless (length $token->{data}) {
4546              ## Stay in the phase              !!!cp ('t88');
4547              !!!next-token;              !!!next-token;
4548              redo B;              next B;
4549            }            }
4550    ## TODO: set $token->{column} appropriately
4551          }          }
4552          #  
4553        } elsif ({          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4554                  'start tag' => 1,            !!!cp ('t89');
4555                  'end tag' => 1,            ## As if <head>
4556                  'end-of-file' => 1,            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4557                 }->{$token->{type}}) {            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4558          ## ISSUE: There is an issue in the spec            push @{$self->{open_elements}},
4559          #                [$self->{head_element}, $el_category->{head}];
4560        } else {  
4561          die "$0: $token->{type}: Unknown token";            ## Reprocess in the "in head" insertion mode...
4562        }            pop @{$self->{open_elements}};
4563        my $root_element; !!!create-element ($root_element, 'html');  
4564        $self->{document}->append_child ($root_element);            ## Reprocess in the "after head" insertion mode...
4565        $open_elements = [[$root_element, 'html']];          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4566        $phase = 'main';            !!!cp ('t90');
4567        ## reprocess            ## As if </noscript>
4568        redo B;            pop @{$self->{open_elements}};
4569      } elsif ($phase eq 'main') {            !!!parse-error (type => 'in noscript:#text', token => $token);
4570        if ($token->{type} eq 'DOCTYPE') {            
4571          !!!parse-error;            ## Reprocess in the "in head" insertion mode...
4572          ## Ignore the token            ## As if </head>
4573          ## Stay in the phase            pop @{$self->{open_elements}};
4574          !!!next-token;  
4575          redo B;            ## Reprocess in the "after head" insertion mode...
4576        } elsif ($token->{type} eq 'start tag' and          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4577                 $token->{tag_name} eq 'html') {            !!!cp ('t91');
4578          ## TODO: unless it is the first start tag token, parse-error            pop @{$self->{open_elements}};
4579          my $top_el = $open_elements->[0]->[0];  
4580          for my $attr_name (keys %{$token->{attributes}}) {            ## Reprocess in the "after head" insertion mode...
           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,  
             }->{$open_elements->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $open_elements->[-1]->[1]};  
           redo B;  
         }  
           
         if (@$open_elements > 2 or  
             (@$open_elements == 2 and $open_elements->[1]->[1] ne 'body')) {  
           !!!parse-error;  
4581          } else {          } else {
4582            ## TODO: inner_html parser and @$open_elements > 1 and $open_elements->[1] ne 'body', then parse-error            !!!cp ('t92');
4583          }          }
4584    
4585          ## Stop parsing          ## "after head" insertion mode
4586          last B;          ## As if <body>
4587            !!!insert-element ('body',, $token);
4588          ## ISSUE: There is an issue in the spec.          $self->{insertion_mode} = IN_BODY_IM;
4589        } else {          ## reprocess
4590          if ($insertion_mode eq 'before head') {          next B;
4591            if ($token->{type} eq 'character') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4592              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{tag_name} eq 'head') {
4593                $open_elements->[-1]->[0]->manakai_append_text ($1);            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4594                unless (length $token->{data}) {              !!!cp ('t93');
4595                  !!!next-token;              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4596                  redo B;              $self->{open_elements}->[-1]->[0]->append_child
4597                }                  ($self->{head_element});
4598              }              push @{$self->{open_elements}},
4599              ## As if <head>                  [$self->{head_element}, $el_category->{head}];
4600              !!!create-element ($head_element, 'head');              $self->{insertion_mode} = IN_HEAD_IM;
4601              $open_elements->[-1]->[0]->append_child ($head_element);              !!!nack ('t93.1');
             push @$open_elements, [$head_element, 'head'];  
             $insertion_mode = 'in head';  
             ## reprocess  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $open_elements->[-1]->[0]->append_child ($comment);  
4602              !!!next-token;              !!!next-token;
4603              redo B;              next B;
4604            } elsif ($token->{type} eq 'start tag') {            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4605              my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};              !!!cp ('t93.2');
4606              !!!create-element ($head_element, 'head', $attr);              !!!parse-error (type => 'after head', text => 'head',
4607              $open_elements->[-1]->[0]->append_child ($head_element);                              token => $token);
4608              push @$open_elements, [$head_element, 'head'];              ## Ignore the token
4609              $insertion_mode = 'in head';              !!!nack ('t93.3');
4610              if ($token->{tag_name} eq 'head') {              !!!next-token;
4611                !!!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 ($head_element, 'head');  
               $open_elements->[-1]->[0]->append_child ($head_element);  
               push @$open_elements, [$head_element, 'head'];  
               $insertion_mode = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               !!!parse-error;  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             }  
4612            } else {            } else {
4613              die "$0: $token->{type}: Unknown type";              !!!cp ('t95');
4614            }              !!!parse-error (type => 'in head:head',
4615          } elsif ($insertion_mode eq 'in head') {                              token => $token); # or in head noscript
4616            if ($token->{type} eq 'character') {              ## Ignore the token
4617              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              !!!nack ('t95.1');
               $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});  
             $open_elements->[-1]->[0]->append_child ($comment);  
4618              !!!next-token;              !!!next-token;
4619              redo B;              next B;
4620            } elsif ($token->{type} eq 'start tag') {            }
4621              if ($token->{tag_name} eq 'title') {          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4622                ## NOTE: There is an "as if in head" code clone            !!!cp ('t96');
4623                my $title_el;            ## As if <head>
4624                !!!create-element ($title_el, 'title', $token->{attributes});            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4625                (defined $head_element ? $head_element : $open_elements->[-1]->[0])            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4626                  ->append_child ($title_el);            push @{$self->{open_elements}},
4627                $self->{content_model_flag} = 'RCDATA';                [$self->{head_element}, $el_category->{head}];
4628    
4629                my $text = '';            $self->{insertion_mode} = IN_HEAD_IM;
4630                !!!next-token;            ## Reprocess in the "in head" insertion mode...
4631                while ($token->{type} eq 'character') {          } else {
4632                  $text .= $token->{data};            !!!cp ('t97');
4633                  !!!next-token;          }
4634                }  
4635                if (length $text) {              if ($token->{tag_name} eq 'base') {
4636                  $title_el->manakai_append_text ($text);                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4637                }                  !!!cp ('t98');
4638                                  ## As if </noscript>
4639                $self->{content_model_flag} = 'PCDATA';                  pop @{$self->{open_elements}};
4640                    !!!parse-error (type => 'in noscript', text => 'base',
4641                                    token => $token);
4642                                
4643                if ($token->{type} eq 'end tag' and                  $self->{insertion_mode} = IN_HEAD_IM;
4644                    $token->{tag_name} eq 'title') {                  ## Reprocess in the "in head" insertion mode...
                 ## Ignore the token  
4645                } else {                } else {
4646                  !!!parse-error;                  !!!cp ('t99');
                 ## ISSUE: And ignore?  
4647                }                }
               !!!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 $head_element ? $head_element : $open_elements->[-1]->[0])  
                 ->append_child ($el);  
4648    
4649                !!!next-token;                ## NOTE: There is a "as if in head" code clone.
4650                redo B;                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4651              } elsif ($token->{tag_name} eq 'head') {                  !!!cp ('t100');
4652                !!!parse-error;                  !!!parse-error (type => 'after head',
4653                ## Ignore the token                                  text => $token->{tag_name}, token => $token);
4654                !!!next-token;                  push @{$self->{open_elements}},
4655                redo B;                      [$self->{head_element}, $el_category->{head}];
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'head') {  
               if ($open_elements->[-1]->[1] eq 'head') {  
                 pop @$open_elements;  
4656                } else {                } else {
4657                  !!!parse-error;                  !!!cp ('t101');
4658                }                }
4659                $insertion_mode = 'after head';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4660                !!!next-token;                pop @{$self->{open_elements}};
4661                redo B;                pop @{$self->{open_elements}} # <head>
4662              } elsif ($token->{tag_name} eq 'html') {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4663                #                !!!nack ('t101.1');
             } else {  
               !!!parse-error;  
               ## Ignore the token  
4664                !!!next-token;                !!!next-token;
4665                redo B;                next B;
4666              }          } elsif ($token->{tag_name} eq 'link') {
4667              ## NOTE: There is a "as if in head" code clone.
4668              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4669                !!!cp ('t102');
4670                !!!parse-error (type => 'after head',
4671                                text => $token->{tag_name}, token => $token);
4672                push @{$self->{open_elements}},
4673                    [$self->{head_element}, $el_category->{head}];
4674            } else {            } else {
4675              #              !!!cp ('t103');
4676            }            }
4677              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4678            if ($open_elements->[-1]->[1] eq 'head') {            pop @{$self->{open_elements}};
4679              ## As if </head>            pop @{$self->{open_elements}} # <head>
4680              pop @$open_elements;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4681            }            !!!ack ('t103.1');
4682            $insertion_mode = 'after head';            !!!next-token;
4683            ## reprocess            next B;
4684            redo B;          } elsif ($token->{tag_name} eq 'command' or
4685                     $token->{tag_name} eq 'eventsource') {
4686            ## ISSUE: An issue in the spec.            if ($self->{insertion_mode} == IN_HEAD_IM) {
4687          } elsif ($insertion_mode eq 'after head') {              ## NOTE: If the insertion mode at the time of the emission
4688            if ($token->{type} eq 'character') {              ## of the token was "before head", $self->{insertion_mode}
4689              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              ## is already changed to |IN_HEAD_IM|.
4690                $open_elements->[-1]->[0]->manakai_append_text ($1);  
4691                unless (length $token->{data}) {              ## NOTE: There is a "as if in head" code clone.
4692                  !!!next-token;              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4693                  redo B;              pop @{$self->{open_elements}};
4694                }              pop @{$self->{open_elements}} # <head>
4695              }                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4696                            !!!ack ('t103.2');
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $open_elements->[-1]->[0]->append_child ($comment);  
4697              !!!next-token;              !!!next-token;
4698              redo B;              next B;
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $insertion_mode = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'frameset') {  
               !!!insert-element ('frameset', $token->{attributes});  
               $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;  
                $insertion_mode = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
4699            } else {            } else {
4700                ## NOTE: "in head noscript" or "after head" insertion mode
4701                ## - in these cases, these tags are treated as same as
4702                ## normal in-body tags.
4703                !!!cp ('t103.3');
4704              #              #
4705            }            }
4706                          } elsif ($token->{tag_name} eq 'meta') {
4707            ## As if <body>                ## NOTE: There is a "as if in head" code clone.
4708            !!!insert-element ('body');                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4709            $insertion_mode = 'in body';                  !!!cp ('t104');
4710            ## reprocess                  !!!parse-error (type => 'after head',
4711            redo B;                                  text => $token->{tag_name}, token => $token);
4712          } elsif ($insertion_mode eq 'in body') {                  push @{$self->{open_elements}},
4713            if ($token->{type} eq 'character') {                      [$self->{head_element}, $el_category->{head}];
4714              ## NOTE: There is a code clone of "character in body".                } else {
4715              $reconstruct_active_formatting_elements->($insert_to_current);                  !!!cp ('t105');
               
             $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});  
             $open_elements->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } else {  
             $in_body->($insert_to_current);  
             redo B;  
           }  
         } elsif ($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]+)//) {  
               $open_elements->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
4716                }                }
4717              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4718                  my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4719    
4720              ## As if in body, but insert into foster parent element                unless ($self->{confident}) {
4721              ## ISSUE: Spec says that "whenever a node would be inserted                  if ($token->{attributes}->{charset}) {
4722              ## into the current node" while characters might not be                    !!!cp ('t106');
4723              ## result in a new Text node.                    ## NOTE: Whether the encoding is supported or not is handled
4724              $reconstruct_active_formatting_elements->($insert_to_foster);                    ## in the {change_encoding} callback.
4725                                  $self->{change_encoding}
4726              if ({                        ->($self, $token->{attributes}->{charset}->{value},
4727                   table => 1, tbody => 1, tfoot => 1,                           $token);
4728                   thead => 1, tr => 1,                    
4729                  }->{$open_elements->[-1]->[1]}) {                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4730                # MUST                        ->set_user_data (manakai_has_reference =>
4731                my $foster_parent_element;                                             $token->{attributes}->{charset}
4732                my $next_sibling;                                                 ->{has_reference});
4733                my $prev_sibling;                  } elsif ($token->{attributes}->{content}) {
4734                OE: for (reverse 0..$#$open_elements) {                    if ($token->{attributes}->{content}->{value}
4735                  if ($open_elements->[$_]->[1] eq 'table') {                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4736                    my $parent = $open_elements->[$_]->[0]->parent_node;                            [\x09\x0A\x0C\x0D\x20]*=
4737                    if (defined $parent and $parent->node_type == 1) {                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4738                      $foster_parent_element = $parent;                            ([^"'\x09\x0A\x0C\x0D\x20]
4739                      $next_sibling = $open_elements->[$_]->[0];                             [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4740                      $prev_sibling = $next_sibling->previous_sibling;                      !!!cp ('t107');
4741                        ## NOTE: Whether the encoding is supported or not is handled
4742                        ## in the {change_encoding} callback.
4743                        $self->{change_encoding}
4744                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4745                               $token);
4746                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4747                            ->set_user_data (manakai_has_reference =>
4748                                                 $token->{attributes}->{content}
4749                                                       ->{has_reference});
4750                    } else {                    } else {
4751                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      !!!cp ('t108');
                     $prev_sibling = $foster_parent_element->last_child;  
4752                    }                    }
                   last OE;  
4753                  }                  }
               } # OE  
               $foster_parent_element = $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});  
4754                } else {                } else {
4755                  $foster_parent_element->insert_before                  if ($token->{attributes}->{charset}) {
4756                    ($self->{document}->create_text_node ($token->{data}),                    !!!cp ('t109');
4757                     $next_sibling);                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4758                          ->set_user_data (manakai_has_reference =>
4759                                               $token->{attributes}->{charset}
4760                                                   ->{has_reference});
4761                    }
4762                    if ($token->{attributes}->{content}) {
4763                      !!!cp ('t110');
4764                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4765                          ->set_user_data (manakai_has_reference =>
4766                                               $token->{attributes}->{content}
4767                                                   ->{has_reference});
4768                    }
4769                }                }
4770              } else {  
4771                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});                pop @{$self->{open_elements}} # <head>
4772              }                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4773                              !!!ack ('t110.1');
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $open_elements->[-1]->[0]->append_child ($comment);  
             !!!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 ($open_elements->[-1]->[1] ne 'table' and  
                      $open_elements->[-1]->[1] ne 'html') {  
                 !!!parse-error;  
                 pop @$open_elements;  
               }  
   
               push @$active_formatting_elements, ['#marker', '']  
                 if $token->{tag_name} eq 'caption';  
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $insertion_mode = {  
                                  caption => 'in caption',  
                                  colgroup => 'in column group',  
                                  tbody => 'in table body',  
                                  tfoot => 'in table body',  
                                  thead => 'in table body',  
                                 }->{$token->{tag_name}};  
4774                !!!next-token;                !!!next-token;
4775                redo B;                next B;
4776              } elsif ({              } elsif ($token->{tag_name} eq 'title') {
4777                        col => 1,                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4778                        td => 1, th => 1, tr => 1,                  !!!cp ('t111');
4779                       }->{$token->{tag_name}}) {                  ## As if </noscript>
4780                ## Clear back to table context                  pop @{$self->{open_elements}};
4781                while ($open_elements->[-1]->[1] ne 'table' and                  !!!parse-error (type => 'in noscript', text => 'title',
4782                       $open_elements->[-1]->[1] ne 'html') {                                  token => $token);
4783                  !!!parse-error;                
4784                  pop @$open_elements;                  $self->{insertion_mode} = IN_HEAD_IM;
4785                    ## Reprocess in the "in head" insertion mode...
4786                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4787                    !!!cp ('t112');
4788                    !!!parse-error (type => 'after head',
4789                                    text => $token->{tag_name}, token => $token);
4790                    push @{$self->{open_elements}},
4791                        [$self->{head_element}, $el_category->{head}];
4792                  } else {
4793                    !!!cp ('t113');
4794                }                }
4795    
4796                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                ## NOTE: There is a "as if in head" code clone.
4797                $insertion_mode = $token->{tag_name} eq 'col'                my $parent = defined $self->{head_element} ? $self->{head_element}
4798                  ? 'in column group' : 'in table body';                    : $self->{open_elements}->[-1]->[0];
4799                ## reprocess                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4800                redo B;                pop @{$self->{open_elements}} # <head>
4801              } elsif ($token->{tag_name} eq 'table') {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4802                ## NOTE: There are code clones for this "table in table"                next B;
4803                !!!parse-error;              } elsif ($token->{tag_name} eq 'style' or
4804                         $token->{tag_name} eq 'noframes') {
4805                ## As if </table>                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4806                ## have a table element in table scope                ## insertion mode IN_HEAD_IM)
4807                my $i;                ## NOTE: There is a "as if in head" code clone.
4808                INSCOPE: for (reverse 0..$#$open_elements) {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4809                  my $node = $open_elements->[$_];                  !!!cp ('t114');
4810                  if ($node->[1] eq 'table') {                  !!!parse-error (type => 'after head',
4811                    $i = $_;                                  text => $token->{tag_name}, token => $token);
4812                    last INSCOPE;                  push @{$self->{open_elements}},
4813                  } elsif ({                      [$self->{head_element}, $el_category->{head}];
4814                            table => 1, html => 1,                } else {
4815                           }->{$node->[1]}) {                  !!!cp ('t115');
4816                    last INSCOPE;                }
4817                  }                $parse_rcdata->(CDATA_CONTENT_MODEL);
4818                } # INSCOPE                pop @{$self->{open_elements}} # <head>
4819                unless (defined $i) {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4820                  !!!parse-error;                next B;
4821                  ## Ignore tokens </table><table>              } elsif ($token->{tag_name} eq 'noscript') {
4822                  if ($self->{insertion_mode} == IN_HEAD_IM) {
4823                    !!!cp ('t116');
4824                    ## NOTE: and scripting is disalbed
4825                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4826                    $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4827                    !!!nack ('t116.1');
4828                    !!!next-token;
4829                    next B;
4830                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4831                    !!!cp ('t117');
4832                    !!!parse-error (type => 'in noscript', text => 'noscript',
4833                                    token => $token);
4834                    ## Ignore the token
4835                    !!!nack ('t117.1');
4836                  !!!next-token;                  !!!next-token;
4837                  redo B;                  next B;
4838                  } else {
4839                    !!!cp ('t118');
4840                    #
4841                }                }
4842                } elsif ($token->{tag_name} eq 'script') {
4843                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4844                    !!!cp ('t119');
4845                    ## As if </noscript>
4846                    pop @{$self->{open_elements}};
4847                    !!!parse-error (type => 'in noscript', text => 'script',
4848                                    token => $token);
4849                                
4850                ## generate implied end tags                  $self->{insertion_mode} = IN_HEAD_IM;
4851                if ({                  ## Reprocess in the "in head" insertion mode...
4852                     dd => 1, dt => 1, li => 1, p => 1,                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4853                     td => 1, th => 1, tr => 1,                  !!!cp ('t120');
4854                    }->{$open_elements->[-1]->[1]}) {                  !!!parse-error (type => 'after head',
4855                  !!!back-token; # <table>                                  text => $token->{tag_name}, token => $token);
4856                  $token = {type => 'end tag', tag_name => 'table'};                  push @{$self->{open_elements}},
4857                  !!!back-token;                      [$self->{head_element}, $el_category->{head}];
4858                  $token = {type => 'end tag',                } else {
4859                            tag_name => $open_elements->[-1]->[1]}; # MUST                  !!!cp ('t121');
                 redo B;  
4860                }                }
4861    
4862                if ($open_elements->[-1]->[1] ne 'table') {                ## NOTE: There is a "as if in head" code clone.
4863                  !!!parse-error;                $script_start_tag->();
4864                  pop @{$self->{open_elements}} # <head>
4865                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4866                  next B;
4867                } elsif ($token->{tag_name} eq 'body' or
4868                         $token->{tag_name} eq 'frameset') {
4869                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4870                    !!!cp ('t122');
4871                    ## As if </noscript>
4872                    pop @{$self->{open_elements}};
4873                    !!!parse-error (type => 'in noscript',
4874                                    text => $token->{tag_name}, token => $token);
4875                    
4876                    ## Reprocess in the "in head" insertion mode...
4877                    ## As if </head>
4878                    pop @{$self->{open_elements}};
4879                    
4880                    ## Reprocess in the "after head" insertion mode...
4881                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4882                    !!!cp ('t124');
4883                    pop @{$self->{open_elements}};
4884                    
4885                    ## Reprocess in the "after head" insertion mode...
4886                  } else {
4887                    !!!cp ('t125');
4888                }                }
4889    
4890                splice @$open_elements, $i;                ## "after head" insertion mode
4891                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4892                  if ($token->{tag_name} eq 'body') {
4893                    !!!cp ('t126');
4894                    $self->{insertion_mode} = IN_BODY_IM;
4895                  } elsif ($token->{tag_name} eq 'frameset') {
4896                    !!!cp ('t127');
4897                    $self->{insertion_mode} = IN_FRAMESET_IM;
4898                  } else {
4899                    die "$0: tag name: $self->{tag_name}";
4900                  }
4901                  !!!nack ('t127.1');
4902                  !!!next-token;
4903                  next B;
4904                } else {
4905                  !!!cp ('t128');
4906                  #
4907                }
4908    
4909                $reset_insertion_mode->();                            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4910                  !!!cp ('t129');
4911                  ## As if </noscript>
4912                  pop @{$self->{open_elements}};
4913                  !!!parse-error (type => 'in noscript:/',
4914                                  text => $token->{tag_name}, token => $token);
4915                  
4916                  ## Reprocess in the "in head" insertion mode...
4917                  ## As if </head>
4918                  pop @{$self->{open_elements}};
4919    
4920                  ## Reprocess in the "after head" insertion mode...
4921                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4922                  !!!cp ('t130');
4923                  ## As if </head>
4924                  pop @{$self->{open_elements}};
4925    
4926                ## reprocess                ## Reprocess in the "after head" insertion mode...
               redo B;  
4927              } else {              } else {
4928                #                !!!cp ('t131');
4929              }              }
4930            } elsif ($token->{type} eq 'end tag') {  
4931              if ($token->{tag_name} eq 'table') {              ## "after head" insertion mode
4932                ## have a table element in table scope              ## As if <body>
4933                my $i;              !!!insert-element ('body',, $token);
4934                INSCOPE: for (reverse 0..$#$open_elements) {              $self->{insertion_mode} = IN_BODY_IM;
4935                  my $node = $open_elements->[$_];              ## reprocess
4936                  if ($node->[1] eq $token->{tag_name}) {              !!!ack-later;
4937                    $i = $_;              next B;
4938                    last INSCOPE;            } elsif ($token->{type} == END_TAG_TOKEN) {
4939                  } elsif ({              if ($token->{tag_name} eq 'head') {
4940                            table => 1, html => 1,                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4941                           }->{$node->[1]}) {                  !!!cp ('t132');
4942                    last INSCOPE;                  ## As if <head>
4943                  }                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4944                } # INSCOPE                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4945                unless (defined $i) {                  push @{$self->{open_elements}},
4946                  !!!parse-error;                      [$self->{head_element}, $el_category->{head}];
4947    
4948                    ## Reprocess in the "in head" insertion mode...
4949                    pop @{$self->{open_elements}};
4950                    $self->{insertion_mode} = AFTER_HEAD_IM;
4951                    !!!next-token;
4952                    next B;
4953                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4954                    !!!cp ('t133');
4955                    ## As if </noscript>
4956                    pop @{$self->{open_elements}};
4957                    !!!parse-error (type => 'in noscript:/',
4958                                    text => 'head', token => $token);
4959                    
4960                    ## Reprocess in the "in head" insertion mode...
4961                    pop @{$self->{open_elements}};
4962                    $self->{insertion_mode} = AFTER_HEAD_IM;
4963                    !!!next-token;
4964                    next B;
4965                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4966                    !!!cp ('t134');
4967                    pop @{$self->{open_elements}};
4968                    $self->{insertion_mode} = AFTER_HEAD_IM;
4969                    !!!next-token;
4970                    next B;
4971                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4972                    !!!cp ('t134.1');
4973                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4974                                    token => $token);
4975                  ## Ignore the token                  ## Ignore the token
4976                  !!!next-token;                  !!!next-token;
4977                  redo B;                  next B;
4978                  } else {
4979                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4980                }                }
4981                              } elsif ($token->{tag_name} eq 'noscript') {
4982                ## generate implied end tags                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4983                if ({                  !!!cp ('t136');
4984                     dd => 1, dt => 1, li => 1, p => 1,                  pop @{$self->{open_elements}};
4985                     td => 1, th => 1, tr => 1,                  $self->{insertion_mode} = IN_HEAD_IM;
4986                    }->{$open_elements->[-1]->[1]}) {                  !!!next-token;
4987                  !!!back-token;                  next B;
4988                  $token = {type => 'end tag',                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4989                            tag_name => $open_elements->[-1]->[1]}; # MUST                         $self->{insertion_mode} == AFTER_HEAD_IM) {
4990                  redo B;                  !!!cp ('t137');
4991                    !!!parse-error (type => 'unmatched end tag',
4992                                    text => 'noscript', token => $token);
4993                    ## Ignore the token ## ISSUE: An issue in the spec.
4994                    !!!next-token;
4995                    next B;
4996                  } else {
4997                    !!!cp ('t138');
4998                    #
4999                }                }
5000                } elsif ({
5001                if ($open_elements->[-1]->[1] ne 'table') {                        body => 1, html => 1,
5002                  !!!parse-error;                       }->{$token->{tag_name}}) {
5003                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5004                      $self->{insertion_mode} == IN_HEAD_IM or
5005                      $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5006                    !!!cp ('t140');
5007                    !!!parse-error (type => 'unmatched end tag',
5008                                    text => $token->{tag_name}, token => $token);
5009                    ## Ignore the token
5010                    !!!next-token;
5011                    next B;
5012                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5013                    !!!cp ('t140.1');
5014                    !!!parse-error (type => 'unmatched end tag',
5015                                    text => $token->{tag_name}, token => $token);
5016                    ## Ignore the token
5017                    !!!next-token;
5018                    next B;
5019                  } else {
5020                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5021                  }
5022                } elsif ($token->{tag_name} eq 'p') {
5023                  !!!cp ('t142');
5024                  !!!parse-error (type => 'unmatched end tag',
5025                                  text => $token->{tag_name}, token => $token);
5026                  ## Ignore the token
5027                  !!!next-token;
5028                  next B;
5029                } elsif ($token->{tag_name} eq 'br') {
5030                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5031                    !!!cp ('t142.2');
5032                    ## (before head) as if <head>, (in head) as if </head>
5033                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5034                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5035                    $self->{insertion_mode} = AFTER_HEAD_IM;
5036      
5037                    ## Reprocess in the "after head" insertion mode...
5038                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5039                    !!!cp ('t143.2');
5040                    ## As if </head>
5041                    pop @{$self->{open_elements}};
5042                    $self->{insertion_mode} = AFTER_HEAD_IM;
5043      
5044                    ## Reprocess in the "after head" insertion mode...
5045                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5046                    !!!cp ('t143.3');
5047                    ## ISSUE: Two parse errors for <head><noscript></br>
5048                    !!!parse-error (type => 'unmatched end tag',
5049                                    text => 'br', token => $token);
5050                    ## As if </noscript>
5051                    pop @{$self->{open_elements}};
5052                    $self->{insertion_mode} = IN_HEAD_IM;
5053    
5054                    ## Reprocess in the "in head" insertion mode...
5055                    ## As if </head>
5056                    pop @{$self->{open_elements}};
5057                    $self->{insertion_mode} = AFTER_HEAD_IM;
5058    
5059                    ## Reprocess in the "after head" insertion mode...
5060                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5061                    !!!cp ('t143.4');
5062                    #
5063                  } else {
5064                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5065                }                }
5066    
5067                splice @$open_elements, $i;                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5068                  !!!parse-error (type => 'unmatched end tag',
5069                $reset_insertion_mode->();                                text => 'br', token => $token);
5070                  ## Ignore the token
5071                !!!next-token;                !!!next-token;
5072                redo B;                next B;
5073              } elsif ({              } else {
5074                        body => 1, caption => 1, col => 1, colgroup => 1,                !!!cp ('t145');
5075                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,                !!!parse-error (type => 'unmatched end tag',
5076                        thead => 1, tr => 1,                                text => $token->{tag_name}, token => $token);
                      }->{$token->{tag_name}}) {  
               !!!parse-error;  
5077                ## Ignore the token                ## Ignore the token
5078                !!!next-token;                !!!next-token;
5079                redo B;                next B;
5080                }
5081    
5082                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5083                  !!!cp ('t146');
5084                  ## As if </noscript>
5085                  pop @{$self->{open_elements}};
5086                  !!!parse-error (type => 'in noscript:/',
5087                                  text => $token->{tag_name}, token => $token);
5088                  
5089                  ## Reprocess in the "in head" insertion mode...
5090                  ## As if </head>
5091                  pop @{$self->{open_elements}};
5092    
5093                  ## Reprocess in the "after head" insertion mode...
5094                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5095                  !!!cp ('t147');
5096                  ## As if </head>
5097                  pop @{$self->{open_elements}};
5098    
5099                  ## Reprocess in the "after head" insertion mode...
5100                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5101    ## ISSUE: This case cannot be reached?
5102                  !!!cp ('t148');
5103                  !!!parse-error (type => 'unmatched end tag',
5104                                  text => $token->{tag_name}, token => $token);
5105                  ## Ignore the token ## ISSUE: An issue in the spec.
5106                  !!!next-token;
5107                  next B;
5108              } else {              } else {
5109                #                !!!cp ('t149');
5110              }              }
           } else {  
             #  
           }  
5111    
5112            !!!parse-error;              ## "after head" insertion mode
5113            $in_body->($insert_to_foster);              ## As if <body>
5114            redo B;              !!!insert-element ('body',, $token);
5115          } elsif ($insertion_mode eq 'in caption') {              $self->{insertion_mode} = IN_BODY_IM;
5116            if ($token->{type} eq 'character') {              ## reprocess
5117              ## NOTE: This is a code clone of "character in body".              next B;
5118          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5119            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5120              !!!cp ('t149.1');
5121    
5122              ## NOTE: As if <head>
5123              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5124              $self->{open_elements}->[-1]->[0]->append_child
5125                  ($self->{head_element});
5126              #push @{$self->{open_elements}},
5127              #    [$self->{head_element}, $el_category->{head}];
5128              #$self->{insertion_mode} = IN_HEAD_IM;
5129              ## NOTE: Reprocess.
5130    
5131              ## NOTE: As if </head>
5132              #pop @{$self->{open_elements}};
5133              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5134              ## NOTE: Reprocess.
5135              
5136              #
5137            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5138              !!!cp ('t149.2');
5139    
5140              ## NOTE: As if </head>
5141              pop @{$self->{open_elements}};
5142              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5143              ## NOTE: Reprocess.
5144    
5145              #
5146            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5147              !!!cp ('t149.3');
5148    
5149              !!!parse-error (type => 'in noscript:#eof', token => $token);
5150    
5151              ## As if </noscript>
5152              pop @{$self->{open_elements}};
5153              #$self->{insertion_mode} = IN_HEAD_IM;
5154              ## NOTE: Reprocess.
5155    
5156              ## NOTE: As if </head>
5157              pop @{$self->{open_elements}};
5158              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5159              ## NOTE: Reprocess.
5160    
5161              #
5162            } else {
5163              !!!cp ('t149.4');
5164              #
5165            }
5166    
5167            ## NOTE: As if <body>
5168            !!!insert-element ('body',, $token);
5169            $self->{insertion_mode} = IN_BODY_IM;
5170            ## NOTE: Reprocess.
5171            next B;
5172          } else {
5173            die "$0: $token->{type}: Unknown token type";
5174          }
5175    
5176              ## ISSUE: An issue in the spec.
5177        } elsif ($self->{insertion_mode} & BODY_IMS) {
5178              if ($token->{type} == CHARACTER_TOKEN) {
5179                !!!cp ('t150');
5180                ## NOTE: There is a code clone of "character in body".
5181              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5182                            
5183              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5184    
5185              !!!next-token;              !!!next-token;
5186              redo B;              next B;
5187            } 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});  
             $open_elements->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
5188              if ({              if ({
5189                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5190                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5191                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5192                !!!parse-error;                if ($self->{insertion_mode} == IN_CELL_IM) {
5193                    ## have an element in table scope
5194                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
5195                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
5196                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
5197                INSCOPE: for (reverse 0..$#$open_elements) {                      !!!cp ('t151');
5198                  my $node = $open_elements->[$_];  
5199                  if ($node->[1] eq 'caption') {                      ## Close the cell
5200                    $i = $_;                      !!!back-token; # <x>
5201                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
5202                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
5203                            table => 1, html => 1,                                line => $token->{line},
5204                           }->{$node->[1]}) {                                column => $token->{column}};
5205                    last INSCOPE;                      next B;
5206                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5207                        !!!cp ('t152');
5208                        ## ISSUE: This case can never be reached, maybe.
5209                        last;
5210                      }
5211                  }                  }
5212                } # INSCOPE  
5213                unless (defined $i) {                  !!!cp ('t153');
5214                  !!!parse-error;                  !!!parse-error (type => 'start tag not allowed',
5215                        text => $token->{tag_name}, token => $token);
5216                  ## Ignore the token                  ## Ignore the token
5217                    !!!nack ('t153.1');
5218                  !!!next-token;                  !!!next-token;
5219                  redo B;                  next B;
5220                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5221                                  !!!parse-error (type => 'not closed', text => 'caption',
5222                ## generate implied end tags                                  token => $token);
5223                if ({                  
5224                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5225                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5226                    }->{$open_elements->[-1]->[1]}) {                  my $i;
5227                  !!!back-token; # <?>                  INSCOPE: {
5228                  $token = {type => 'end tag', tag_name => 'caption'};                    for (reverse 0..$#{$self->{open_elements}}) {
5229                  !!!back-token;                      my $node = $self->{open_elements}->[$_];
5230                  $token = {type => 'end tag',                      if ($node->[1] & CAPTION_EL) {
5231                            tag_name => $open_elements->[-1]->[1]}; # MUST                        !!!cp ('t155');
5232                  redo B;                        $i = $_;
5233                }                        last INSCOPE;
5234                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5235                if ($open_elements->[-1]->[1] ne 'caption') {                        !!!cp ('t156');
5236                  !!!parse-error;                        last;
5237                }                      }
5238                      }
               splice @$open_elements, $i;  
   
               $clear_up_to_marker->();  
5239    
5240                $insertion_mode = 'in table';                    !!!cp ('t157');
5241                      !!!parse-error (type => 'start tag not allowed',
5242                                      text => $token->{tag_name}, token => $token);
5243                      ## Ignore the token
5244                      !!!nack ('t157.1');
5245                      !!!next-token;
5246                      next B;
5247                    } # INSCOPE
5248                    
5249                    ## generate implied end tags
5250                    while ($self->{open_elements}->[-1]->[1]
5251                               & END_TAG_OPTIONAL_EL) {
5252                      !!!cp ('t158');
5253                      pop @{$self->{open_elements}};
5254                    }
5255    
5256                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5257                redo B;                    !!!cp ('t159');
5258                      !!!parse-error (type => 'not closed',
5259                                      text => $self->{open_elements}->[-1]->[0]
5260                                          ->manakai_local_name,
5261                                      token => $token);
5262                    } else {
5263                      !!!cp ('t160');
5264                    }
5265                    
5266                    splice @{$self->{open_elements}}, $i;
5267                    
5268                    $clear_up_to_marker->();
5269                    
5270                    $self->{insertion_mode} = IN_TABLE_IM;
5271                    
5272                    ## reprocess
5273                    !!!ack-later;
5274                    next B;
5275                  } else {
5276                    !!!cp ('t161');
5277                    #
5278                  }
5279              } else {              } else {
5280                  !!!cp ('t162');
5281                #                #
5282              }              }
5283            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5284              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5285                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
5286                my $i;                  ## have an element in table scope
5287                INSCOPE: for (reverse 0..$#$open_elements) {                  my $i;
5288                  my $node = $open_elements->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5289                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5290                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5291                    last INSCOPE;                      !!!cp ('t163');
5292                  } elsif ({                      $i = $_;
5293                            table => 1, html => 1,                      last INSCOPE;
5294                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5295                    last INSCOPE;                      !!!cp ('t164');
5296                        last INSCOPE;
5297                      }
5298                    } # INSCOPE
5299                      unless (defined $i) {
5300                        !!!cp ('t165');
5301                        !!!parse-error (type => 'unmatched end tag',
5302                                        text => $token->{tag_name},
5303                                        token => $token);
5304                        ## Ignore the token
5305                        !!!next-token;
5306                        next B;
5307                      }
5308                    
5309                    ## generate implied end tags
5310                    while ($self->{open_elements}->[-1]->[1]
5311                               & END_TAG_OPTIONAL_EL) {
5312                      !!!cp ('t166');
5313                      pop @{$self->{open_elements}};
5314                  }                  }
5315                } # INSCOPE  
5316                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5317                  !!!parse-error;                          ne $token->{tag_name}) {
5318                      !!!cp ('t167');
5319                      !!!parse-error (type => 'not closed',
5320                                      text => $self->{open_elements}->[-1]->[0]
5321                                          ->manakai_local_name,
5322                                      token => $token);
5323                    } else {
5324                      !!!cp ('t168');
5325                    }
5326                    
5327                    splice @{$self->{open_elements}}, $i;
5328                    
5329                    $clear_up_to_marker->();
5330                    
5331                    $self->{insertion_mode} = IN_ROW_IM;
5332                    
5333                    !!!next-token;
5334                    next B;
5335                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5336                    !!!cp ('t169');
5337                    !!!parse-error (type => 'unmatched end tag',
5338                                    text => $token->{tag_name}, token => $token);
5339                  ## Ignore the token                  ## Ignore the token
5340                  !!!next-token;                  !!!next-token;
5341                  redo B;                  next B;
5342                }                } else {
5343                                  !!!cp ('t170');
5344                ## generate implied end tags                  #
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$open_elements->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $open_elements->[-1]->[1]}; # MUST  
                 redo B;  
5345                }                }
5346                } elsif ($token->{tag_name} eq 'caption') {
5347                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5348                    ## have a table element in table scope
5349                    my $i;
5350                    INSCOPE: {
5351                      for (reverse 0..$#{$self->{open_elements}}) {
5352                        my $node = $self->{open_elements}->[$_];
5353                        if ($node->[1] & CAPTION_EL) {
5354                          !!!cp ('t171');
5355                          $i = $_;
5356                          last INSCOPE;
5357                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5358                          !!!cp ('t172');
5359                          last;
5360                        }
5361                      }
5362    
5363                if ($open_elements->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5364                  !!!parse-error;                    !!!parse-error (type => 'unmatched end tag',
5365                                      text => $token->{tag_name}, token => $token);
5366                      ## Ignore the token
5367                      !!!next-token;
5368                      next B;
5369                    } # INSCOPE
5370                    
5371                    ## generate implied end tags
5372                    while ($self->{open_elements}->[-1]->[1]
5373                               & END_TAG_OPTIONAL_EL) {
5374                      !!!cp ('t174');
5375                      pop @{$self->{open_elements}};
5376                    }
5377                    
5378                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5379                      !!!cp ('t175');
5380                      !!!parse-error (type => 'not closed',
5381                                      text => $self->{open_elements}->[-1]->[0]
5382                                          ->manakai_local_name,
5383                                      token => $token);
5384                    } else {
5385                      !!!cp ('t176');
5386                    }
5387                    
5388                    splice @{$self->{open_elements}}, $i;
5389                    
5390                    $clear_up_to_marker->();
5391                    
5392                    $self->{insertion_mode} = IN_TABLE_IM;
5393                    
5394                    !!!next-token;
5395                    next B;
5396                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5397                    !!!cp ('t177');
5398                    !!!parse-error (type => 'unmatched end tag',
5399                                    text => $token->{tag_name}, token => $token);
5400                    ## Ignore the token
5401                    !!!next-token;
5402                    next B;
5403                  } else {
5404                    !!!cp ('t178');
5405                    #
5406                }                }
5407                } elsif ({
5408                          table => 1, tbody => 1, tfoot => 1,
5409                          thead => 1, tr => 1,
5410                         }->{$token->{tag_name}} and
5411                         $self->{insertion_mode} == IN_CELL_IM) {
5412                  ## have an element in table scope
5413                  my $i;
5414                  my $tn;
5415                  INSCOPE: {
5416                    for (reverse 0..$#{$self->{open_elements}}) {
5417                      my $node = $self->{open_elements}->[$_];
5418                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5419                        !!!cp ('t179');
5420                        $i = $_;
5421    
5422                        ## Close the cell
5423                        !!!back-token; # </x>
5424                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5425                                  line => $token->{line},
5426                                  column => $token->{column}};
5427                        next B;
5428                      } elsif ($node->[1] & TABLE_CELL_EL) {
5429                        !!!cp ('t180');
5430                        $tn = $node->[0]->manakai_local_name;
5431                        ## NOTE: There is exactly one |td| or |th| element
5432                        ## in scope in the stack of open elements by definition.
5433                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5434                        ## ISSUE: Can this be reached?
5435                        !!!cp ('t181');
5436                        last;
5437                      }
5438                    }
5439    
5440                splice @$open_elements, $i;                  !!!cp ('t182');
5441                    !!!parse-error (type => 'unmatched end tag',
5442                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5443                    ## Ignore the token
5444                $insertion_mode = 'in table';                  !!!next-token;
5445                    next B;
5446                !!!next-token;                } # INSCOPE
5447                redo B;              } elsif ($token->{tag_name} eq 'table' and
5448              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5449                !!!parse-error;                !!!parse-error (type => 'not closed', text => 'caption',
5450                                  token => $token);
5451    
5452                ## As if </caption>                ## As if </caption>
5453                ## have a table element in table scope                ## have a table element in table scope
5454                my $i;                my $i;
5455                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5456                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
5457                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5458                      !!!cp ('t184');
5459                    $i = $_;                    $i = $_;
5460                    last INSCOPE;                    last INSCOPE;
5461                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5462                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5463                    last INSCOPE;                    last INSCOPE;
5464                  }                  }
5465                } # INSCOPE                } # INSCOPE
5466                unless (defined $i) {                unless (defined $i) {
5467                  !!!parse-error;                  !!!cp ('t186');
5468                    !!!parse-error (type => 'unmatched end tag',
5469                                    text => 'caption', token => $token);
5470                  ## Ignore the token                  ## Ignore the token
5471                  !!!next-token;                  !!!next-token;
5472                  redo B;                  next B;
5473                }                }
5474                                
5475                ## generate implied end tags                ## generate implied end tags
5476                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5477                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5478                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$open_elements->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $open_elements->[-1]->[1]}; # MUST  
                 redo B;  
5479                }                }
5480    
5481                if ($open_elements->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5482                  !!!parse-error;                  !!!cp ('t188');
5483                    !!!parse-error (type => 'not closed',
5484                                    text => $self->{open_elements}->[-1]->[0]
5485                                        ->manakai_local_name,
5486                                    token => $token);
5487                  } else {
5488                    !!!cp ('t189');
5489                }                }
5490    
5491                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
5492    
5493                $clear_up_to_marker->();                $clear_up_to_marker->();
5494    
5495                $insertion_mode = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5496    
5497                ## reprocess                ## reprocess
5498                redo B;                next B;
5499              } elsif ({              } elsif ({
5500                        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,  
5501                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5502                !!!parse-error;                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5503                ## Ignore the token                  !!!cp ('t190');
5504                redo B;                  !!!parse-error (type => 'unmatched end tag',
5505              } else {                                  text => $token->{tag_name}, token => $token);
               #  
             }  
           } else {  
             #  
           }  
                 
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($insertion_mode eq 'in column group') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $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});  
             $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 @$open_elements;  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'colgroup') {  
               if ($open_elements->[-1]->[1] eq 'html') {  
                 !!!parse-error;  
5506                  ## Ignore the token                  ## Ignore the token
5507                  !!!next-token;                  !!!next-token;
5508                  redo B;                  next B;
5509                } else {                } else {
5510                  pop @$open_elements; # colgroup                  !!!cp ('t191');
5511                  $insertion_mode = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5512                }                }
5513              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5514                !!!parse-error;                        tbody => 1, tfoot => 1,
5515                          thead => 1, tr => 1,
5516                         }->{$token->{tag_name}} and
5517                         $self->{insertion_mode} == IN_CAPTION_IM) {
5518                  !!!cp ('t192');
5519                  !!!parse-error (type => 'unmatched end tag',
5520                                  text => $token->{tag_name}, token => $token);
5521                ## Ignore the token                ## Ignore the token
5522                !!!next-token;                !!!next-token;
5523                redo B;                next B;
5524              } else {              } else {
5525                #                !!!cp ('t193');
5526                  #
5527              }              }
5528            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5529              #          for my $entry (@{$self->{open_elements}}) {
5530              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5531                !!!cp ('t75');
5532                !!!parse-error (type => 'in body:#eof', token => $token);
5533                last;
5534            }            }
5535            }
5536    
5537            ## As if </colgroup>          ## Stop parsing.
5538            if ($open_elements->[-1]->[1] eq 'html') {          last B;
5539              !!!parse-error;        } else {
5540              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5541          }
5542    
5543          $insert = $insert_to_current;
5544          #
5545        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5546          if ($token->{type} == CHARACTER_TOKEN) {
5547            if (not $open_tables->[-1]->[1] and # tainted
5548                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5549              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5550                  
5551              unless (length $token->{data}) {
5552                !!!cp ('t194');
5553              !!!next-token;              !!!next-token;
5554              redo B;              next B;
5555            } else {            } else {
5556              pop @$open_elements; # colgroup              !!!cp ('t195');
             $insertion_mode = 'in table';  
             ## reprocess  
             redo B;  
5557            }            }
5558          } elsif ($insertion_mode eq 'in table body') {          }
5559            if ($token->{type} eq 'character') {  
5560              ## NOTE: This is a "character in table" code clone.          !!!parse-error (type => 'in table:#text', token => $token);
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $open_elements->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
5561    
5562              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5563              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
5564              ## into the current node" while characters might not be              ## into the current node" while characters might not be
5565              ## result in a new Text node.              ## result in a new Text node.
5566              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5567                
5568              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$open_elements->[-1]->[1]}) {  
5569                # MUST                # MUST
5570                my $foster_parent_element;                my $foster_parent_element;
5571                my $next_sibling;                my $next_sibling;
5572                my $prev_sibling;                my $prev_sibling;
5573                OE: for (reverse 0..$#$open_elements) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5574                  if ($open_elements->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5575                    my $parent = $open_elements->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5576                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5577                        !!!cp ('t196');
5578                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5579                      $next_sibling = $open_elements->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5580                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5581                    } else {                    } else {
5582                      $foster_parent_element = $open_elements->[$_ - 1]->[0];                      !!!cp ('t197');
5583                        $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5584                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5585                    }                    }
5586                    last OE;                    last OE;
5587                  }                  }
5588                } # OE                } # OE
5589                $foster_parent_element = $open_elements->[0]->[0] and                $foster_parent_element = $self->{open_elements}->[0]->[0] and
5590                $prev_sibling = $foster_parent_element->last_child                $prev_sibling = $foster_parent_element->last_child
5591                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5592                if (defined $prev_sibling and                if (defined $prev_sibling and
5593                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5594                    !!!cp ('t198');
5595                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5596                } else {                } else {
5597                    !!!cp ('t199');
5598                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5599                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5600                     $next_sibling);                     $next_sibling);
5601                }                }
5602              } else {            $open_tables->[-1]->[1] = 1; # tainted
5603                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5604              !!!cp ('t200');
5605              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5606            }
5607                
5608            !!!next-token;
5609            next B;
5610          } elsif ($token->{type} == START_TAG_TOKEN) {
5611            if ({
5612                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5613                 th => 1, td => 1,
5614                }->{$token->{tag_name}}) {
5615              if ($self->{insertion_mode} == IN_TABLE_IM) {
5616                ## Clear back to table context
5617                while (not ($self->{open_elements}->[-1]->[1]
5618                                & TABLE_SCOPING_EL)) {
5619                  !!!cp ('t201');
5620                  pop @{$self->{open_elements}};
5621              }              }
5622                            
5623              !!!next-token;              !!!insert-element ('tbody',, $token);
5624              redo B;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5625            } elsif ($token->{type} eq 'comment') {              ## reprocess in the "in table body" insertion mode...
5626              ## Copied from 'in table'            }
5627              my $comment = $self->{document}->create_comment ($token->{data});            
5628              $open_elements->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5629              !!!next-token;              unless ($token->{tag_name} eq 'tr') {
5630              redo B;                !!!cp ('t202');
5631            } elsif ($token->{type} eq 'start tag') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
5632              if ({              }
5633                   tr => 1,                  
5634                   th => 1, td => 1,              ## Clear back to table body context
5635                  }->{$token->{tag_name}}) {              while (not ($self->{open_elements}->[-1]->[1]
5636                ## Clear back to table body context                              & TABLE_ROWS_SCOPING_EL)) {
5637                while (not {                !!!cp ('t203');
5638                  tbody => 1, tfoot => 1, thead => 1, html => 1,                ## ISSUE: Can this case be reached?
5639                }->{$open_elements->[-1]->[1]}) {                pop @{$self->{open_elements}};
5640                  !!!parse-error;              }
5641                  pop @$open_elements;                  
5642                }                  $self->{insertion_mode} = IN_ROW_IM;
5643                                  if ($token->{tag_name} eq 'tr') {
5644                $insertion_mode = 'in row';                    !!!cp ('t204');
5645                if ($token->{tag_name} eq 'tr') {                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5646                  !!!insert-element ($token->{tag_name}, $token->{attributes});                    !!!nack ('t204');
5647                  !!!next-token;                    !!!next-token;
5648                      next B;
5649                    } else {
5650                      !!!cp ('t205');
5651                      !!!insert-element ('tr',, $token);
5652                      ## reprocess in the "in row" insertion mode
5653                    }
5654                } else {                } else {
5655                  !!!insert-element ('tr');                  !!!cp ('t206');
                 ## reprocess  
5656                }                }
5657                redo B;  
5658                  ## Clear back to table row context
5659                  while (not ($self->{open_elements}->[-1]->[1]
5660                                  & TABLE_ROW_SCOPING_EL)) {
5661                    !!!cp ('t207');
5662                    pop @{$self->{open_elements}};
5663                  }
5664                  
5665                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5666                  $self->{insertion_mode} = IN_CELL_IM;
5667    
5668                  push @$active_formatting_elements, ['#marker', ''];
5669                  
5670                  !!!nack ('t207.1');
5671                  !!!next-token;
5672                  next B;
5673              } elsif ({              } elsif ({
5674                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5675                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5676                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5677                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5678                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
5679                my $i;                  ## As if </tr>
5680                INSCOPE: for (reverse 0..$#$open_elements) {                  ## have an element in table scope
5681                  my $node = $open_elements->[$_];                  my $i;
5682                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5683                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
5684                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
5685                    $i = $_;                      !!!cp ('t208');
5686                    last INSCOPE;                      $i = $_;
5687                  } elsif ({                      last INSCOPE;
5688                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5689                           }->{$node->[1]}) {                      !!!cp ('t209');
5690                    last INSCOPE;                      last INSCOPE;
5691                      }
5692                    } # INSCOPE
5693                    unless (defined $i) {
5694                      !!!cp ('t210');
5695    ## TODO: This type is wrong.
5696                      !!!parse-error (type => 'unmacthed end tag',
5697                                      text => $token->{tag_name}, token => $token);
5698                      ## Ignore the token
5699                      !!!nack ('t210.1');
5700                      !!!next-token;
5701                      next B;
5702                    }
5703                    
5704                    ## Clear back to table row context
5705                    while (not ($self->{open_elements}->[-1]->[1]
5706                                    & TABLE_ROW_SCOPING_EL)) {
5707                      !!!cp ('t211');
5708                      ## ISSUE: Can this case be reached?
5709                      pop @{$self->{open_elements}};
5710                    }
5711                    
5712                    pop @{$self->{open_elements}}; # tr
5713                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5714                    if ($token->{tag_name} eq 'tr') {
5715                      !!!cp ('t212');
5716                      ## reprocess
5717                      !!!ack-later;
5718                      next B;
5719                    } else {
5720                      !!!cp ('t213');
5721                      ## reprocess in the "in table body" insertion mode...
5722                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error;  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## Clear back to table body context  
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$open_elements->[-1]->[1]}) {  
                 !!!parse-error;  
                 pop @$open_elements;  
5723                }                }
5724    
5725                ## As if <{current node}>                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5726                ## have an element in table scope                  ## have an element in table scope
5727                ## true by definition                  my $i;
5728                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5729                      my $node = $self->{open_elements}->[$_];
5730                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5731                        !!!cp ('t214');
5732                        $i = $_;
5733                        last INSCOPE;
5734                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5735                        !!!cp ('t215');
5736                        last INSCOPE;
5737                      }
5738                    } # INSCOPE
5739                    unless (defined $i) {
5740                      !!!cp ('t216');
5741    ## TODO: This erorr type is wrong.
5742                      !!!parse-error (type => 'unmatched end tag',
5743                                      text => $token->{tag_name}, token => $token);
5744                      ## Ignore the token
5745                      !!!nack ('t216.1');
5746                      !!!next-token;
5747                      next B;
5748                    }
5749    
5750                ## Clear back to table body context                  ## Clear back to table body context
5751                ## nop by definition                  while (not ($self->{open_elements}->[-1]->[1]
5752                                    & TABLE_ROWS_SCOPING_EL)) {
5753                      !!!cp ('t217');
5754                      ## ISSUE: Can this state be reached?
5755                      pop @{$self->{open_elements}};
5756                    }
5757                    
5758                    ## As if <{current node}>
5759                    ## have an element in table scope
5760                    ## true by definition
5761                    
5762                    ## Clear back to table body context
5763                    ## nop by definition
5764                    
5765                    pop @{$self->{open_elements}};
5766                    $self->{insertion_mode} = IN_TABLE_IM;
5767                    ## reprocess in "in table" insertion mode...
5768                  } else {
5769                    !!!cp ('t218');
5770                  }
5771    
5772                pop @$open_elements;                if ($token->{tag_name} eq 'col') {
5773                $insertion_mode = 'in table';                  ## Clear back to table context
5774                ## reprocess                  while (not ($self->{open_elements}->[-1]->[1]
5775                redo B;                                  & TABLE_SCOPING_EL)) {
5776                      !!!cp ('t219');
5777                      ## ISSUE: Can this state be reached?
5778                      pop @{$self->{open_elements}};
5779                    }
5780                    
5781                    !!!insert-element ('colgroup',, $token);
5782                    $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5783                    ## reprocess
5784                    !!!ack-later;
5785                    next B;
5786                  } elsif ({
5787                            caption => 1,
5788                            colgroup => 1,
5789                            tbody => 1, tfoot => 1, thead => 1,
5790                           }->{$token->{tag_name}}) {
5791                    ## Clear back to table context
5792                    while (not ($self->{open_elements}->[-1]->[1]
5793                                    & TABLE_SCOPING_EL)) {
5794                      !!!cp ('t220');
5795                      ## ISSUE: Can this state be reached?
5796                      pop @{$self->{open_elements}};
5797                    }
5798                    
5799                    push @$active_formatting_elements, ['#marker', '']
5800                        if $token->{tag_name} eq 'caption';
5801                    
5802                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5803                    $self->{insertion_mode} = {
5804                                               caption => IN_CAPTION_IM,
5805                                               colgroup => IN_COLUMN_GROUP_IM,
5806                                               tbody => IN_TABLE_BODY_IM,
5807                                               tfoot => IN_TABLE_BODY_IM,
5808                                               thead => IN_TABLE_BODY_IM,
5809                                              }->{$token->{tag_name}};
5810                    !!!next-token;
5811                    !!!nack ('t220.1');
5812                    next B;
5813                  } else {
5814                    die "$0: in table: <>: $token->{tag_name}";
5815                  }
5816              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5817                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5818                !!!parse-error;                                text => $self->{open_elements}->[-1]->[0]
5819                                      ->manakai_local_name,
5820                                  token => $token);
5821    
5822                ## As if </table>                ## As if </table>
5823                ## have a table element in table scope                ## have a table element in table scope
5824                my $i;                my $i;
5825                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5826                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
5827                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5828                      !!!cp ('t221');
5829                    $i = $_;                    $i = $_;
5830                    last INSCOPE;                    last INSCOPE;
5831                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5832                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5833                    last INSCOPE;                    last INSCOPE;
5834                  }                  }
5835                } # INSCOPE                } # INSCOPE
5836                unless (defined $i) {                unless (defined $i) {
5837                  !!!parse-error;                  !!!cp ('t223');
5838    ## TODO: The following is wrong, maybe.
5839                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5840                                    token => $token);
5841                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5842                    !!!nack ('t223.1');
5843                  !!!next-token;                  !!!next-token;
5844                  redo B;                  next B;
5845                }                }
5846                                
5847    ## TODO: Followings are removed from the latest spec.
5848                ## generate implied end tags                ## generate implied end tags
5849                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5850                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5851                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$open_elements->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $open_elements->[-1]->[1]}; # MUST  
                 redo B;  
5852                }                }
5853    
5854                if ($open_elements->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5855                  !!!parse-error;                  !!!cp ('t225');
5856                    ## NOTE: |<table><tr><table>|
5857                    !!!parse-error (type => 'not closed',
5858                                    text => $self->{open_elements}->[-1]->[0]
5859                                        ->manakai_local_name,
5860                                    token => $token);
5861                  } else {
5862                    !!!cp ('t226');
5863                }                }
5864    
5865                splice @$open_elements, $i;                splice @{$self->{open_elements}}, $i;
5866                  pop @{$open_tables};
               $reset_insertion_mode->();                
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ({  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#$open_elements) {  
                 my $node = $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;  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
5867    
5868                ## Clear back to table body context                $self->_reset_insertion_mode;
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$open_elements->[-1]->[1]}) {  
                 !!!parse-error;  
                 pop @$open_elements;  
               }  
5869    
5870                pop @$open_elements;            ## reprocess
5871                $insertion_mode = 'in table';            !!!ack-later;
5872                !!!next-token;            next B;
5873                redo B;          } elsif ($token->{tag_name} eq 'style') {
5874              } elsif ($token->{tag_name} eq 'table') {            if (not $open_tables->[-1]->[1]) { # tainted
5875                ## have an element in table scope              !!!cp ('t227.8');
5876                my $i;              ## NOTE: This is a "as if in head" code clone.
5877                INSCOPE: for (reverse 0..$#$open_elements) {              $parse_rcdata->(CDATA_CONTENT_MODEL);
5878                  my $node = $open_elements->[$_];              next B;
5879                  if ({            } else {
5880                       tbody => 1, thead => 1, tfoot => 1,              !!!cp ('t227.7');
5881                      }->{$node->[1]}) {              #
5882                    $i = $_;            }
5883                    last INSCOPE;          } elsif ($token->{tag_name} eq 'script') {
5884                  } elsif ({            if (not $open_tables->[-1]->[1]) { # tainted
5885                            table => 1, html => 1,              !!!cp ('t227.6');
5886                           }->{$node->[1]}) {              ## NOTE: This is a "as if in head" code clone.
5887                    last INSCOPE;              $script_start_tag->();
5888                  }              next B;
5889                } # INSCOPE            } else {
5890                unless (defined $i) {              !!!cp ('t227.5');
5891                  !!!parse-error;              #
5892                  ## Ignore the token            }
5893                  !!!next-token;          } elsif ($token->{tag_name} eq 'input') {
5894                  redo B;            if (not $open_tables->[-1]->[1]) { # tainted
5895                }              if ($token->{attributes}->{type}) { ## TODO: case
5896                  my $type = lc $token->{attributes}->{type}->{value};
5897                  if ($type eq 'hidden') {
5898                    !!!cp ('t227.3');
5899                    !!!parse-error (type => 'in table',
5900                                    text => $token->{tag_name}, token => $token);
5901    
5902                ## Clear back to table body context                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$open_elements->[-1]->[1]}) {  
                 !!!parse-error;  
                 pop @$open_elements;  
               }  
5903    
5904                ## As if <{current node}>                  ## TODO: form element pointer
               ## have an element in table scope  
               ## true by definition  
5905    
5906                ## Clear back to table body context                  pop @{$self->{open_elements}};
               ## nop by definition  
5907    
5908                pop @$open_elements;                  !!!next-token;
5909                $insertion_mode = 'in table';                  !!!ack ('t227.2.1');
5910                ## reprocess                  next B;
5911                redo B;                } else {
5912              } elsif ({                  !!!cp ('t227.2');
5913                        body => 1, caption => 1, col => 1, colgroup => 1,                  #
5914                        html => 1, td => 1, th => 1, tr => 1,                }
                      }->{$token->{tag_name}}) {  
               !!!parse-error;  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
5915              } else {              } else {
5916                  !!!cp ('t227.1');
5917                #                #
5918              }              }
5919            } else {            } else {
5920                !!!cp ('t227.4');
5921              #              #
5922            }            }
5923                      } else {
5924            ## As if in table            !!!cp ('t227');
5925            !!!parse-error;            #
5926            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($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]+)//) {  
               $open_elements->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
5927    
5928              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table', text => $token->{tag_name},
5929              ## 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,  
                 }->{$open_elements->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#$open_elements) {  
                 if ($open_elements->[$_]->[1] eq 'table') {  
                   my $parent = $open_elements->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $open_elements->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $open_elements->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $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 {  
               $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});  
             $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,  
               }->{$open_elements->[-1]->[1]}) {  
                 !!!parse-error;  
                 pop @$open_elements;  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $insertion_mode = 'in cell';  
5930    
5931                push @$active_formatting_elements, ['#marker', ''];          $insert = $insert_to_foster;
5932                          #
5933                !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
5934                redo B;              if ($token->{tag_name} eq 'tr' and
5935              } 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>  
5936                ## have an element in table scope                ## have an element in table scope
5937                my $i;                my $i;
5938                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5939                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
5940                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5941                      !!!cp ('t228');
5942                    $i = $_;                    $i = $_;
5943                    last INSCOPE;                    last INSCOPE;
5944                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5945                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5946                    last INSCOPE;                    last INSCOPE;
5947                  }                  }
5948                } # INSCOPE                } # INSCOPE
5949                unless (defined $i) {                unless (defined $i) {
5950                  !!!parse-error;                  !!!cp ('t230');
5951                    !!!parse-error (type => 'unmatched end tag',
5952                                    text => $token->{tag_name}, token => $token);
5953                  ## Ignore the token                  ## Ignore the token
5954                    !!!nack ('t230.1');
5955                  !!!next-token;                  !!!next-token;
5956                  redo B;                  next B;
5957                  } else {
5958                    !!!cp ('t232');
5959                }                }
5960    
5961                ## Clear back to table row context                ## Clear back to table row context
5962                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5963                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5964                }->{$open_elements->[-1]->[1]}) {                  !!!cp ('t231');
5965                  !!!parse-error;  ## ISSUE: Can this state be reached?
5966                  pop @$open_elements;                  pop @{$self->{open_elements}};
5967                }                }
5968    
5969                pop @$open_elements; # tr                pop @{$self->{open_elements}}; # tr
5970                $insertion_mode = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5971                ## reprocess                !!!next-token;
5972                redo B;                !!!nack ('t231.1');
5973                  next B;
5974              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5975                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
5976                !!!parse-error;                  ## As if </tr>
5977                    ## have an element in table scope
5978                ## As if </table>                  my $i;
5979                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5980                my $i;                    my $node = $self->{open_elements}->[$_];
5981                INSCOPE: for (reverse 0..$#$open_elements) {                    if ($node->[1] & TABLE_ROW_EL) {
5982                  my $node = $open_elements->[$_];                      !!!cp ('t233');
5983                  if ($node->[1] eq 'table') {                      $i = $_;
5984                    $i = $_;                      last INSCOPE;
5985                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5986                  } elsif ({                      !!!cp ('t234');
5987                            table => 1, html => 1,                      last INSCOPE;
5988                           }->{$node->[1]}) {                    }
5989                    last INSCOPE;                  } # INSCOPE
5990                    unless (defined $i) {
5991                      !!!cp ('t235');
5992    ## TODO: The following is wrong.
5993                      !!!parse-error (type => 'unmatched end tag',
5994                                      text => $token->{type}, token => $token);
5995                      ## Ignore the token
5996                      !!!nack ('t236.1');
5997                      !!!next-token;
5998                      next B;
5999                  }                  }
6000                } # INSCOPE                  
6001                unless (defined $i) {                  ## Clear back to table row context
6002                  !!!parse-error;                  while (not ($self->{open_elements}->[-1]->[1]
6003                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
6004                  !!!next-token;                    !!!cp ('t236');
6005                  redo B;  ## ISSUE: Can this state be reached?
6006                }                    pop @{$self->{open_elements}};
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$open_elements->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $open_elements->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($open_elements->[-1]->[1] ne 'table') {  
                 !!!parse-error;  
               }  
   
               splice @$open_elements, $i;  
   
               $reset_insertion_mode->();                
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#$open_elements) {  
                 my $node = $open_elements->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
6007                  }                  }
6008                } # INSCOPE                  
6009                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
6010                  !!!parse-error;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6011                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
6012                  !!!next-token;                }
6013                  redo B;  
6014                }                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6015                    ## have an element in table scope
6016                ## Clear back to table row context                  my $i;
6017                while (not {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6018                  tr => 1, html => 1,                    my $node = $self->{open_elements}->[$_];
6019                }->{$open_elements->[-1]->[1]}) {                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6020                  !!!parse-error;                      !!!cp ('t237');
6021                  pop @$open_elements;                      $i = $_;
6022                }                      last INSCOPE;
6023                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6024                        !!!cp ('t238');
6025                        last INSCOPE;
6026                      }
6027                    } # INSCOPE
6028                    unless (defined $i) {
6029                      !!!cp ('t239');
6030                      !!!parse-error (type => 'unmatched end tag',
6031                                      text => $token->{tag_name}, token => $token);
6032                      ## Ignore the token
6033                      !!!nack ('t239.1');
6034                      !!!next-token;
6035                      next B;
6036                    }
6037                    
6038                    ## Clear back to table body context
6039                    while (not ($self->{open_elements}->[-1]->[1]
6040                                    & TABLE_ROWS_SCOPING_EL)) {
6041                      !!!cp ('t240');
6042                      pop @{$self->{open_elements}};
6043                    }
6044                    
6045                    ## As if <{current node}>
6046                    ## have an element in table scope
6047                    ## true by definition
6048                    
6049                    ## Clear back to table body context
6050                    ## nop by definition
6051                    
6052                    pop @{$self->{open_elements}};
6053                    $self->{insertion_mode} = IN_TABLE_IM;
6054                    ## reprocess in the "in table" insertion mode...
6055                  }
6056    
6057                  ## NOTE: </table> in the "in table" insertion mode.
6058                  ## When you edit the code fragment below, please ensure that
6059                  ## the code for <table> in the "in table" insertion mode
6060                  ## is synced with it.
6061    
6062                pop @$open_elements; # tr                ## have a table element in table scope
               $insertion_mode = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </tr>  
               ## have an element in table scope  
6063                my $i;                my $i;
6064                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6065                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
6066                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_EL) {
6067                      !!!cp ('t241');
6068                    $i = $_;                    $i = $_;
6069                    last INSCOPE;                    last INSCOPE;
6070                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6071                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6072                    last INSCOPE;                    last INSCOPE;
6073                  }                  }
6074                } # INSCOPE                } # INSCOPE
6075                unless (defined $i) {                unless (defined $i) {
6076                  !!!parse-error;                  !!!cp ('t243');
6077                    !!!parse-error (type => 'unmatched end tag',
6078                                    text => $token->{tag_name}, token => $token);
6079                  ## Ignore the token                  ## Ignore the token
6080                    !!!nack ('t243.1');
6081                  !!!next-token;                  !!!next-token;
6082                  redo B;                  next B;
6083                }                }
6084                    
6085                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6086                while (not {                pop @{$open_tables};
6087                  tr => 1, html => 1,                
6088                }->{$open_elements->[-1]->[1]}) {                $self->_reset_insertion_mode;
6089                  !!!parse-error;                
6090                  pop @$open_elements;                !!!next-token;
6091                }                next B;
   
               pop @$open_elements; # tr  
               $insertion_mode = 'in table body';  
               ## reprocess  
               redo B;  
6092              } elsif ({              } elsif ({
6093                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6094                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}} and
6095                ## have an element in table scope                       $self->{insertion_mode} & ROW_IMS) {
6096                my $i;                if ($self->{insertion_mode} == IN_ROW_IM) {
6097                INSCOPE: for (reverse 0..$#$open_elements) {                  ## have an element in table scope
6098                  my $node = $open_elements->[$_];                  my $i;
6099                  if ($node->[1] eq $token->{tag_name}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6100                    $i = $_;                    my $node = $self->{open_elements}->[$_];
6101                    last INSCOPE;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6102                  } elsif ({                      !!!cp ('t247');
6103                            table => 1, html => 1,                      $i = $_;
6104                           }->{$node->[1]}) {                      last INSCOPE;
6105                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6106                        !!!cp ('t248');
6107                        last INSCOPE;
6108                      }
6109                    } # INSCOPE
6110                      unless (defined $i) {
6111                        !!!cp ('t249');
6112                        !!!parse-error (type => 'unmatched end tag',
6113                                        text => $token->{tag_name}, token => $token);
6114                        ## Ignore the token
6115                        !!!nack ('t249.1');
6116                        !!!next-token;
6117                        next B;
6118                      }
6119                    
6120                    ## As if </tr>
6121                    ## have an element in table scope
6122                    my $i;
6123                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6124                      my $node = $self->{open_elements}->[$_];
6125                      if ($node->[1] & TABLE_ROW_EL) {
6126                        !!!cp ('t250');
6127                        $i = $_;
6128                        last INSCOPE;
6129                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6130                        !!!cp ('t251');
6131                        last INSCOPE;
6132                      }
6133                    } # INSCOPE
6134                      unless (defined $i) {
6135                        !!!cp ('t252');
6136                        !!!parse-error (type => 'unmatched end tag',
6137                                        text => 'tr', token => $token);
6138                        ## Ignore the token
6139                        !!!nack ('t252.1');
6140                        !!!next-token;
6141                        next B;
6142                      }
6143                    
6144                    ## Clear back to table row context
6145                    while (not ($self->{open_elements}->[-1]->[1]
6146                                    & TABLE_ROW_SCOPING_EL)) {
6147                      !!!cp ('t253');
6148    ## ISSUE: Can this case be reached?
6149                      pop @{$self->{open_elements}};
6150                  }                  }
6151                } # INSCOPE                  
6152                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
6153                  !!!parse-error;                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6154                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
                 !!!next-token;  
                 redo B;  
6155                }                }
6156    
               ## As if </tr>  
6157                ## have an element in table scope                ## have an element in table scope
6158                my $i;                my $i;
6159                INSCOPE: for (reverse 0..$#$open_elements) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6160                  my $node = $open_elements->[$_];                  my $node = $self->{open_elements}->[$_];
6161                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6162                      !!!cp ('t254');
6163                    $i = $_;                    $i = $_;
6164                    last INSCOPE;                    last INSCOPE;
6165                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6166                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6167                    last INSCOPE;                    last INSCOPE;
6168                  }                  }
6169                } # INSCOPE                } # INSCOPE
6170                unless (defined $i) {                unless (defined $i) {
6171                  !!!parse-error;                  !!!cp ('t256');
6172                    !!!parse-error (type => 'unmatched end tag',
6173                                    text => $token->{tag_name}, token => $token);
6174                  ## Ignore the token                  ## Ignore the token
6175                    !!!nack ('t256.1');
6176                  !!!next-token;                  !!!next-token;
6177                  redo B;                  next B;
6178                }                }
6179    
6180                ## Clear back to table row context                ## Clear back to table body context
6181                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6182                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6183                }->{$open_elements->[-1]->[1]}) {                  !!!cp ('t257');
6184                  !!!parse-error;  ## ISSUE: Can this case be reached?
6185                  pop @$open_elements;                  pop @{$self->{open_elements}};
6186                }                }
6187    
6188                pop @$open_elements; # tr                pop @{$self->{open_elements}};
6189                $insertion_mode = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6190                ## reprocess                !!!nack ('t257.1');
6191                redo B;                !!!next-token;
6192                  next B;
6193              } elsif ({              } elsif ({
6194                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6195                        colgroup => 1, html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6196                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6197                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6198                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6199                !!!parse-error;            !!!cp ('t258');
6200                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6201                !!!next-token;                            text => $token->{tag_name}, token => $token);
6202                redo B;            ## Ignore the token
6203              } else {            !!!nack ('t258.1');
6204                #             !!!next-token;
6205              }            next B;
6206            } else {          } else {
6207              #            !!!cp ('t259');
6208            }            !!!parse-error (type => 'in table:/',
6209                              text => $token->{tag_name}, token => $token);
6210    
6211            ## As if in table            $insert = $insert_to_foster;
6212            !!!parse-error;            #
6213            $in_body->($insert_to_foster);          }
6214            redo B;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6215          } elsif ($insertion_mode eq 'in cell') {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6216            if ($token->{type} eq 'character') {                  @{$self->{open_elements}} == 1) { # redundant, maybe
6217              ## NOTE: This is a code clone of "character in body".            !!!parse-error (type => 'in body:#eof', token => $token);
6218              $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t259.1');
6219                          #
6220              $open_elements->[-1]->[0]->manakai_append_text ($token->{data});          } else {
6221              !!!cp ('t259.2');
6222              #
6223            }
6224    
6225              !!!next-token;          ## Stop parsing
6226              redo B;          last B;
6227            } elsif ($token->{type} eq 'comment') {        } else {
6228              ## NOTE: This is a code clone of "comment in body".          die "$0: $token->{type}: Unknown token type";
6229              my $comment = $self->{document}->create_comment ($token->{data});        }
6230              $open_elements->[-1]->[0]->append_child ($comment);      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6231              !!!next-token;            if ($token->{type} == CHARACTER_TOKEN) {
6232              redo B;              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6233            } elsif ($token->{type} eq 'start tag') {                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6234              if ({                unless (length $token->{data}) {
6235                   caption => 1, col => 1, colgroup => 1,                  !!!cp ('t260');
                  tbody => 1, td => 1, tfoot => 1, th => 1,  
                  thead => 1, tr => 1,  
                 }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $tn;  
               INSCOPE: for (reverse 0..$#$open_elements) {  
                 my $node = $open_elements->[$_];  
                 if ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $tn) {  
                 !!!parse-error;  
                 ## Ignore the token  
6236                  !!!next-token;                  !!!next-token;
6237                  redo B;                  next B;
6238                }                }
6239                }
6240                ## Close the cell              
6241                !!!back-token; # <?>              !!!cp ('t261');
6242                $token = {type => 'end tag', tag_name => $tn};              #
6243                redo B;            } elsif ($token->{type} == START_TAG_TOKEN) {
6244              } else {              if ($token->{tag_name} eq 'col') {
6245                  !!!cp ('t262');
6246                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6247                  pop @{$self->{open_elements}};
6248                  !!!ack ('t262.1');
6249                  !!!next-token;
6250                  next B;
6251                } else {
6252                  !!!cp ('t263');
6253                #                #
6254              }              }
6255            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
6256              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'colgroup') {
6257                ## have an element in table scope                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6258                my $i;                  !!!cp ('t264');
6259                INSCOPE: for (reverse 0..$#$open_elements) {                  !!!parse-error (type => 'unmatched end tag',
6260                  my $node = $open_elements->[$_];                                  text => 'colgroup', 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;  
6261                  ## Ignore the token                  ## Ignore the token
6262                  !!!next-token;                  !!!next-token;
6263                  redo B;                  next B;
6264                }                } else {
6265                                  !!!cp ('t265');
6266                ## generate implied end tags                  pop @{$self->{open_elements}}; # colgroup
6267                if ({                  $self->{insertion_mode} = IN_TABLE_IM;
6268                     dd => 1, dt => 1, li => 1, p => 1,                  !!!next-token;
6269                     td => ($token->{tag_name} eq 'th'),                  next B;            
                    th => ($token->{tag_name} eq 'td'),  
                    tr => 1,  
                   }->{$open_elements->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $open_elements->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($open_elements->[-1]->[1] ne $token->{tag_name}) {  
                 !!!parse-error;  
6270                }                }
6271                } elsif ($token->{tag_name} eq 'col') {
6272                splice @$open_elements, $i;                !!!cp ('t266');
6273                  !!!parse-error (type => 'unmatched end tag',
6274                $clear_up_to_marker->();                                text => 'col', token => $token);
   
               $insertion_mode = 'in row';  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error;  
6275                ## Ignore the token                ## Ignore the token
6276                !!!next-token;                !!!next-token;
6277                redo B;                next B;
             } elsif ({  
                       table => 1, tbody => 1, tfoot => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $i;  
               my $tn;  
               INSCOPE: for (reverse 0..$#$open_elements) {  
                 my $node = $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;  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
6278              } else {              } else {
6279                #                !!!cp ('t267');
6280                  #
6281              }              }
6282          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6283            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6284                @{$self->{open_elements}} == 1) { # redundant, maybe
6285              !!!cp ('t270.2');
6286              ## Stop parsing.
6287              last B;
6288            } else {
6289              ## NOTE: As if </colgroup>.
6290              !!!cp ('t270.1');
6291              pop @{$self->{open_elements}}; # colgroup
6292              $self->{insertion_mode} = IN_TABLE_IM;
6293              ## Reprocess.
6294              next B;
6295            }
6296          } else {
6297            die "$0: $token->{type}: Unknown token type";
6298          }
6299    
6300              ## As if </colgroup>
6301              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6302                !!!cp ('t269');
6303    ## TODO: Wrong error type?
6304                !!!parse-error (type => 'unmatched end tag',
6305                                text => 'colgroup', token => $token);
6306                ## Ignore the token
6307                !!!nack ('t269.1');
6308                !!!next-token;
6309                next B;
6310            } else {            } else {
6311              #              !!!cp ('t270');
6312                pop @{$self->{open_elements}}; # colgroup
6313                $self->{insertion_mode} = IN_TABLE_IM;
6314                !!!ack-later;
6315                ## reprocess
6316                next B;
6317              }
6318        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6319          if ($token->{type} == CHARACTER_TOKEN) {
6320            !!!cp ('t271');
6321            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6322            !!!next-token;
6323            next B;
6324          } elsif ($token->{type} == START_TAG_TOKEN) {
6325            if ($token->{tag_name} eq 'option') {
6326              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6327                !!!cp ('t272');
6328                ## As if </option>
6329                pop @{$self->{open_elements}};
6330              } else {
6331                !!!cp ('t273');
6332            }            }
             
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($insertion_mode eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $open_elements->[-1]->[0]->manakai_append_text ($token->{data});  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $open_elements->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'option') {  
               if ($open_elements->[-1]->[1] eq 'option') {  
                 ## As if </option>  
                 pop @$open_elements;  
               }  
6333    
6334                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6335                !!!next-token;            !!!nack ('t273.1');
6336                redo B;            !!!next-token;
6337              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6338                if ($open_elements->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6339                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6340                  pop @$open_elements;              !!!cp ('t274');
6341                }              ## As if </option>
6342                pop @{$self->{open_elements}};
6343              } else {
6344                !!!cp ('t275');
6345              }
6346    
6347                if ($open_elements->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6348                  ## As if </optgroup>              !!!cp ('t276');
6349                  pop @$open_elements;              ## As if </optgroup>
6350                }              pop @{$self->{open_elements}};
6351              } else {
6352                !!!cp ('t277');
6353              }
6354    
6355                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6356                !!!next-token;            !!!nack ('t277.1');
6357                redo B;            !!!next-token;
6358              } elsif ($token->{tag_name} eq 'select') {            next B;
6359                !!!parse-error;          } elsif ({
6360                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6361                ## have an element in table scope                   }->{$token->{tag_name}} or
6362                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6363                INSCOPE: for (reverse 0..$#$open_elements) {                    {
6364                  my $node = $open_elements->[$_];                     caption => 1, table => 1,
6365                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6366                    $i = $_;                     tr => 1, td => 1, th => 1,
6367                    last INSCOPE;                    }->{$token->{tag_name}})) {
6368                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6369                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6370                           }->{$node->[1]}) {                            token => $token);
6371                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6372                  }            ## as if there were </select> (otherwise).
6373                } # INSCOPE            ## have an element in table scope
6374                unless (defined $i) {            my $i;
6375                  !!!parse-error;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6376                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6377                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6378                  redo B;                !!!cp ('t278');
6379                }                $i = $_;
6380                  last INSCOPE;
6381                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6382                  !!!cp ('t279');
6383                  last INSCOPE;
6384                }
6385              } # INSCOPE
6386              unless (defined $i) {
6387                !!!cp ('t280');
6388                !!!parse-error (type => 'unmatched end tag',
6389                                text => 'select', token => $token);
6390                ## Ignore the token
6391                !!!nack ('t280.1');
6392                !!!next-token;
6393                next B;
6394              }
6395                                
6396                splice @$open_elements, $i;            !!!cp ('t281');
6397              splice @{$self->{open_elements}}, $i;
6398    
6399                $reset_insertion_mode->();            $self->_reset_insertion_mode;
6400    
6401                !!!next-token;            if ($token->{tag_name} eq 'select') {
6402                redo B;              !!!nack ('t281.2');
6403              } else {              !!!next-token;
6404                #              next B;
6405              } else {
6406                !!!cp ('t281.1');
6407                !!!ack-later;
6408                ## Reprocess the token.
6409                next B;
6410              }
6411            } else {
6412              !!!cp ('t282');
6413              !!!parse-error (type => 'in select',
6414                              text => $token->{tag_name}, token => $token);
6415              ## Ignore the token
6416              !!!nack ('t282.1');
6417              !!!next-token;
6418              next B;
6419            }
6420          } elsif ($token->{type} == END_TAG_TOKEN) {
6421            if ($token->{tag_name} eq 'optgroup') {
6422              if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6423                  $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6424                !!!cp ('t283');
6425                ## As if </option>
6426                splice @{$self->{open_elements}}, -2;
6427              } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6428                !!!cp ('t284');
6429                pop @{$self->{open_elements}};
6430              } else {
6431                !!!cp ('t285');
6432                !!!parse-error (type => 'unmatched end tag',
6433                                text => $token->{tag_name}, token => $token);
6434                ## Ignore the token
6435              }
6436              !!!nack ('t285.1');
6437              !!!next-token;
6438              next B;
6439            } elsif ($token->{tag_name} eq 'option') {
6440              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6441                !!!cp ('t286');
6442                pop @{$self->{open_elements}};
6443              } else {
6444                !!!cp ('t287');
6445                !!!parse-error (type => 'unmatched end tag',
6446                                text => $token->{tag_name}, token => $token);
6447                ## Ignore the token
6448              }
6449              !!!nack ('t287.1');
6450              !!!next-token;
6451              next B;
6452            } elsif ($token->{tag_name} eq 'select') {
6453              ## have an element in table scope
6454              my $i;
6455              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6456                my $node = $self->{open_elements}->[$_];
6457                if ($node->[1] & SELECT_EL) {
6458                  !!!cp ('t288');
6459                  $i = $_;
6460                  last INSCOPE;
6461                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6462                  !!!cp ('t289');
6463                  last INSCOPE;
6464              }              }
6465            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6466              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
6467                if ($open_elements->[-1]->[1] eq 'option' and              !!!cp ('t290');
6468                    $open_elements->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag',
6469                  ## As if </option>                              text => $token->{tag_name}, token => $token);
6470                  splice @$open_elements, -2;              ## Ignore the token
6471                } elsif ($open_elements->[-1]->[1] eq 'optgroup') {              !!!nack ('t290.1');
6472                  pop @$open_elements;              !!!next-token;
6473                } else {              next B;
6474                  !!!parse-error;            }
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'option') {  
               if ($open_elements->[-1]->[1] eq 'option') {  
                 pop @$open_elements;  
               } else {  
                 !!!parse-error;  
                 ## 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..$#$open_elements) {  
                 my $node = $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;  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6475                                
6476                splice @$open_elements, $i;            !!!cp ('t291');
6477              splice @{$self->{open_elements}}, $i;
6478    
6479                $reset_insertion_mode->();            $self->_reset_insertion_mode;
6480    
6481                !!!next-token;            !!!nack ('t291.1');
6482                redo B;            !!!next-token;
6483              } elsif ({            next B;
6484                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6485                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6486                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6487                !!!parse-error;                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6488                     }->{$token->{tag_name}}) {
6489    ## TODO: The following is wrong?
6490              !!!parse-error (type => 'unmatched end tag',
6491                              text => $token->{tag_name}, token => $token);
6492                                
6493                ## have an element in table scope            ## have an element in table scope
6494                my $i;            my $i;
6495                INSCOPE: for (reverse 0..$#$open_elements) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6496                  my $node = $open_elements->[$_];              my $node = $self->{open_elements}->[$_];
6497                  if ($node->[1] eq $token->{tag_name}) {              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6498                    $i = $_;                !!!cp ('t292');
6499                    last INSCOPE;                $i = $_;
6500                  } elsif ({                last INSCOPE;
6501                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6502                           }->{$node->[1]}) {                !!!cp ('t293');
6503                    last INSCOPE;                last INSCOPE;
6504                  }              }
6505                } # INSCOPE            } # INSCOPE
6506                unless (defined $i) {            unless (defined $i) {
6507                  ## Ignore the token              !!!cp ('t294');
6508                  !!!next-token;              ## Ignore the token
6509                  redo B;              !!!nack ('t294.1');
6510                }              !!!next-token;
6511                next B;
6512              }
6513                                
6514                ## As if </select>            ## As if </select>
6515                ## have an element in table scope            ## have an element in table scope
6516                undef $i;            undef $i;
6517                INSCOPE: for (reverse 0..$#$open_elements) {            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6518                  my $node = $open_elements->[$_];              my $node = $self->{open_elements}->[$_];
6519                  if ($node->[1] eq 'select') {              if ($node->[1] & SELECT_EL) {
6520                    $i = $_;                !!!cp ('t295');
6521                    last INSCOPE;                $i = $_;
6522                  } elsif ({                last INSCOPE;
6523                            table => 1, html => 1,              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6524                           }->{$node->[1]}) {  ## ISSUE: Can this state be reached?
6525                    last INSCOPE;                !!!cp ('t296');
6526                  }                last INSCOPE;
6527                } # INSCOPE              }
6528                unless (defined $i) {            } # INSCOPE
6529                  !!!parse-error;            unless (defined $i) {
6530                  ## Ignore the </select> token              !!!cp ('t297');
6531                  !!!next-token; ## TODO: ok?  ## TODO: The following error type is correct?
6532                  redo B;              !!!parse-error (type => 'unmatched end tag',
6533                }                              text => 'select', token => $token);
6534                ## Ignore the </select> token
6535                !!!nack ('t297.1');
6536                !!!next-token; ## TODO: ok?
6537                next B;
6538              }
6539                                
6540                splice @$open_elements, $i;            !!!cp ('t298');
6541              splice @{$self->{open_elements}}, $i;
6542    
6543                $reset_insertion_mode->();            $self->_reset_insertion_mode;
6544    
6545                ## reprocess            !!!ack-later;
6546                redo B;            ## reprocess
6547              } else {            next B;
6548                #          } else {
6549              }            !!!cp ('t299');
6550              !!!parse-error (type => 'in select:/',
6551                              text => $token->{tag_name}, token => $token);
6552              ## Ignore the token
6553              !!!nack ('t299.3');
6554              !!!next-token;
6555              next B;
6556            }
6557          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6558            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6559                    @{$self->{open_elements}} == 1) { # redundant, maybe
6560              !!!cp ('t299.1');
6561              !!!parse-error (type => 'in body:#eof', token => $token);
6562            } else {
6563              !!!cp ('t299.2');
6564            }
6565    
6566            ## Stop parsing.
6567            last B;
6568          } else {
6569            die "$0: $token->{type}: Unknown token type";
6570          }
6571        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6572          if ($token->{type} == CHARACTER_TOKEN) {
6573            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6574              my $data = $1;
6575              ## As if in body
6576              $reconstruct_active_formatting_elements->($insert_to_current);
6577                  
6578              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6579              
6580              unless (length $token->{data}) {
6581                !!!cp ('t300');
6582                !!!next-token;
6583                next B;
6584              }
6585            }
6586            
6587            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6588              !!!cp ('t301');
6589              !!!parse-error (type => 'after html:#text', token => $token);
6590              #
6591            } else {
6592              !!!cp ('t302');
6593              ## "after body" insertion mode
6594              !!!parse-error (type => 'after body:#text', token => $token);
6595              #
6596            }
6597    
6598            $self->{insertion_mode} = IN_BODY_IM;
6599            ## reprocess
6600            next B;
6601          } elsif ($token->{type} == START_TAG_TOKEN) {
6602            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6603              !!!cp ('t303');
6604              !!!parse-error (type => 'after html',
6605                              text => $token->{tag_name}, token => $token);
6606              #
6607            } else {
6608              !!!cp ('t304');
6609              ## "after body" insertion mode
6610              !!!parse-error (type => 'after body',
6611                              text => $token->{tag_name}, token => $token);
6612              #
6613            }
6614    
6615            $self->{insertion_mode} = IN_BODY_IM;
6616            !!!ack-later;
6617            ## reprocess
6618            next B;
6619          } elsif ($token->{type} == END_TAG_TOKEN) {
6620            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6621              !!!cp ('t305');
6622              !!!parse-error (type => 'after html:/',
6623                              text => $token->{tag_name}, token => $token);
6624              
6625              $self->{insertion_mode} = IN_BODY_IM;
6626              ## Reprocess.
6627              next B;
6628            } else {
6629              !!!cp ('t306');
6630            }
6631    
6632            ## "after body" insertion mode
6633            if ($token->{tag_name} eq 'html') {
6634              if (defined $self->{inner_html_node}) {
6635                !!!cp ('t307');
6636                !!!parse-error (type => 'unmatched end tag',
6637                                text => 'html', token => $token);
6638                ## Ignore the token
6639                !!!next-token;
6640                next B;
6641            } else {            } else {
6642              #              !!!cp ('t308');
6643                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6644                !!!next-token;
6645                next B;
6646            }            }
6647            } else {
6648              !!!cp ('t309');
6649              !!!parse-error (type => 'after body:/',
6650                              text => $token->{tag_name}, token => $token);
6651    
6652            !!!parse-error;            $self->{insertion_mode} = IN_BODY_IM;
6653              ## reprocess
6654              next B;
6655            }
6656          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6657            !!!cp ('t309.2');
6658            ## Stop parsing
6659            last B;
6660          } else {
6661            die "$0: $token->{type}: Unknown token type";
6662          }
6663        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6664          if ($token->{type} == CHARACTER_TOKEN) {
6665            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6666              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6667              
6668              unless (length $token->{data}) {
6669                !!!cp ('t310');
6670                !!!next-token;
6671                next B;
6672              }
6673            }
6674            
6675            if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6676              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6677                !!!cp ('t311');
6678                !!!parse-error (type => 'in frameset:#text', token => $token);
6679              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6680                !!!cp ('t312');
6681                !!!parse-error (type => 'after frameset:#text', token => $token);
6682              } else { # "after after frameset"
6683                !!!cp ('t313');
6684                !!!parse-error (type => 'after html:#text', token => $token);
6685              }
6686              
6687              ## Ignore the token.
6688              if (length $token->{data}) {
6689                !!!cp ('t314');
6690                ## reprocess the rest of characters
6691              } else {
6692                !!!cp ('t315');
6693                !!!next-token;
6694              }
6695              next B;
6696            }
6697            
6698            die qq[$0: Character "$token->{data}"];
6699          } elsif ($token->{type} == START_TAG_TOKEN) {
6700            if ($token->{tag_name} eq 'frameset' and
6701                $self->{insertion_mode} == IN_FRAMESET_IM) {
6702              !!!cp ('t318');
6703              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6704              !!!nack ('t318.1');
6705              !!!next-token;
6706              next B;
6707            } elsif ($token->{tag_name} eq 'frame' and
6708                     $self->{insertion_mode} == IN_FRAMESET_IM) {
6709              !!!cp ('t319');
6710              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6711              pop @{$self->{open_elements}};
6712              !!!ack ('t319.1');
6713              !!!next-token;
6714              next B;
6715            } elsif ($token->{tag_name} eq 'noframes') {
6716              !!!cp ('t320');
6717              ## NOTE: As if in head.
6718              $parse_rcdata->(CDATA_CONTENT_MODEL);
6719              next B;
6720    
6721              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6722              ## has no parse error.
6723            } else {
6724              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6725                !!!cp ('t321');
6726                !!!parse-error (type => 'in frameset',
6727                                text => $token->{tag_name}, token => $token);
6728              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6729                !!!cp ('t322');
6730                !!!parse-error (type => 'after frameset',
6731                                text => $token->{tag_name}, token => $token);
6732              } else { # "after after frameset"
6733                !!!cp ('t322.2');
6734                !!!parse-error (type => 'after after frameset',
6735                                text => $token->{tag_name}, token => $token);
6736              }
6737            ## Ignore the token            ## Ignore the token
6738              !!!nack ('t322.1');
6739            !!!next-token;            !!!next-token;
6740            redo B;            next B;
6741          } elsif ($insertion_mode eq 'after body') {          }
6742            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_TAG_TOKEN) {
6743              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{tag_name} eq 'frameset' and
6744                ## As if in body              $self->{insertion_mode} == IN_FRAMESET_IM) {
6745                $reconstruct_active_formatting_elements->($insert_to_current);            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6746                                @{$self->{open_elements}} == 1) {
6747                $open_elements->[-1]->[0]->manakai_append_text ($token->{data});              !!!cp ('t325');
6748                !!!parse-error (type => 'unmatched end tag',
6749                                text => $token->{tag_name}, token => $token);
6750                ## Ignore the token
6751                !!!next-token;
6752              } else {
6753                !!!cp ('t326');
6754                pop @{$self->{open_elements}};
6755                !!!next-token;
6756              }
6757    
6758                unless (length $token->{data}) {            if (not defined $self->{inner_html_node} and
6759                  !!!next-token;                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6760                  redo B;              !!!cp ('t327');
6761                }              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6762              }            } else {
6763                !!!cp ('t328');
6764              }
6765              next B;
6766            } elsif ($token->{tag_name} eq 'html' and
6767                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6768              !!!cp ('t329');
6769              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6770              !!!next-token;
6771              next B;
6772            } else {
6773              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6774                !!!cp ('t330');
6775                !!!parse-error (type => 'in frameset:/',
6776                                text => $token->{tag_name}, token => $token);
6777              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6778                !!!cp ('t330.1');
6779                !!!parse-error (type => 'after frameset:/',
6780                                text => $token->{tag_name}, token => $token);
6781              } else { # "after after html"
6782                !!!cp ('t331');
6783                !!!parse-error (type => 'after after frameset:/',
6784                                text => $token->{tag_name}, token => $token);
6785              }
6786              ## Ignore the token
6787              !!!next-token;
6788              next B;
6789            }
6790          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6791            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6792                    @{$self->{open_elements}} == 1) { # redundant, maybe
6793              !!!cp ('t331.1');
6794              !!!parse-error (type => 'in body:#eof', token => $token);
6795            } else {
6796              !!!cp ('t331.2');
6797            }
6798            
6799            ## Stop parsing
6800            last B;
6801          } else {
6802            die "$0: $token->{type}: Unknown token type";
6803          }
6804    
6805          ## ISSUE: An issue in spec here
6806        } else {
6807          die "$0: $self->{insertion_mode}: Unknown insertion mode";
6808        }
6809    
6810        ## "in body" insertion mode
6811        if ($token->{type} == START_TAG_TOKEN) {
6812          if ($token->{tag_name} eq 'script') {
6813            !!!cp ('t332');
6814            ## NOTE: This is an "as if in head" code clone
6815            $script_start_tag->();
6816            next B;
6817          } elsif ($token->{tag_name} eq 'style') {
6818            !!!cp ('t333');
6819            ## NOTE: This is an "as if in head" code clone
6820            $parse_rcdata->(CDATA_CONTENT_MODEL);
6821            next B;
6822          } elsif ({
6823                    base => 1, command => 1, eventsource => 1, link => 1,
6824                   }->{$token->{tag_name}}) {
6825            !!!cp ('t334');
6826            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6827            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6828            pop @{$self->{open_elements}};
6829            !!!ack ('t334.1');
6830            !!!next-token;
6831            next B;
6832          } elsif ($token->{tag_name} eq 'meta') {
6833            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6834            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6835            my $meta_el = pop @{$self->{open_elements}};
6836    
6837            unless ($self->{confident}) {
6838              if ($token->{attributes}->{charset}) {
6839                !!!cp ('t335');
6840                ## NOTE: Whether the encoding is supported or not is handled
6841                ## in the {change_encoding} callback.
6842                $self->{change_encoding}
6843                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6844                            
6845              #              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6846            } elsif ($token->{type} eq 'comment') {                  ->set_user_data (manakai_has_reference =>
6847              my $comment = $self->{document}->create_comment ($token->{data});                                       $token->{attributes}->{charset}
6848              $open_elements->[0]->[0]->append_child ($comment);                                           ->{has_reference});
6849              !!!next-token;            } elsif ($token->{attributes}->{content}) {
6850              redo B;              if ($token->{attributes}->{content}->{value}
6851            } elsif ($token->{type} eq 'end tag') {                  =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6852              if ($token->{tag_name} eq 'html') {                      [\x09\x0A\x0C\x0D\x20]*=
6853                ## TODO: if inner_html, parse-error, ignore the token; otherwise,                      [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6854                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6855                       /x) {
6856                  !!!cp ('t336');
6857                  ## NOTE: Whether the encoding is supported or not is handled
6858                  ## in the {change_encoding} callback.
6859                  $self->{change_encoding}
6860                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6861                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6862                      ->set_user_data (manakai_has_reference =>
6863                                           $token->{attributes}->{content}
6864                                                 ->{has_reference});
6865                }
6866              }
6867            } else {
6868              if ($token->{attributes}->{charset}) {
6869                !!!cp ('t337');
6870                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6871                    ->set_user_data (manakai_has_reference =>
6872                                         $token->{attributes}->{charset}
6873                                             ->{has_reference});
6874              }
6875              if ($token->{attributes}->{content}) {
6876                !!!cp ('t338');
6877                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6878                    ->set_user_data (manakai_has_reference =>
6879                                         $token->{attributes}->{content}
6880                                             ->{has_reference});
6881              }
6882            }
6883    
6884                $phase = 'trailing end';          !!!ack ('t338.1');
6885            !!!next-token;
6886            next B;
6887          } elsif ($token->{tag_name} eq 'title') {
6888            !!!cp ('t341');
6889            ## NOTE: This is an "as if in head" code clone
6890            $parse_rcdata->(RCDATA_CONTENT_MODEL);
6891            next B;
6892          } elsif ($token->{tag_name} eq 'body') {
6893            !!!parse-error (type => 'in body', text => 'body', token => $token);
6894                  
6895            if (@{$self->{open_elements}} == 1 or
6896                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6897              !!!cp ('t342');
6898              ## Ignore the token
6899            } else {
6900              my $body_el = $self->{open_elements}->[1]->[0];
6901              for my $attr_name (keys %{$token->{attributes}}) {
6902                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6903                  !!!cp ('t343');
6904                  $body_el->set_attribute_ns
6905                    (undef, [undef, $attr_name],
6906                     $token->{attributes}->{$attr_name}->{value});
6907                }
6908              }
6909            }
6910            !!!nack ('t343.1');
6911            !!!next-token;
6912            next B;
6913          } elsif ({
6914                    address => 1, blockquote => 1, center => 1, dir => 1,
6915                    div => 1, dl => 1, fieldset => 1,
6916                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6917                    menu => 1, ol => 1, p => 1, ul => 1,
6918                    pre => 1, listing => 1,
6919                    form => 1,
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;  
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 ('data after body');            !!!nack ('t347.2');
6966            $insertion_mode = 'in body';            !!!next-token;
6967            ## reprocess          } elsif ($token->{tag_name} eq 'table') {
6968            redo B;            !!!cp ('t382');
6969          } elsif ($insertion_mode eq 'in frameset') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6970            if ($token->{type} eq 'character') {            
6971              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            $self->{insertion_mode} = IN_TABLE_IM;
               $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 ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6987            ## has a p element in scope
6988            INSCOPE: for (reverse @{$self->{open_elements}}) {
6989              if ($_->[1] & P_EL) {
6990                !!!cp ('t353');
6991                !!!back-token; # <x>
6992                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6993                          line => $token->{line}, column => $token->{column}};
6994                next B;
6995              } elsif ($_->[1] & SCOPING_EL) {
6996                !!!cp ('t354');
6997                last INSCOPE;
6998              }
6999            } # INSCOPE
7000    
7001              #          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7002            } elsif ($token->{type} eq 'comment') {            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7003              my $comment = $self->{document}->create_comment ($token->{data});            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7004              $open_elements->[-1]->[0]->append_child ($comment);            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7005              !!!next-token;            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7006              redo B;            ## object (Fx)
7007            } elsif ($token->{type} eq 'start tag') {            ## Generate non-tree (non-conforming)
7008              if ($token->{tag_name} eq 'frameset') {            ## basefont (IE7 (where basefont is non-void)), center (IE),
7009                !!!insert-element ($token->{tag_name}, $token->{attributes});            ## form (IE), hn (IE)
7010                !!!next-token;          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7011                redo B;            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7012              } elsif ($token->{tag_name} eq 'frame') {            ## div (Fx, S)
7013                !!!insert-element ($token->{tag_name}, $token->{attributes});            
7014                pop @$open_elements;          ## Step 1
7015                !!!next-token;          my $i = -1;
7016                redo B;          my $node = $self->{open_elements}->[$i];
7017              } elsif ($token->{tag_name} eq 'noframes') {          my $li_or_dtdd = {li => {li => 1},
7018                $in_body->($insert_to_current);                            dt => {dt => 1, dd => 1},
7019                redo B;                            dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
7020            LI: {
7021              ## Step 2
7022              if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
7023                if ($i != -1) {
7024                  !!!cp ('t355');
7025                  !!!parse-error (type => 'not closed',
7026                                  text => $self->{open_elements}->[-1]->[0]
7027                                      ->manakai_local_name,
7028                                  token => $token);
7029              } else {              } else {
7030                #                !!!cp ('t356');
7031              }              }
7032            } elsif ($token->{type} eq 'end tag') {              splice @{$self->{open_elements}}, $i;
7033              if ($token->{tag_name} eq 'frameset') {              last LI;
7034                if ($open_elements->[-1]->[1] eq 'html' and            } else {
7035                    @$open_elements == 1) {              !!!cp ('t357');
7036                  !!!parse-error;            }
7037                  ## Ignore the token            
7038                  !!!next-token;            ## Step 3
7039                } else {            if (not ($node->[1] & FORMATTING_EL) and
7040                  pop @$open_elements;                #not $phrasing_category->{$node->[1]} and
7041                  !!!next-token;                ($node->[1] & SPECIAL_EL or
7042                   $node->[1] & SCOPING_EL) and
7043                  not ($node->[1] & ADDRESS_EL) and
7044                  not ($node->[1] & DIV_EL)) {
7045                !!!cp ('t358');
7046                last LI;
7047              }
7048              
7049              !!!cp ('t359');
7050              ## Step 4
7051              $i--;
7052              $node = $self->{open_elements}->[$i];
7053              redo LI;
7054            } # LI
7055              
7056            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7057            !!!nack ('t359.1');
7058            !!!next-token;
7059            next B;
7060          } elsif ($token->{tag_name} eq 'plaintext') {
7061            ## has a p element in scope
7062            INSCOPE: for (reverse @{$self->{open_elements}}) {
7063              if ($_->[1] & P_EL) {
7064                !!!cp ('t367');
7065                !!!back-token; # <plaintext>
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 ('t368');
7071                last INSCOPE;
7072              }
7073            } # INSCOPE
7074              
7075            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7076              
7077            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7078              
7079            !!!nack ('t368.1');
7080            !!!next-token;
7081            next B;
7082          } elsif ($token->{tag_name} eq 'a') {
7083            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7084              my $node = $active_formatting_elements->[$i];
7085              if ($node->[1] & A_EL) {
7086                !!!cp ('t371');
7087                !!!parse-error (type => 'in a:a', token => $token);
7088                
7089                !!!back-token; # <a>
7090                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7091                          line => $token->{line}, column => $token->{column}};
7092                $formatting_end_tag->($token);
7093                
7094                AFE2: for (reverse 0..$#$active_formatting_elements) {
7095                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7096                    !!!cp ('t372');
7097                    splice @$active_formatting_elements, $_, 1;
7098                    last AFE2;
7099                }                }
7100                              } # AFE2
7101                ## if not inner_html and              OE: for (reverse 0..$#{$self->{open_elements}}) {
7102                if ($open_elements->[-1]->[1] ne 'frameset') {                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7103                  $insertion_mode = 'after frameset';                  !!!cp ('t373');
7104                    splice @{$self->{open_elements}}, $_, 1;
7105                    last OE;
7106                }                }
7107                redo B;              } # OE
7108              } else {              last AFE;
7109                #            } elsif ($node->[0] eq '#marker') {
7110              }              !!!cp ('t374');
7111            } else {              last AFE;
             #  
7112            }            }
7113            } # AFE
7114              
7115            $reconstruct_active_formatting_elements->($insert_to_current);
7116    
7117            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7118            push @$active_formatting_elements, $self->{open_elements}->[-1];
7119    
7120            !!!nack ('t374.1');
7121            !!!next-token;
7122            next B;
7123          } elsif ($token->{tag_name} eq 'nobr') {
7124            $reconstruct_active_formatting_elements->($insert_to_current);
7125    
7126            ## has a |nobr| element in scope
7127            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7128              my $node = $self->{open_elements}->[$_];
7129              if ($node->[1] & NOBR_EL) {
7130                !!!cp ('t376');
7131                !!!parse-error (type => 'in nobr:nobr', token => $token);
7132                !!!back-token; # <nobr>
7133                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7134                          line => $token->{line}, column => $token->{column}};
7135                next B;
7136              } elsif ($node->[1] & SCOPING_EL) {
7137                !!!cp ('t377');
7138                last INSCOPE;
7139              }
7140            } # INSCOPE
7141            
7142            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7143            push @$active_formatting_elements, $self->{open_elements}->[-1];
7144            
7145            !!!nack ('t377.1');
7146            !!!next-token;
7147            next B;
7148          } elsif ($token->{tag_name} eq 'button') {
7149            ## has a button element in scope
7150            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7151              my $node = $self->{open_elements}->[$_];
7152              if ($node->[1] & BUTTON_EL) {
7153                !!!cp ('t378');
7154                !!!parse-error (type => 'in button:button', token => $token);
7155                !!!back-token; # <button>
7156                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7157                          line => $token->{line}, column => $token->{column}};
7158                next B;
7159              } elsif ($node->[1] & SCOPING_EL) {
7160                !!!cp ('t379');
7161                last INSCOPE;
7162              }
7163            } # INSCOPE
7164              
7165            $reconstruct_active_formatting_elements->($insert_to_current);
7166                        
7167            !!!parse-error;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7168    
7169            ## TODO: associate with $self->{form_element} if defined
7170    
7171            push @$active_formatting_elements, ['#marker', ''];
7172    
7173            !!!nack ('t379.1');
7174            !!!next-token;
7175            next B;
7176          } elsif ({
7177                    xmp => 1,
7178                    iframe => 1,
7179                    noembed => 1,
7180                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7181                    noscript => 0, ## TODO: 1 if scripting is enabled
7182                   }->{$token->{tag_name}}) {
7183            if ($token->{tag_name} eq 'xmp') {
7184              !!!cp ('t381');
7185              $reconstruct_active_formatting_elements->($insert_to_current);
7186            } else {
7187              !!!cp ('t399');
7188            }
7189            ## NOTE: There is an "as if in body" code clone.
7190            $parse_rcdata->(CDATA_CONTENT_MODEL);
7191            next B;
7192          } elsif ($token->{tag_name} eq 'isindex') {
7193            !!!parse-error (type => 'isindex', token => $token);
7194            
7195            if (defined $self->{form_element}) {
7196              !!!cp ('t389');
7197            ## Ignore the token            ## Ignore the token
7198              !!!nack ('t389'); ## NOTE: Not acknowledged.
7199            !!!next-token;            !!!next-token;
7200            redo B;            next B;
7201          } elsif ($insertion_mode eq 'after frameset') {          } else {
7202            if ($token->{type} eq 'character') {            !!!ack ('t391.1');
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $open_elements->[-1]->[0]->manakai_append_text ($token->{data});  
   
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
7203    
7204              #            my $at = $token->{attributes};
7205            } elsif ($token->{type} eq 'comment') {            my $form_attrs;
7206              my $comment = $self->{document}->create_comment ($token->{data});            $form_attrs->{action} = $at->{action} if $at->{action};
7207              $open_elements->[-1]->[0]->append_child ($comment);            my $prompt_attr = $at->{prompt};
7208              $at->{name} = {name => 'name', value => 'isindex'};
7209              delete $at->{action};
7210              delete $at->{prompt};
7211              my @tokens = (
7212                            {type => START_TAG_TOKEN, tag_name => 'form',
7213                             attributes => $form_attrs,
7214                             line => $token->{line}, column => $token->{column}},
7215                            {type => START_TAG_TOKEN, tag_name => 'hr',
7216                             line => $token->{line}, column => $token->{column}},
7217                            {type => START_TAG_TOKEN, tag_name => 'p',
7218                             line => $token->{line}, column => $token->{column}},
7219                            {type => START_TAG_TOKEN, tag_name => 'label',
7220                             line => $token->{line}, column => $token->{column}},
7221                           );
7222              if ($prompt_attr) {
7223                !!!cp ('t390');
7224                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7225                               #line => $token->{line}, column => $token->{column},
7226                              };
7227              } else {
7228                !!!cp ('t391');
7229                push @tokens, {type => CHARACTER_TOKEN,
7230                               data => 'This is a searchable index. Insert your search keywords here: ',
7231                               #line => $token->{line}, column => $token->{column},
7232                              }; # SHOULD
7233                ## TODO: make this configurable
7234              }
7235              push @tokens,
7236                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7237                             line => $token->{line}, column => $token->{column}},
7238                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7239                            {type => END_TAG_TOKEN, tag_name => 'label',
7240                             line => $token->{line}, column => $token->{column}},
7241                            {type => END_TAG_TOKEN, tag_name => 'p',
7242                             line => $token->{line}, column => $token->{column}},
7243                            {type => START_TAG_TOKEN, tag_name => 'hr',
7244                             line => $token->{line}, column => $token->{column}},
7245                            {type => END_TAG_TOKEN, tag_name => 'form',
7246                             line => $token->{line}, column => $token->{column}};
7247              !!!back-token (@tokens);
7248              !!!next-token;
7249              next B;
7250            }
7251          } elsif ($token->{tag_name} eq 'textarea') {
7252            my $tag_name = $token->{tag_name};
7253            my $el;
7254            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7255            
7256            ## TODO: $self->{form_element} if defined
7257            $self->{content_model} = RCDATA_CONTENT_MODEL;
7258            delete $self->{escape}; # MUST
7259            
7260            $insert->($el);
7261            
7262            my $text = '';
7263            !!!nack ('t392.1');
7264            !!!next-token;
7265            if ($token->{type} == CHARACTER_TOKEN) {
7266              $token->{data} =~ s/^\x0A//;
7267              unless (length $token->{data}) {
7268                !!!cp ('t392');
7269              !!!next-token;              !!!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 {  
               #  
             }  
7270            } else {            } else {
7271              #              !!!cp ('t393');
7272            }            }
7273                      } else {
7274            !!!parse-error;            !!!cp ('t394');
7275            ## Ignore the token          }
7276            while ($token->{type} == CHARACTER_TOKEN) {
7277              !!!cp ('t395');
7278              $text .= $token->{data};
7279            !!!next-token;            !!!next-token;
7280            redo B;          }
7281            if (length $text) {
7282              !!!cp ('t396');
7283              $el->manakai_append_text ($text);
7284            }
7285            
7286            $self->{content_model} = PCDATA_CONTENT_MODEL;
7287            
7288            if ($token->{type} == END_TAG_TOKEN and
7289                $token->{tag_name} eq $tag_name) {
7290              !!!cp ('t397');
7291              ## Ignore the token
7292            } else {
7293              !!!cp ('t398');
7294              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7295            }
7296            !!!next-token;
7297            next B;
7298          } elsif ($token->{tag_name} eq 'rt' or
7299                   $token->{tag_name} eq 'rp') {
7300            ## has a |ruby| element in scope
7301            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7302              my $node = $self->{open_elements}->[$_];
7303              if ($node->[1] & RUBY_EL) {
7304                !!!cp ('t398.1');
7305                ## generate implied end tags
7306                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7307                  !!!cp ('t398.2');
7308                  pop @{$self->{open_elements}};
7309                }
7310                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7311                  !!!cp ('t398.3');
7312                  !!!parse-error (type => 'not closed',
7313                                  text => $self->{open_elements}->[-1]->[0]
7314                                      ->manakai_local_name,
7315                                  token => $token);
7316                  pop @{$self->{open_elements}}
7317                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7318                }
7319                last INSCOPE;
7320              } elsif ($node->[1] & SCOPING_EL) {
7321                !!!cp ('t398.4');
7322                last INSCOPE;
7323              }
7324            } # INSCOPE
7325    
7326            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7327    
7328            !!!nack ('t398.5');
7329            !!!next-token;
7330            redo B;
7331          } elsif ($token->{tag_name} eq 'math' or
7332                   $token->{tag_name} eq 'svg') {
7333            $reconstruct_active_formatting_elements->($insert_to_current);
7334    
7335            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7336    
7337            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7338    
7339            ## ISSUE: An issue in spec there          ## "adjust foreign attributes" - done in insert-element-f
7340            
7341            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7342            
7343            if ($self->{self_closing}) {
7344              pop @{$self->{open_elements}};
7345              !!!ack ('t398.1');
7346          } else {          } else {
7347            die "$0: $insertion_mode: Unknown insertion mode";            !!!cp ('t398.2');
7348              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7349              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7350              ## mode, "in body" (not "in foreign content") secondary insertion
7351              ## mode, maybe.
7352          }          }
7353        }  
7354      } elsif ($phase eq 'trailing end') {          !!!next-token;
7355        ## states in the main stage is preserved yet # MUST          next B;
7356                } elsif ({
7357        if ($token->{type} eq 'DOCTYPE') {                  caption => 1, col => 1, colgroup => 1, frame => 1,
7358          !!!parse-error;                  frameset => 1, head => 1, option => 1, optgroup => 1,
7359                    tbody => 1, td => 1, tfoot => 1, th => 1,
7360                    thead => 1, tr => 1,
7361                   }->{$token->{tag_name}}) {
7362            !!!cp ('t401');
7363            !!!parse-error (type => 'in body',
7364                            text => $token->{tag_name}, token => $token);
7365          ## Ignore the token          ## Ignore the token
7366            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7367          !!!next-token;          !!!next-token;
7368          redo B;          next B;
7369        } elsif ($token->{type} eq 'comment') {          
7370          my $comment = $self->{document}->create_comment ($token->{data});          ## ISSUE: An issue on HTML5 new elements in the spec.
7371          $self->{document}->append_child ($comment);        } else {
7372            if ($token->{tag_name} eq 'image') {
7373              !!!cp ('t384');
7374              !!!parse-error (type => 'image', token => $token);
7375              $token->{tag_name} = 'img';
7376            } else {
7377              !!!cp ('t385');
7378            }
7379    
7380            ## NOTE: There is an "as if <br>" code clone.
7381            $reconstruct_active_formatting_elements->($insert_to_current);
7382            
7383            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7384    
7385            if ({
7386                 applet => 1, marquee => 1, object => 1,
7387                }->{$token->{tag_name}}) {
7388              !!!cp ('t380');
7389              push @$active_formatting_elements, ['#marker', ''];
7390              !!!nack ('t380.1');
7391            } elsif ({
7392                      b => 1, big => 1, em => 1, font => 1, i => 1,
7393                      s => 1, small => 1, strike => 1,
7394                      strong => 1, tt => 1, u => 1,
7395                     }->{$token->{tag_name}}) {
7396              !!!cp ('t375');
7397              push @$active_formatting_elements, $self->{open_elements}->[-1];
7398              !!!nack ('t375.1');
7399            } elsif ($token->{tag_name} eq 'input') {
7400              !!!cp ('t388');
7401              ## TODO: associate with $self->{form_element} if defined
7402              pop @{$self->{open_elements}};
7403              !!!ack ('t388.2');
7404            } elsif ({
7405                      area => 1, basefont => 1, bgsound => 1, br => 1,
7406                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
7407                      #image => 1,
7408                     }->{$token->{tag_name}}) {
7409              !!!cp ('t388.1');
7410              pop @{$self->{open_elements}};
7411              !!!ack ('t388.3');
7412            } elsif ($token->{tag_name} eq 'select') {
7413              ## TODO: associate with $self->{form_element} if defined
7414            
7415              if ($self->{insertion_mode} & TABLE_IMS or
7416                  $self->{insertion_mode} & BODY_TABLE_IMS or
7417                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7418                !!!cp ('t400.1');
7419                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7420              } else {
7421                !!!cp ('t400.2');
7422                $self->{insertion_mode} = IN_SELECT_IM;
7423              }
7424              !!!nack ('t400.3');
7425            } else {
7426              !!!nack ('t402');
7427            }
7428            
7429          !!!next-token;          !!!next-token;
7430          redo B;          next B;
7431        } elsif ($token->{type} eq 'character') {        }
7432          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {      } elsif ($token->{type} == END_TAG_TOKEN) {
7433            my $data = $1;        if ($token->{tag_name} eq 'body') {
7434            ## As if in the main phase.          ## has a |body| element in scope
7435            ## NOTE: The insertion mode in the main phase          my $i;
7436            ## just before the phase has been changed to the trailing          INSCOPE: {
7437            ## end phase is either "after body" or "after frameset".            for (reverse @{$self->{open_elements}}) {
7438            $reconstruct_active_formatting_elements->($insert_to_current)              if ($_->[1] & BODY_EL) {
7439              if $phase eq 'main';                !!!cp ('t405');
7440                  $i = $_;
7441                  last INSCOPE;
7442                } elsif ($_->[1] & SCOPING_EL) {
7443                  !!!cp ('t405.1');
7444                  last;
7445                }
7446              }
7447    
7448              !!!parse-error (type => 'start tag not allowed',
7449                              text => $token->{tag_name}, token => $token);
7450              ## NOTE: Ignore the token.
7451              !!!next-token;
7452              next B;
7453            } # INSCOPE
7454    
7455            for (@{$self->{open_elements}}) {
7456              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7457                !!!cp ('t403');
7458                !!!parse-error (type => 'not closed',
7459                                text => $_->[0]->manakai_local_name,
7460                                token => $token);
7461                last;
7462              } else {
7463                !!!cp ('t404');
7464              }
7465            }
7466    
7467            $self->{insertion_mode} = AFTER_BODY_IM;
7468            !!!next-token;
7469            next B;
7470          } elsif ($token->{tag_name} eq 'html') {
7471            ## TODO: Update this code.  It seems that the code below is not
7472            ## up-to-date, though it has same effect as speced.
7473            if (@{$self->{open_elements}} > 1 and
7474                $self->{open_elements}->[1]->[1] & BODY_EL) {
7475              ## ISSUE: There is an issue in the spec.
7476              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7477                !!!cp ('t406');
7478                !!!parse-error (type => 'not closed',
7479                                text => $self->{open_elements}->[1]->[0]
7480                                    ->manakai_local_name,
7481                                token => $token);
7482              } else {
7483                !!!cp ('t407');
7484              }
7485              $self->{insertion_mode} = AFTER_BODY_IM;
7486              ## reprocess
7487              next B;
7488            } else {
7489              !!!cp ('t408');
7490              !!!parse-error (type => 'unmatched end tag',
7491                              text => $token->{tag_name}, token => $token);
7492              ## Ignore the token
7493              !!!next-token;
7494              next B;
7495            }
7496          } elsif ({
7497                    address => 1, blockquote => 1, center => 1, dir => 1,
7498                    div => 1, dl => 1, fieldset => 1, listing => 1,
7499                    menu => 1, ol => 1, pre => 1, ul => 1,
7500                    dd => 1, dt => 1, li => 1,
7501                    applet => 1, button => 1, marquee => 1, object => 1,
7502                   }->{$token->{tag_name}}) {
7503            ## has an element in scope
7504            my $i;
7505            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7506              my $node = $self->{open_elements}->[$_];
7507              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7508                !!!cp ('t410');
7509                $i = $_;
7510                last INSCOPE;
7511              } elsif ($node->[1] & SCOPING_EL) {
7512                !!!cp ('t411');
7513                last INSCOPE;
7514              }
7515            } # INSCOPE
7516    
7517            unless (defined $i) { # has an element in scope
7518              !!!cp ('t413');
7519              !!!parse-error (type => 'unmatched end tag',
7520                              text => $token->{tag_name}, token => $token);
7521              ## NOTE: Ignore the token.
7522            } else {
7523              ## Step 1. generate implied end tags
7524              while ({
7525                      ## END_TAG_OPTIONAL_EL
7526                      dd => ($token->{tag_name} ne 'dd'),
7527                      dt => ($token->{tag_name} ne 'dt'),
7528                      li => ($token->{tag_name} ne 'li'),
7529                      option => 1,
7530                      optgroup => 1,
7531                      p => 1,
7532                      rt => 1,
7533                      rp => 1,
7534                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7535                !!!cp ('t409');
7536                pop @{$self->{open_elements}};
7537              }
7538    
7539              ## Step 2.
7540              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7541                      ne $token->{tag_name}) {
7542                !!!cp ('t412');
7543                !!!parse-error (type => 'not closed',
7544                                text => $self->{open_elements}->[-1]->[0]
7545                                    ->manakai_local_name,
7546                                token => $token);
7547              } else {
7548                !!!cp ('t414');
7549              }
7550    
7551              ## Step 3.
7552              splice @{$self->{open_elements}}, $i;
7553    
7554              ## Step 4.
7555              $clear_up_to_marker->()
7556                  if {
7557                    applet => 1, button => 1, marquee => 1, object => 1,
7558                  }->{$token->{tag_name}};
7559            }
7560            !!!next-token;
7561            next B;
7562          } elsif ($token->{tag_name} eq 'form') {
7563            undef $self->{form_element};
7564    
7565            ## has an element in scope
7566            my $i;
7567            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7568              my $node = $self->{open_elements}->[$_];
7569              if ($node->[1] & FORM_EL) {
7570                !!!cp ('t418');
7571                $i = $_;
7572                last INSCOPE;
7573              } elsif ($node->[1] & SCOPING_EL) {
7574                !!!cp ('t419');
7575                last INSCOPE;
7576              }
7577            } # INSCOPE
7578    
7579            unless (defined $i) { # has an element in scope
7580              !!!cp ('t421');
7581              !!!parse-error (type => 'unmatched end tag',
7582                              text => $token->{tag_name}, token => $token);
7583              ## NOTE: Ignore the token.
7584            } else {
7585              ## Step 1. generate implied end tags
7586              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7587                !!!cp ('t417');
7588                pop @{$self->{open_elements}};
7589              }
7590              
7591              ## Step 2.
7592              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7593                      ne $token->{tag_name}) {
7594                !!!cp ('t417.1');
7595                !!!parse-error (type => 'not closed',
7596                                text => $self->{open_elements}->[-1]->[0]
7597                                    ->manakai_local_name,
7598                                token => $token);
7599              } else {
7600                !!!cp ('t420');
7601              }  
7602                        
7603            $open_elements->[-1]->[0]->manakai_append_text ($data);            ## Step 3.
7604              splice @{$self->{open_elements}}, $i;
7605            }
7606    
7607            !!!next-token;
7608            next B;
7609          } elsif ({
7610                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7611                   }->{$token->{tag_name}}) {
7612            ## has an element in scope
7613            my $i;
7614            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7615              my $node = $self->{open_elements}->[$_];
7616              if ($node->[1] & HEADING_EL) {
7617                !!!cp ('t423');
7618                $i = $_;
7619                last INSCOPE;
7620              } elsif ($node->[1] & SCOPING_EL) {
7621                !!!cp ('t424');
7622                last INSCOPE;
7623              }
7624            } # INSCOPE
7625    
7626            unless (defined $i) { # has an element in scope
7627              !!!cp ('t425.1');
7628              !!!parse-error (type => 'unmatched end tag',
7629                              text => $token->{tag_name}, token => $token);
7630              ## NOTE: Ignore the token.
7631            } else {
7632              ## Step 1. generate implied end tags
7633              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7634                !!!cp ('t422');
7635                pop @{$self->{open_elements}};
7636              }
7637                        
7638            unless (length $token->{data}) {            ## Step 2.
7639              !!!next-token;            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7640              redo B;                    ne $token->{tag_name}) {
7641                !!!cp ('t425');
7642                !!!parse-error (type => 'unmatched end tag',
7643                                text => $token->{tag_name}, token => $token);
7644              } else {
7645                !!!cp ('t426');
7646            }            }
7647    
7648              ## Step 3.
7649              splice @{$self->{open_elements}}, $i;
7650          }          }
7651            
7652            !!!next-token;
7653            next B;
7654          } elsif ($token->{tag_name} eq 'p') {
7655            ## has an element in scope
7656            my $i;
7657            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7658              my $node = $self->{open_elements}->[$_];
7659              if ($node->[1] & P_EL) {
7660                !!!cp ('t410.1');
7661                $i = $_;
7662                last INSCOPE;
7663              } elsif ($node->[1] & SCOPING_EL) {
7664                !!!cp ('t411.1');
7665                last INSCOPE;
7666              }
7667            } # INSCOPE
7668    
7669          !!!parse-error;          if (defined $i) {
7670          $phase = 'main';            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7671          ## reprocess                    ne $token->{tag_name}) {
7672          redo B;              !!!cp ('t412.1');
7673        } elsif ($token->{type} eq 'start tag' or              !!!parse-error (type => 'not closed',
7674                 $token->{type} eq 'end tag') {                              text => $self->{open_elements}->[-1]->[0]
7675          !!!parse-error;                                  ->manakai_local_name,
7676          $phase = 'main';                              token => $token);
7677          ## reprocess            } else {
7678          redo B;              !!!cp ('t414.1');
7679        } elsif ($token->{type} eq 'end-of-file') {            }
7680          ## Stop parsing  
7681          last B;            splice @{$self->{open_elements}}, $i;
7682            } else {
7683              !!!cp ('t413.1');
7684              !!!parse-error (type => 'unmatched end tag',
7685                              text => $token->{tag_name}, token => $token);
7686    
7687              !!!cp ('t415.1');
7688              ## As if <p>, then reprocess the current token
7689              my $el;
7690              !!!create-element ($el, $HTML_NS, 'p',, $token);
7691              $insert->($el);
7692              ## NOTE: Not inserted into |$self->{open_elements}|.
7693            }
7694    
7695            !!!next-token;
7696            next B;
7697          } elsif ({
7698                    a => 1,
7699                    b => 1, big => 1, em => 1, font => 1, i => 1,
7700                    nobr => 1, s => 1, small => 1, strike => 1,
7701                    strong => 1, tt => 1, u => 1,
7702                   }->{$token->{tag_name}}) {
7703            !!!cp ('t427');
7704            $formatting_end_tag->($token);
7705            next B;
7706          } elsif ($token->{tag_name} eq 'br') {
7707            !!!cp ('t428');
7708            !!!parse-error (type => 'unmatched end tag',
7709                            text => 'br', token => $token);
7710    
7711            ## As if <br>
7712            $reconstruct_active_formatting_elements->($insert_to_current);
7713            
7714            my $el;
7715            !!!create-element ($el, $HTML_NS, 'br',, $token);
7716            $insert->($el);
7717            
7718            ## Ignore the token.
7719            !!!next-token;
7720            next B;
7721          } elsif ({
7722                    caption => 1, col => 1, colgroup => 1, frame => 1,
7723                    frameset => 1, head => 1, option => 1, optgroup => 1,
7724                    tbody => 1, td => 1, tfoot => 1, th => 1,
7725                    thead => 1, tr => 1,
7726                    area => 1, basefont => 1, bgsound => 1,
7727                    embed => 1, hr => 1, iframe => 1, image => 1,
7728                    img => 1, input => 1, isindex => 1, noembed => 1,
7729                    noframes => 1, param => 1, select => 1, spacer => 1,
7730                    table => 1, textarea => 1, wbr => 1,
7731                    noscript => 0, ## TODO: if scripting is enabled
7732                   }->{$token->{tag_name}}) {
7733            !!!cp ('t429');
7734            !!!parse-error (type => 'unmatched end tag',
7735                            text => $token->{tag_name}, token => $token);
7736            ## Ignore the token
7737            !!!next-token;
7738            next B;
7739            
7740            ## ISSUE: Issue on HTML5 new elements in spec
7741            
7742        } else {        } else {
7743          die "$0: $token->{type}: Unknown token";          ## Step 1
7744            my $node_i = -1;
7745            my $node = $self->{open_elements}->[$node_i];
7746    
7747            ## Step 2
7748            S2: {
7749              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7750                ## Step 1
7751                ## generate implied end tags
7752                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7753                  !!!cp ('t430');
7754                  ## NOTE: |<ruby><rt></ruby>|.
7755                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7756                  ## which seems wrong.
7757                  pop @{$self->{open_elements}};
7758                  $node_i++;
7759                }
7760            
7761                ## Step 2
7762                if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7763                        ne $token->{tag_name}) {
7764                  !!!cp ('t431');
7765                  ## NOTE: <x><y></x>
7766                  !!!parse-error (type => 'not closed',
7767                                  text => $self->{open_elements}->[-1]->[0]
7768                                      ->manakai_local_name,
7769                                  token => $token);
7770                } else {
7771                  !!!cp ('t432');
7772                }
7773                
7774                ## Step 3
7775                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7776    
7777                !!!next-token;
7778                last S2;
7779              } else {
7780                ## Step 3
7781                if (not ($node->[1] & FORMATTING_EL) and
7782                    #not $phrasing_category->{$node->[1]} and
7783                    ($node->[1] & SPECIAL_EL or
7784                     $node->[1] & SCOPING_EL)) {
7785                  !!!cp ('t433');
7786                  !!!parse-error (type => 'unmatched end tag',
7787                                  text => $token->{tag_name}, token => $token);
7788                  ## Ignore the token
7789                  !!!next-token;
7790                  last S2;
7791    
7792                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7793                  ## 9.27, "a" is a child of <dd> (conforming).  In
7794                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7795                  ## "a" is a child of both <body> and <dd>.
7796                }
7797                
7798                !!!cp ('t434');
7799              }
7800              
7801              ## Step 4
7802              $node_i--;
7803              $node = $self->{open_elements}->[$node_i];
7804              
7805              ## Step 5;
7806              redo S2;
7807            } # S2
7808            next B;
7809        }        }
7810      }      }
7811        next B;
7812      } continue { # B
7813        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7814          ## NOTE: The code below is executed in cases where it does not have
7815          ## to be, but it it is harmless even in those cases.
7816          ## has an element in scope
7817          INSCOPE: {
7818            for (reverse 0..$#{$self->{open_elements}}) {
7819              my $node = $self->{open_elements}->[$_];
7820              if ($node->[1] & FOREIGN_EL) {
7821                last INSCOPE;
7822              } elsif ($node->[1] & SCOPING_EL) {
7823                last;
7824              }
7825            }
7826            
7827            ## NOTE: No foreign element in scope.
7828            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7829          } # INSCOPE
7830        }
7831    } # B    } # B
7832    
7833    ## Stop parsing # MUST    ## Stop parsing # MUST
7834        
7835    ## TODO: script stuffs    ## TODO: script stuffs
7836  } # _construct_tree  } # _tree_construct_main
   
 sub get_inner_html ($$$) {  
   my ($class, $node, $on_error) = @_;  
   
   ## Step 1  
   my $s = '';  
7837    
7838    my $in_cdata;  sub set_inner_html ($$$$;$) {
7839    my $parent = $node;    my $class = shift;
7840    while (defined $parent) {    my $node = shift;
7841      if ($parent->node_type == 1 and    #my $s = \$_[0];
7842          $parent->namespace_uri eq 'http://www.w3.org/1999/xhtml' and    my $onerror = $_[1];
7843          {    my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7844            style => 1, script => 1, xmp => 1, iframe => 1,  
7845            noembed => 1, noframes => 1, noscript => 1,    ## ISSUE: Should {confident} be true?
7846          }->{$parent->local_name}) { ## TODO: case thingy  
7847        $in_cdata = 1;    my $nt = $node->node_type;
7848      if ($nt == 9) {
7849        # MUST
7850        
7851        ## Step 1 # MUST
7852        ## TODO: If the document has an active parser, ...
7853        ## ISSUE: There is an issue in the spec.
7854        
7855        ## Step 2 # MUST
7856        my @cn = @{$node->child_nodes};
7857        for (@cn) {
7858          $node->remove_child ($_);
7859      }      }
     $parent = $parent->parent_node;  
   }  
7860    
7861    ## Step 2      ## Step 3, 4, 5 # MUST
7862    my @node = @{$node->child_nodes};      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7863    C: while (@node) {    } elsif ($nt == 1) {
7864      my $child = shift @node;      ## TODO: If non-html element
7865      unless (ref $child) {  
7866        if ($child eq 'cdata-out') {      ## NOTE: Most of this code is copied from |parse_string|
7867          $in_cdata = 0;  
7868    ## TODO: Support for $get_wrapper
7869    
7870        ## Step 1 # MUST
7871        my $this_doc = $node->owner_document;
7872        my $doc = $this_doc->implementation->create_document;
7873        $doc->manakai_is_html (1);
7874        my $p = $class->new;
7875        $p->{document} = $doc;
7876    
7877        ## Step 8 # MUST
7878        my $i = 0;
7879        $p->{line_prev} = $p->{line} = 1;
7880        $p->{column_prev} = $p->{column} = 0;
7881        require Whatpm::Charset::DecodeHandle;
7882        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7883        $input = $get_wrapper->($input);
7884        $p->{set_nc} = sub {
7885          my $self = shift;
7886    
7887          my $char = '';
7888          if (defined $self->{next_nc}) {
7889            $char = $self->{next_nc};
7890            delete $self->{next_nc};
7891            $self->{nc} = ord $char;
7892        } else {        } else {
7893          $s .= $child; # end tag          $self->{char_buffer} = '';
7894            $self->{char_buffer_pos} = 0;
7895            
7896            my $count = $input->manakai_read_until
7897                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7898                 $self->{char_buffer_pos});
7899            if ($count) {
7900              $self->{line_prev} = $self->{line};
7901              $self->{column_prev} = $self->{column};
7902              $self->{column}++;
7903              $self->{nc}
7904                  = ord substr ($self->{char_buffer},
7905                                $self->{char_buffer_pos}++, 1);
7906              return;
7907            }
7908            
7909            if ($input->read ($char, 1)) {
7910              $self->{nc} = ord $char;
7911            } else {
7912              $self->{nc} = -1;
7913              return;
7914            }
7915        }        }
7916        next C;  
7917      }        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7918          $p->{column}++;
7919    
7920          if ($self->{nc} == 0x000A) { # LF
7921            $p->{line}++;
7922            $p->{column} = 0;
7923            !!!cp ('i1');
7924          } elsif ($self->{nc} == 0x000D) { # CR
7925    ## TODO: support for abort/streaming
7926            my $next = '';
7927            if ($input->read ($next, 1) and $next ne "\x0A") {
7928              $self->{next_nc} = $next;
7929            }
7930            $self->{nc} = 0x000A; # LF # MUST
7931            $p->{line}++;
7932            $p->{column} = 0;
7933            !!!cp ('i2');
7934          } elsif ($self->{nc} == 0x0000) { # NULL
7935            !!!cp ('i4');
7936            !!!parse-error (type => 'NULL');
7937            $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7938          }
7939        };
7940    
7941        $p->{read_until} = sub {
7942          #my ($scalar, $specials_range, $offset) = @_;
7943          return 0 if defined $p->{next_nc};
7944    
7945          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7946          my $offset = $_[2] || 0;
7947          
7948          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7949            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7950            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7951              substr ($_[0], $offset)
7952                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7953              my $count = $+[0] - $-[0];
7954              if ($count) {
7955                $p->{column} += $count;
7956                $p->{char_buffer_pos} += $count;
7957                $p->{line_prev} = $p->{line};
7958                $p->{column_prev} = $p->{column} - 1;
7959                $p->{nc} = -1;
7960              }
7961              return $count;
7962            } else {
7963              return 0;
7964            }
7965          } else {
7966            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7967            if ($count) {
7968              $p->{column} += $count;
7969              $p->{column_prev} += $count;
7970              $p->{nc} = -1;
7971            }
7972            return $count;
7973          }
7974        }; # $p->{read_until}
7975    
7976        my $ponerror = $onerror || sub {
7977          my (%opt) = @_;
7978          my $line = $opt{line};
7979          my $column = $opt{column};
7980          if (defined $opt{token} and defined $opt{token}->{line}) {
7981            $line = $opt{token}->{line};
7982            $column = $opt{token}->{column};
7983          }
7984          warn "Parse error ($opt{type}) at line $line column $column\n";
7985        };
7986        $p->{parse_error} = sub {
7987          $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7988        };
7989            
7990      my $nt = $child->node_type;      my $char_onerror = sub {
7991      if ($nt == 1) { # Element        my (undef, $type, %opt) = @_;
7992        my $tag_name = lc $child->tag_name; ## ISSUE: Definition of "lowercase"        $ponerror->(layer => 'encode',
7993        $s .= '<' . $tag_name;                    line => $p->{line}, column => $p->{column} + 1,
7994                      %opt, type => $type);
7995        ## ISSUE: Non-html elements      }; # $char_onerror
7996        $input->onerror ($char_onerror);
7997        my @attrs = @{$child->attributes}; # sort order MUST be stable  
7998        for my $attr (@attrs) { # order is implementation dependent      $p->_initialize_tokenizer;
7999          my $attr_name = lc $attr->name; ## ISSUE: Definition of "lowercase"      $p->_initialize_tree_constructor;
8000          $s .= ' ' . $attr_name . '="';  
8001          my $attr_value = $attr->value;      ## Step 2
8002          ## escape      my $node_ln = $node->manakai_local_name;
8003          $attr_value =~ s/&/&amp;/g;      $p->{content_model} = {
8004          $attr_value =~ s/</&lt;/g;        title => RCDATA_CONTENT_MODEL,
8005          $attr_value =~ s/>/&gt;/g;        textarea => RCDATA_CONTENT_MODEL,
8006          $attr_value =~ s/"/&quot;/g;        style => CDATA_CONTENT_MODEL,
8007          $s .= $attr_value . '"';        script => CDATA_CONTENT_MODEL,
8008          xmp => CDATA_CONTENT_MODEL,
8009          iframe => CDATA_CONTENT_MODEL,
8010          noembed => CDATA_CONTENT_MODEL,
8011          noframes => CDATA_CONTENT_MODEL,
8012          noscript => CDATA_CONTENT_MODEL,
8013          plaintext => PLAINTEXT_CONTENT_MODEL,
8014        }->{$node_ln};
8015        $p->{content_model} = PCDATA_CONTENT_MODEL
8016            unless defined $p->{content_model};
8017            ## ISSUE: What is "the name of the element"? local name?
8018    
8019        $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8020          ## TODO: Foreign element OK?
8021    
8022        ## Step 3
8023        my $root = $doc->create_element_ns
8024          ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8025    
8026        ## Step 4 # MUST
8027        $doc->append_child ($root);
8028    
8029        ## Step 5 # MUST
8030        push @{$p->{open_elements}}, [$root, $el_category->{html}];
8031    
8032        undef $p->{head_element};
8033    
8034        ## Step 6 # MUST
8035        $p->_reset_insertion_mode;
8036    
8037        ## Step 7 # MUST
8038        my $anode = $node;
8039        AN: while (defined $anode) {
8040          if ($anode->node_type == 1) {
8041            my $nsuri = $anode->namespace_uri;
8042            if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8043              if ($anode->manakai_local_name eq 'form') {
8044                !!!cp ('i5');
8045                $p->{form_element} = $anode;
8046                last AN;
8047              }
8048            }
8049        }        }
8050        $s .= '>';        $anode = $anode->parent_node;
8051              } # AN
8052        next C if {      
8053          area => 1, base => 1, basefont => 1, bgsound => 1,      ## Step 9 # MUST
8054          br => 1, col => 1, embed => 1, frame => 1, hr => 1,      {
8055          img => 1, input => 1, link => 1, meta => 1, param => 1,        my $self = $p;
8056          spacer => 1, wbr => 1,        !!!next-token;
       }->{$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;  
8057      }      }
8058      ## ISSUE: This code does not support PIs.      $p->_tree_construction_main;
8059    } # C  
8060          ## Step 10 # MUST
8061    ## Step 3      my @cn = @{$node->child_nodes};
8062    return \$s;      for (@cn) {
8063  } # get_inner_html        $node->remove_child ($_);
8064        }
8065        ## ISSUE: mutation events? read-only?
8066    
8067        ## Step 11 # MUST
8068        @cn = @{$root->child_nodes};
8069        for (@cn) {
8070          $this_doc->adopt_node ($_);
8071          $node->append_child ($_);
8072        }
8073        ## ISSUE: mutation events?
8074    
8075        $p->_terminate_tree_constructor;
8076    
8077        delete $p->{parse_error}; # delete loop
8078      } else {
8079        die "$0: |set_inner_html| is not defined for node of type $nt";
8080      }
8081    } # set_inner_html
8082    
8083    } # tree construction stage
8084    
8085    package Whatpm::HTML::RestartParser;
8086    push our @ISA, 'Error';
8087    
8088  1;  1;
8089  # $Date$  # $Date$

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24