/[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.13 by wakaba, Sat Jun 23 05:29:48 2007 UTC revision 1.204 by wakaba, Sun Oct 5 05:59:35 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    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17    ## ISSUE:
18    ## var doc = implementation.createDocument (null, null, null);
19    ## 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      ## ISSUE: option, optgroup, rt, rp?
90    
91      BODY_EL |
92      HTML_EL |
93      TABLE_CELL_EL |
94      TABLE_ROW_EL |
95      TABLE_ROW_GROUP_EL
96    }
97    
98    sub SCOPING_EL () {
99      BUTTON_EL |
100      CAPTION_EL |
101      HTML_EL |
102      TABLE_EL |
103      TABLE_CELL_EL |
104      MISC_SCOPING_EL
105    }
106    
107    sub TABLE_SCOPING_EL () {
108      HTML_EL |
109      TABLE_EL
110    }
111    
112    sub TABLE_ROWS_SCOPING_EL () {
113      HTML_EL |
114      TABLE_ROW_GROUP_EL
115    }
116    
117    sub TABLE_ROW_SCOPING_EL () {
118      HTML_EL |
119      TABLE_ROW_EL
120    }
121    
122    sub SPECIAL_EL () {
123      ADDRESS_EL |
124      BODY_EL |
125      DIV_EL |
126    
127      DD_EL |
128      DT_EL |
129      LI_EL |
130      P_EL |
131    
132      FORM_EL |
133      FRAMESET_EL |
134      HEADING_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 $el_category_f = {
243      $MML_NS => {
244        'annotation-xml' => MML_AXML_EL,
245        mi => FOREIGN_FLOW_CONTENT_EL,
246        mo => FOREIGN_FLOW_CONTENT_EL,
247        mn => FOREIGN_FLOW_CONTENT_EL,
248        ms => FOREIGN_FLOW_CONTENT_EL,
249        mtext => FOREIGN_FLOW_CONTENT_EL,
250      },
251      $SVG_NS => {
252        foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259  my $permitted_slash_tag_name = {  my $svg_attr_name = {
260    base => 1,    attributename => 'attributeName',
261    link => 1,    attributetype => 'attributeType',
262    meta => 1,    basefrequency => 'baseFrequency',
263    hr => 1,    baseprofile => 'baseProfile',
264    br => 1,    calcmode => 'calcMode',
265    img=> 1,    clippathunits => 'clipPathUnits',
266    embed => 1,    contentscripttype => 'contentScriptType',
267    param => 1,    contentstyletype => 'contentStyleType',
268    area => 1,    diffuseconstant => 'diffuseConstant',
269    col => 1,    edgemode => 'edgeMode',
270    input => 1,    externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323    
324  my $entity_char = {  my $foreign_attr_xname = {
325    AElig => "\x{00C6}",    'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326    Aacute => "\x{00C1}",    'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327    Acirc => "\x{00C2}",    'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328    Agrave => "\x{00C0}",    'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329    Alpha => "\x{0391}",    'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330    Aring => "\x{00C5}",    'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331    Atilde => "\x{00C3}",    'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332    Auml => "\x{00C4}",    'xml:base' => [$XML_NS, ['xml', 'base']],
333    Beta => "\x{0392}",    'xml:lang' => [$XML_NS, ['xml', 'lang']],
334    Ccedil => "\x{00C7}",    'xml:space' => [$XML_NS, ['xml', 'space']],
335    Chi => "\x{03A7}",    'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336    Dagger => "\x{2021}",    'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    Delta => "\x{0394}",  };
338    ETH => "\x{00D0}",  
339    Eacute => "\x{00C9}",  ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
   Ecirc => "\x{00CA}",  
   Egrave => "\x{00C8}",  
   Epsilon => "\x{0395}",  
   Eta => "\x{0397}",  
   Euml => "\x{00CB}",  
   Gamma => "\x{0393}",  
   Iacute => "\x{00CD}",  
   Icirc => "\x{00CE}",  
   Igrave => "\x{00CC}",  
   Iota => "\x{0399}",  
   Iuml => "\x{00CF}",  
   Kappa => "\x{039A}",  
   Lambda => "\x{039B}",  
   Mu => "\x{039C}",  
   Ntilde => "\x{00D1}",  
   Nu => "\x{039D}",  
   OElig => "\x{0152}",  
   Oacute => "\x{00D3}",  
   Ocirc => "\x{00D4}",  
   Ograve => "\x{00D2}",  
   Omega => "\x{03A9}",  
   Omicron => "\x{039F}",  
   Oslash => "\x{00D8}",  
   Otilde => "\x{00D5}",  
   Ouml => "\x{00D6}",  
   Phi => "\x{03A6}",  
   Pi => "\x{03A0}",  
   Prime => "\x{2033}",  
   Psi => "\x{03A8}",  
   Rho => "\x{03A1}",  
   Scaron => "\x{0160}",  
   Sigma => "\x{03A3}",  
   THORN => "\x{00DE}",  
   Tau => "\x{03A4}",  
   Theta => "\x{0398}",  
   Uacute => "\x{00DA}",  
   Ucirc => "\x{00DB}",  
   Ugrave => "\x{00D9}",  
   Upsilon => "\x{03A5}",  
   Uuml => "\x{00DC}",  
   Xi => "\x{039E}",  
   Yacute => "\x{00DD}",  
   Yuml => "\x{0178}",  
   Zeta => "\x{0396}",  
   aacute => "\x{00E1}",  
   acirc => "\x{00E2}",  
   acute => "\x{00B4}",  
   aelig => "\x{00E6}",  
   agrave => "\x{00E0}",  
   alefsym => "\x{2135}",  
   alpha => "\x{03B1}",  
   amp => "\x{0026}",  
   AMP => "\x{0026}",  
   and => "\x{2227}",  
   ang => "\x{2220}",  
   apos => "\x{0027}",  
   aring => "\x{00E5}",  
   asymp => "\x{2248}",  
   atilde => "\x{00E3}",  
   auml => "\x{00E4}",  
   bdquo => "\x{201E}",  
   beta => "\x{03B2}",  
   brvbar => "\x{00A6}",  
   bull => "\x{2022}",  
   cap => "\x{2229}",  
   ccedil => "\x{00E7}",  
   cedil => "\x{00B8}",  
   cent => "\x{00A2}",  
   chi => "\x{03C7}",  
   circ => "\x{02C6}",  
   clubs => "\x{2663}",  
   cong => "\x{2245}",  
   copy => "\x{00A9}",  
   COPY => "\x{00A9}",  
   crarr => "\x{21B5}",  
   cup => "\x{222A}",  
   curren => "\x{00A4}",  
   dArr => "\x{21D3}",  
   dagger => "\x{2020}",  
   darr => "\x{2193}",  
   deg => "\x{00B0}",  
   delta => "\x{03B4}",  
   diams => "\x{2666}",  
   divide => "\x{00F7}",  
   eacute => "\x{00E9}",  
   ecirc => "\x{00EA}",  
   egrave => "\x{00E8}",  
   empty => "\x{2205}",  
   emsp => "\x{2003}",  
   ensp => "\x{2002}",  
   epsilon => "\x{03B5}",  
   equiv => "\x{2261}",  
   eta => "\x{03B7}",  
   eth => "\x{00F0}",  
   euml => "\x{00EB}",  
   euro => "\x{20AC}",  
   exist => "\x{2203}",  
   fnof => "\x{0192}",  
   forall => "\x{2200}",  
   frac12 => "\x{00BD}",  
   frac14 => "\x{00BC}",  
   frac34 => "\x{00BE}",  
   frasl => "\x{2044}",  
   gamma => "\x{03B3}",  
   ge => "\x{2265}",  
   gt => "\x{003E}",  
   GT => "\x{003E}",  
   hArr => "\x{21D4}",  
   harr => "\x{2194}",  
   hearts => "\x{2665}",  
   hellip => "\x{2026}",  
   iacute => "\x{00ED}",  
   icirc => "\x{00EE}",  
   iexcl => "\x{00A1}",  
   igrave => "\x{00EC}",  
   image => "\x{2111}",  
   infin => "\x{221E}",  
   int => "\x{222B}",  
   iota => "\x{03B9}",  
   iquest => "\x{00BF}",  
   isin => "\x{2208}",  
   iuml => "\x{00EF}",  
   kappa => "\x{03BA}",  
   lArr => "\x{21D0}",  
   lambda => "\x{03BB}",  
   lang => "\x{2329}",  
   laquo => "\x{00AB}",  
   larr => "\x{2190}",  
   lceil => "\x{2308}",  
   ldquo => "\x{201C}",  
   le => "\x{2264}",  
   lfloor => "\x{230A}",  
   lowast => "\x{2217}",  
   loz => "\x{25CA}",  
   lrm => "\x{200E}",  
   lsaquo => "\x{2039}",  
   lsquo => "\x{2018}",  
   lt => "\x{003C}",  
   LT => "\x{003C}",  
   macr => "\x{00AF}",  
   mdash => "\x{2014}",  
   micro => "\x{00B5}",  
   middot => "\x{00B7}",  
   minus => "\x{2212}",  
   mu => "\x{03BC}",  
   nabla => "\x{2207}",  
   nbsp => "\x{00A0}",  
   ndash => "\x{2013}",  
   ne => "\x{2260}",  
   ni => "\x{220B}",  
   not => "\x{00AC}",  
   notin => "\x{2209}",  
   nsub => "\x{2284}",  
   ntilde => "\x{00F1}",  
   nu => "\x{03BD}",  
   oacute => "\x{00F3}",  
   ocirc => "\x{00F4}",  
   oelig => "\x{0153}",  
   ograve => "\x{00F2}",  
   oline => "\x{203E}",  
   omega => "\x{03C9}",  
   omicron => "\x{03BF}",  
   oplus => "\x{2295}",  
   or => "\x{2228}",  
   ordf => "\x{00AA}",  
   ordm => "\x{00BA}",  
   oslash => "\x{00F8}",  
   otilde => "\x{00F5}",  
   otimes => "\x{2297}",  
   ouml => "\x{00F6}",  
   para => "\x{00B6}",  
   part => "\x{2202}",  
   permil => "\x{2030}",  
   perp => "\x{22A5}",  
   phi => "\x{03C6}",  
   pi => "\x{03C0}",  
   piv => "\x{03D6}",  
   plusmn => "\x{00B1}",  
   pound => "\x{00A3}",  
   prime => "\x{2032}",  
   prod => "\x{220F}",  
   prop => "\x{221D}",  
   psi => "\x{03C8}",  
   quot => "\x{0022}",  
   QUOT => "\x{0022}",  
   rArr => "\x{21D2}",  
   radic => "\x{221A}",  
   rang => "\x{232A}",  
   raquo => "\x{00BB}",  
   rarr => "\x{2192}",  
   rceil => "\x{2309}",  
   rdquo => "\x{201D}",  
   real => "\x{211C}",  
   reg => "\x{00AE}",  
   REG => "\x{00AE}",  
   rfloor => "\x{230B}",  
   rho => "\x{03C1}",  
   rlm => "\x{200F}",  
   rsaquo => "\x{203A}",  
   rsquo => "\x{2019}",  
   sbquo => "\x{201A}",  
   scaron => "\x{0161}",  
   sdot => "\x{22C5}",  
   sect => "\x{00A7}",  
   shy => "\x{00AD}",  
   sigma => "\x{03C3}",  
   sigmaf => "\x{03C2}",  
   sim => "\x{223C}",  
   spades => "\x{2660}",  
   sub => "\x{2282}",  
   sube => "\x{2286}",  
   sum => "\x{2211}",  
   sup => "\x{2283}",  
   sup1 => "\x{00B9}",  
   sup2 => "\x{00B2}",  
   sup3 => "\x{00B3}",  
   supe => "\x{2287}",  
   szlig => "\x{00DF}",  
   tau => "\x{03C4}",  
   there4 => "\x{2234}",  
   theta => "\x{03B8}",  
   thetasym => "\x{03D1}",  
   thinsp => "\x{2009}",  
   thorn => "\x{00FE}",  
   tilde => "\x{02DC}",  
   times => "\x{00D7}",  
   trade => "\x{2122}",  
   uArr => "\x{21D1}",  
   uacute => "\x{00FA}",  
   uarr => "\x{2191}",  
   ucirc => "\x{00FB}",  
   ugrave => "\x{00F9}",  
   uml => "\x{00A8}",  
   upsih => "\x{03D2}",  
   upsilon => "\x{03C5}",  
   uuml => "\x{00FC}",  
   weierp => "\x{2118}",  
   xi => "\x{03BE}",  
   yacute => "\x{00FD}",  
   yen => "\x{00A5}",  
   yuml => "\x{00FF}",  
   zeta => "\x{03B6}",  
   zwj => "\x{200D}",  
   zwnj => "\x{200C}",  
 }; # $entity_char  
340    
341  my $c1_entity_char = {  my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 313  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $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  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
387    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
388    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  sub parse_byte_string ($$$$;$) {
389    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    my $self = shift;
390    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    my $charset_name = shift;
391    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  } # parse_byte_string
394    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
395    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  sub parse_byte_stream ($$$$;$$) {
396  };    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397  my $scoping_category = {    my $self = ref $_[0] ? shift : shift->new;
398    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $charset_name = shift;
399    table => 1, td => 1, th => 1,    my $byte_stream = $_[0];
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
400    
401  sub parse_string ($$$;$) {    my $onerror = $_[2] || sub {
402    my $self = shift->new;      my (%opt) = @_;
403    my $s = \$_[0];      warn "Parse error ($opt{type})\n";
404      };
405      $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      };
410    
411      ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444        }
445    
446        ## Step 2
447        my $byte_buffer = '';
448        for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460          $self->{confident} = 1;
461          last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476        }
477    
478        ## Step 4
479        ## TODO: <meta charset>
480    
481        ## Step 5
482        ## TODO: from history
483    
484        ## Step 6
485        require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550      $self->{change_encoding} = sub {
551        my $self = shift;
552        $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599      }; # $self->{change_encoding}
600    
601      my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615      my $return;
616      try {
617        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618      } catch Whatpm::HTML::RestartParser with {
619        ## NOTE: Invoked after {change_encoding}.
620    
621        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639        $self->{confident} = 1;
640    
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645      };
646      return $return;
647    } # parse_byte_stream
648    
649    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
651    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
652    ## because the core part of our HTML parser expects a string of character,
653    ## not a string of bytes or code units or anything which might contain a BOM.
654    ## Therefore, any parser interface that accepts a string of bytes,
655    ## such as |parse_byte_string| in this module, must ensure that it does
656    ## strip the BOM and never strip any ZWNBSP.
657    
658    sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668    sub parse_char_stream ($$$;$$) {
669      my $self = ref $_[0] ? shift : shift->new;
670      my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672      @{$self->{document}->child_nodes} = ();
673    
674    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
675    
676    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
677    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
678    my $column = 0;        if defined $self->{input_encoding};
679    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
680    
681      $self->{line_prev} = $self->{line} = 1;
682      $self->{column_prev} = -1;
683      $self->{column} = 0;
684      $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_input_character}};      my $char = '';
688      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690          delete $self->{next_nc};
691          $self->{nc} = ord $char;
692        } else {
693          $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696      $self->{next_input_character} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
697      $self->{next_input_character} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698      $column++;        if ($count) {
699                $self->{line_prev} = $self->{line};
700      if ($self->{next_input_character} == 0x000A) { # LF          $self->{column_prev} = $self->{column};
701        $line++;          $self->{column}++;
702        $column = 0;          $self->{nc}
703      } elsif ($self->{next_input_character} == 0x000D) { # CR              = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704        if ($i >= length $$s) {          return;
705          #        }
706    
707          if ($input->read ($char, 1)) {
708            $self->{nc} = ord $char;
709        } else {        } else {
710          my $next_char = ord substr $$s, $i++, 1;          $self->{nc} = -1;
711          if ($next_char == 0x000A) { # LF          return;
           #  
         } else {  
           push @{$self->{char}}, $next_char;  
         }  
712        }        }
713        $self->{next_input_character} = 0x000A; # LF # MUST      }
714        $line++;  
715        $column = 0;      ($self->{line_prev}, $self->{column_prev})
716      } elsif ($self->{next_input_character} > 0x10FFFF) {          = ($self->{line}, $self->{column});
717        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST      $self->{column}++;
718      } elsif ($self->{next_input_character} == 0x0000) { # NULL      
719        if ($self->{nc} == 0x000A) { # LF
720          !!!cp ('j1');
721          $self->{line}++;
722          $self->{column} = 0;
723        } elsif ($self->{nc} == 0x000D) { # CR
724          !!!cp ('j2');
725    ## TODO: support for abort/streaming
726          my $next = '';
727          if ($input->read ($next, 1) and $next ne "\x0A") {
728            $self->{next_nc} = $next;
729          }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_input_character} = [-1, -1, -1];  
740    $self->{next_input_character} = -1;    $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771          }
772          return $count;
773        }
774      }; # $self->{read_until}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_input_character} = sub {      level => {must => 'm',
814      $self->{next_input_character} = -1;                should => 's',
815                  warn => 'w',
816                  info => 'i',
817                  uncertain => 'u'},
818      }, $class;
819      $self->{set_nc} = sub {
820        $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
824    };    };
825      $self->{change_encoding} = sub {
826        # if ($_[0] is a supported encoding) {
827        #   run "change the encoding" algorithm;
828        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
829        # }
830      };
831      $self->{application_cache_selection} = sub {
832        #
833      };
834    return $self;    return $self;
835  } # new  } # new
836    
837    sub CM_ENTITY () { 0b001 } # & markup in data
838    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
839    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
840    
841    sub PLAINTEXT_CONTENT_MODEL () { 0 }
842    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
843    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
844    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846    sub DATA_STATE () { 0 }
847    #sub ENTITY_DATA_STATE () { 1 }
848    sub TAG_OPEN_STATE () { 2 }
849    sub CLOSE_TAG_OPEN_STATE () { 3 }
850    sub TAG_NAME_STATE () { 4 }
851    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
852    sub ATTRIBUTE_NAME_STATE () { 6 }
853    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
854    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
855    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860    sub COMMENT_START_STATE () { 14 }
861    sub COMMENT_START_DASH_STATE () { 15 }
862    sub COMMENT_STATE () { 16 }
863    sub COMMENT_END_STATE () { 17 }
864    sub COMMENT_END_DASH_STATE () { 18 }
865    sub BOGUS_COMMENT_STATE () { 19 }
866    sub DOCTYPE_STATE () { 20 }
867    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
868    sub DOCTYPE_NAME_STATE () { 22 }
869    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
870    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
871    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
872    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
873    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
874    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
875    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
876    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
877    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878    sub BOGUS_DOCTYPE_STATE () { 32 }
879    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901    sub DOCTYPE_TOKEN () { 1 }
902    sub COMMENT_TOKEN () { 2 }
903    sub START_TAG_TOKEN () { 3 }
904    sub END_TAG_TOKEN () { 4 }
905    sub END_OF_FILE_TOKEN () { 5 }
906    sub CHARACTER_TOKEN () { 6 }
907    
908    sub AFTER_HTML_IMS () { 0b100 }
909    sub HEAD_IMS ()       { 0b1000 }
910    sub BODY_IMS ()       { 0b10000 }
911    sub BODY_TABLE_IMS () { 0b100000 }
912    sub TABLE_IMS ()      { 0b1000000 }
913    sub ROW_IMS ()        { 0b10000000 }
914    sub BODY_AFTER_IMS () { 0b100000000 }
915    sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922    ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
924    ## NOTE: "after after body" insertion mode.
925    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
926    
927    ## NOTE: "after after frameset" insertion mode.
928    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
929    
930    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
931    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
932    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
933    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
934    sub IN_BODY_IM () { BODY_IMS }
935    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
936    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
937    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
938    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
939    sub IN_TABLE_IM () { TABLE_IMS }
940    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945    sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
948    
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
952    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{s_kwd}; # state keyword - initialized when used
953    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    #$self->{entity__value}; # initialized when used
954    undef $self->{current_attribute};    #$self->{entity__match}; # initialized when used
955    undef $self->{last_emitted_start_tag_name};    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{last_attribute_value_state};    undef $self->{ct}; # current token
957    $self->{char} = [];    undef $self->{ca}; # current attribute
958    # $self->{next_input_character}    undef $self->{last_stag_name}; # last emitted start tag name
959      #$self->{prev_state}; # initialized when used
960      delete $self->{self_closing};
961      $self->{char_buffer} = '';
962      $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967      # $self->{escape}
968  } # _initialize_tokenizer  } # _initialize_tokenizer
969    
970  ## A token has:  ## A token has:
971  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
972  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE, start tag (tagname), end tag (tagname))  ##   ->{name} (DOCTYPE_TOKEN)
974      ## ISSUE: the spec need s/tagname/tag name/  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{error} == 1 or 0 (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{data} (comment, character)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978    ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979  ## Macros  ##        ->{name}
980  ##   Macros MUST be preceded by three EXCLAMATION MARKs.  ##        ->{value}
981  ##   emit ($token)  ##        ->{has_reference} == 1 or 0
982  ##     Emits the specified token.  ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
983    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
984    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985    ##     while the token is pushed back to the stack.
986    
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
# Line 450  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996    ## (This requirement was dropped from HTML5 spec, unfortunately.)
997    
998    my $is_space = {
999      0x0009 => 1, # CHARACTER TABULATION (HT)
1000      0x000A => 1, # LINE FEED (LF)
1001      #0x000B => 0, # LINE TABULATION (VT)
1002      0x000C => 1, # FORM FEED (FF)
1003      #0x000D => 1, # CARRIAGE RETURN (CR)
1004      0x0020 => 1, # SPACE (SP)
1005    };
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010      if ($self->{self_closing}) {
1011        !!!parse-error (type => 'nestc', token => $self->{ct});
1012        ## NOTE: The |self_closing| flag is only set by start tag token.
1013        ## In addition, when a start tag token is emitted, it is always set to
1014        ## |ct|.
1015        delete $self->{self_closing};
1016      }
1017    
1018    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1019        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1020      return shift @{$self->{token}};      return shift @{$self->{token}};
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026          if ($self->{content_model_flag} eq 'PCDATA' or  
1027              $self->{content_model_flag} eq 'RCDATA') {        if ($self->{nc} == 0x0026) { # &
1028            $self->{state} = 'entity data';          !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069                not $self->{escape}) {
1070              !!!cp (1);
1071              ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081              !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1085          if ($self->{content_model_flag} eq 'RCDATA' or          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086              $self->{content_model_flag} eq 'CDATA') {            $self->{s_kwd} .= '-';
1087            unless ($self->{escape}) {            
1088              if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_input_character}->[1] == 0x0021 and # !              !!!cp (3);
1090                  $self->{prev_input_character}->[2] == 0x003C) { # <              $self->{escape} = 1; # unless $self->{escape};
1091                $self->{escape} = 1;              $self->{s_kwd} = '--';
1092              }              #
1093              } elsif ($self->{s_kwd} eq '---') {
1094                !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097              } else {
1098                !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1105          if ($self->{content_model_flag} eq 'PCDATA' or          if (length $self->{s_kwd}) {
1106              (($self->{content_model_flag} eq 'CDATA' or            !!!cp (5.1);
1107                $self->{content_model_flag} eq 'RCDATA') and            $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112              #
1113            }
1114            #
1115          } elsif ($self->{nc} == 0x003C) { # <
1116            if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118               not $self->{escape})) {               not $self->{escape})) {
1119            $self->{state} = 'tag open';            !!!cp (6);
1120              $self->{state} = TAG_OPEN_STATE;
1121            !!!next-input-character;            !!!next-input-character;
1122            redo A;            redo A;
1123          } else {          } else {
1124              !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1129          if ($self->{escape} and          if ($self->{escape} and
1130              ($self->{content_model_flag} eq 'RCDATA' or              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131               $self->{content_model_flag} eq 'CDATA')) {            if ($self->{s_kwd} eq '--') {
1132            if ($self->{prev_input_character}->[0] == 0x002D and # -              !!!cp (8);
               $self->{prev_input_character}->[1] == 0x002D) { # -  
1133              delete $self->{escape};              delete $self->{escape};
1134              } else {
1135                !!!cp (9);
1136            }            }
1137            } else {
1138              !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!emit ({type => 'end-of-file'});          !!!cp (11);
1145            $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149          } else {
1150            !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
1154    
1155        !!!emit ($token);        # Anything else
1156          my $token = {type => CHARACTER_TOKEN,
1157        redo A;                     data => chr $self->{nc},
1158      } elsif ($self->{state} eq 'entity data') {                     line => $self->{line}, column => $self->{column},
1159        ## (cannot happen in CDATA state)                    };
1160                if ($self->{read_until}->($token->{data}, q[-!<>&],
1161        my $token = $self->_tokenize_attempt_to_consume_an_entity;                                  length $token->{data})) {
1162            $self->{s_kwd} = '';
1163        $self->{state} = 'data';        }
       # next-input-character is already done  
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          !!!emit ({type => 'character', data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167            !!!cp (13);
1168            $self->{state} = PCDATA_STATE;
1169        } else {        } else {
1170          !!!emit ($token);          !!!cp (14);
1171            ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178            $self->{content_model_flag} eq 'CDATA') {          if ($self->{nc} == 0x002F) { # /
1179          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            ## reconsume            !!!cp (16);
1189            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1190          }          }
1191        } elsif ($self->{content_model_flag} eq 'PCDATA') {  
1192          if ($self->{next_input_character} == 0x0021) { # !          ## reconsume
1193            $self->{state} = 'markup declaration open';          $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199          } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200            if ($self->{nc} == 0x0021) { # !
1201              !!!cp (17);
1202              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206            $self->{state} = 'close tag open';            !!!cp (18);
1207              $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            $self->{current_token}            !!!cp (19);
1213              = {type => 'start tag',            $self->{ct}
1214                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1215            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1216                   line => $self->{line_prev},
1217                   column => $self->{column_prev}};
1218              $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1224                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1225            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1226                                        line => $self->{line_prev},
1227                                        column => $self->{column_prev}};
1228              $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1233            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1234                              line => $self->{line_prev},
1235                              column => $self->{column_prev});
1236              $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!parse-error (type => 'pio');            !!!cp (22);
1247            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1248            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250              $self->{state} = BOGUS_COMMENT_STATE;
1251              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!parse-error (type => 'bare stago');            !!!cp (23);
1259            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1260                              line => $self->{line_prev},
1261                              column => $self->{column_prev});
1262              $self->{state} = DATA_STATE;
1263            ## reconsume            ## reconsume
1264    
1265            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
1272        } else {        } else {
1273          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1277            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1278          my @next_char;  
1279          TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            push @next_char, $self->{next_input_character};        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281            my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);          if (defined $self->{last_stag_name}) {
1282            my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283            if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {            $self->{s_kwd} = '';
1284              !!!next-input-character;            ## Reconsume.
             next TAGNAME;  
           } else {  
             !!!parse-error (type => 'unmatched end tag');  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = 'data';  
   
             !!!emit ({type => 'character', data => '</'});  
   
             redo A;  
           }  
         }  
         push @next_char, $self->{next_input_character};  
       
         unless ($self->{next_input_character} == 0x0009 or # HT  
                 $self->{next_input_character} == 0x000A or # LF  
                 $self->{next_input_character} == 0x000B or # VT  
                 $self->{next_input_character} == 0x000C or # FF  
                 $self->{next_input_character} == 0x0020 or # SP  
                 $self->{next_input_character} == 0x003E or # >  
                 $self->{next_input_character} == 0x002F or # /  
                 $self->{next_input_character} == 0x003C or # <  
                 $self->{next_input_character} == -1) {  
           !!!parse-error (type => 'unmatched end tag');  
           $self->{next_input_character} = shift @next_char; # reconsume  
           !!!back-next-input-character (@next_char);  
           $self->{state} = 'data';  
   
           !!!emit ({type => 'character', data => '</'});  
   
1285            redo A;            redo A;
1286          } else {          } else {
1287            $self->{next_input_character} = shift @next_char;            ## No start tag token has ever been emitted
1288            !!!back-next-input-character (@next_char);            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289            # and consume...            !!!cp (28);
1290              $self->{state} = DATA_STATE;
1291              ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295              redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1302                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1303          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1304          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1305          redo A;                 line => $l, column => $c};
1306        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1307                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1308          $self->{current_token} = {type => 'end tag',          redo A;
1309                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1310          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1311          !!!next-input-character;          !!!cp (30);
1312          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1313        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1314          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1315          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1316            !!!next-input-character;
1317            redo A;
1318          } elsif ($self->{nc} == 0x003E) { # >
1319            !!!cp (31);
1320            !!!parse-error (type => 'empty end tag',
1321                            line => $self->{line_prev}, ## "<" in "</>"
1322                            column => $self->{column_prev} - 1);
1323            $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1327            !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338            !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343        }                                    column => $self->{column_prev} - 1,
1344      } elsif ($self->{state} eq 'tag name') {                                   };
1345        if ($self->{next_input_character} == 0x0009 or # HT          ## NOTE: $self->{nc} is intentionally left as is.
1346            $self->{next_input_character} == 0x000A or # LF          ## Although the "anything else" case of the spec not explicitly
1347            $self->{next_input_character} == 0x000B or # VT          ## states that the next input character is to be reconsumed,
1348            $self->{next_input_character} == 0x000C or # FF          ## it will be included to the |data| of the comment token
1349            $self->{next_input_character} == 0x0020) { # SP          ## generated from the bogus end tag, as defined in the
1350          $self->{state} = 'before attribute name';          ## "bogus comment state" entry.
1351          !!!next-input-character;          redo A;
1352          redo A;        }
1353        } elsif ($self->{next_input_character} == 0x003E) { # >      } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          if ($self->{current_token}->{type} eq 'start tag') {        my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};        if (length $ch) {
1356          } elsif ($self->{current_token}->{type} eq 'end tag') {          my $CH = $ch;
1357            $self->{content_model_flag} = 'PCDATA'; # MUST          $ch =~ tr/a-z/A-Z/;
1358            if ($self->{current_token}->{attributes}) {          my $nch = chr $self->{nc};
1359              !!!parse-error (type => 'end tag attribute');          if ($nch eq $ch or $nch eq $CH) {
1360            }            !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365          } else {          } else {
1366            die "$0: $self->{current_token}->{type}: Unknown token type";            !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375          }          }
1376          $self->{state} = 'data';        } else { # after "<{tag-name}"
1377          !!!next-input-character;          unless ($is_space->{$self->{nc}} or
1378                    {
1379          !!!emit ($self->{current_token}); # start tag or end tag                   0x003E => 1, # >
1380          undef $self->{current_token};                   0x002F => 1, # /
1381                     -1 => 1, # EOF
1382          redo A;                  }->{$self->{nc}}) {
1383        } elsif (0x0041 <= $self->{next_input_character} and            !!!cp (26);
1384                 $self->{next_input_character} <= 0x005A) { # A..Z            ## Reconsume.
1385          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);            $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403          }
1404        } elsif ($self->{state} == TAG_NAME_STATE) {
1405          if ($is_space->{$self->{nc}}) {
1406            !!!cp (34);
1407            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408            !!!next-input-character;
1409            redo A;
1410          } elsif ($self->{nc} == 0x003E) { # >
1411            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412              !!!cp (35);
1413              $self->{last_stag_name} = $self->{ct}->{tag_name};
1414            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1415              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416              #if ($self->{ct}->{attributes}) {
1417              #  ## NOTE: This should never be reached.
1418              #  !!! cp (36);
1419              #  !!! parse-error (type => 'end tag attribute');
1420              #} else {
1421                !!!cp (37);
1422              #}
1423            } else {
1424              die "$0: $self->{ct}->{type}: Unknown token type";
1425            }
1426            $self->{state} = DATA_STATE;
1427            !!!next-input-character;
1428    
1429            !!!emit ($self->{ct}); # start tag or end tag
1430    
1431            redo A;
1432          } elsif (0x0041 <= $self->{nc} and
1433                   $self->{nc} <= 0x005A) { # A..Z
1434            !!!cp (38);
1435            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (39);
1444          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1446            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447              !!!parse-error (type => 'end tag attribute');            #if ($self->{ct}->{attributes}) {
1448            }            #  ## NOTE: This state should never be reached.
1449              #  !!! cp (40);
1450              #  !!! parse-error (type => 'end tag attribute');
1451              #} else {
1452                !!!cp (41);
1453              #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464            !!!cp (42);
1465            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1467          redo A;          redo A;
1468        } else {        } else {
1469          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1470            $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1478            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (46);
1485          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1487            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488              if ($self->{ct}->{attributes}) {
1489                !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491              } else {
1492                !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1506                                value => ''};          $self->{ca}
1507          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510            $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514            !!!cp (50);
1515            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1517          redo A;          redo A;
1518        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (52);
1522          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1524            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525              if ($self->{ct}->{attributes}) {
1526                !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1538    
1539          redo A;          redo A;
1540        } else {        } else {
1541          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1542                                value => ''};               0x0022 => 1, # "
1543          $self->{state} = 'attribute name';               0x0027 => 1, # '
1544                 0x003D => 1, # =
1545                }->{$self->{nc}}) {
1546              !!!cp (55);
1547              !!!parse-error (type => 'bad attribute name');
1548            } else {
1549              !!!cp (56);
1550            }
1551            $self->{ca}
1552                = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555            $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1564            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1565          } else {            ## Discard $self->{ca} # MUST
1566            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1567              = $self->{current_attribute};            !!!cp (58);
1568              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569                = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1574            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580            !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (61);
1589          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591            if ($self->{current_token}->{attributes}) {            !!!cp (62);
1592              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593              if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1608            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613            !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1617          redo A;          redo A;
1618        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (66);
1623          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1625            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626              if ($self->{ct}->{attributes}) {
1627                !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629              } else {
1630                ## NOTE: This state should never be reached.
1631                !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1644                $self->{nc} == 0x0027) { # '
1645              !!!cp (69);
1646              !!!parse-error (type => 'bad attribute name');
1647            } else {
1648              !!!cp (70);
1649            }
1650            $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1657            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662          $self->{state} = 'before attribute value';          !!!cp (72);
1663            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (73);
1669          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1671            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672              if ($self->{ct}->{attributes}) {
1673                !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675              } else {
1676                ## NOTE: This state should never be reached.
1677                !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1691                                value => ''};          $self->{ca}
1692          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695            $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699            !!!cp (77);
1700            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1702          redo A;          redo A;
1703        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (79);
1707          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1709            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710              if ($self->{ct}->{attributes}) {
1711                !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713              } else {
1714                ## NOTE: This state should never be reached.
1715                !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1728                                value => ''};              $self->{nc} == 0x0027) { # '
1729          $self->{state} = 'attribute name';            !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738            $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1744            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1750            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1755            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1760            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1765            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (87);
1767            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1769              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770              if ($self->{ct}->{attributes}) {
1771                !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773              } else {
1774                ## NOTE: This state should never be reached.
1775                !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (90);
1790          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1792            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793              if ($self->{ct}->{attributes}) {
1794                !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796              } else {
1797                ## NOTE: This state should never be reached.
1798                !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1811          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1812              !!!parse-error (type => 'bad attribute value');
1813            } else {
1814              !!!cp (94);
1815            }
1816            $self->{ca}->{value} .= chr ($self->{nc});
1817            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          $self->{state} = 'before attribute name';          !!!cp (95);
1824            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825          !!!next-input-character;          !!!next-input-character;
1826          redo A;          redo A;
1827        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1829          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1830            ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (97);
1842          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1844            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845              if ($self->{ct}->{attributes}) {
1846                !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848              } else {
1849                ## NOTE: This state should never be reached.
1850                !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1863            $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          $self->{state} = 'before attribute name';          !!!cp (101);
1875          !!!next-input-character;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876          redo A;          !!!next-input-character;
1877        } elsif ($self->{next_input_character} == 0x0026) { # &          redo A;
1878          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';        } elsif ($self->{nc} == 0x0026) { # &
1879          $self->{state} = 'entity in attribute value';          !!!cp (102);
1880            ## NOTE: In the spec, the tokenizer is switched to the
1881            ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (103);
1893          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1895            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896              if ($self->{ct}->{attributes}) {
1897                !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899              } else {
1900                ## NOTE: This state should never be reached.
1901                !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1914            $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1925            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1926            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1928            $self->{next_input_character} == 0x0020) { # SP          redo A;
1929          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1930          !!!next-input-character;          !!!cp (108);
1931          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1932        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1933          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1935          !!!next-input-character;          $self->{entity_add} = -1;
1936          redo A;          $self->{prev_state} = $self->{state};
1937        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1938          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1939            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          redo A;
1940          } elsif ($self->{current_token}->{type} eq 'end tag') {        } elsif ($self->{nc} == 0x003E) { # >
1941            $self->{content_model_flag} = 'PCDATA'; # MUST          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942            if ($self->{current_token}->{attributes}) {            !!!cp (109);
1943              $self->{last_stag_name} = $self->{ct}->{tag_name};
1944            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1945              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946              if ($self->{ct}->{attributes}) {
1947                !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949              } else {
1950                ## NOTE: This state should never be reached.
1951                !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_input_character} == 0x003C or # <        } elsif ($self->{nc} == -1) {
                $self->{next_input_character} == -1) {  
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (112);
1966          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1968            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969              if ($self->{ct}->{attributes}) {
1970                !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972              } else {
1973                ## NOTE: This state should never be reached.
1974                !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
         undef $self->{current_token};  
1983    
1984          redo A;          redo A;
1985        } else {        } else {
1986          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1987                 0x0022 => 1, # "
1988                 0x0027 => 1, # '
1989                 0x003D => 1, # =
1990                }->{$self->{nc}}) {
1991              !!!cp (115);
1992              !!!parse-error (type => 'bad attribute value');
1993            } else {
1994              !!!cp (116);
1995            }
1996            $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
2005      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        my $token = $self->_tokenize_attempt_to_consume_an_entity;        if ($is_space->{$self->{nc}}) {
2007            !!!cp (118);
2008            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009            !!!next-input-character;
2010            redo A;
2011          } elsif ($self->{nc} == 0x003E) { # >
2012            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013              !!!cp (119);
2014              $self->{last_stag_name} = $self->{ct}->{tag_name};
2015            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2016              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017              if ($self->{ct}->{attributes}) {
2018                !!!cp (120);
2019                !!!parse-error (type => 'end tag attribute');
2020              } else {
2021                ## NOTE: This state should never be reached.
2022                !!!cp (121);
2023              }
2024            } else {
2025              die "$0: $self->{ct}->{type}: Unknown token type";
2026            }
2027            $self->{state} = DATA_STATE;
2028            !!!next-input-character;
2029    
2030        unless (defined $token) {          !!!emit ($self->{ct}); # start tag or end tag
2031          $self->{current_attribute}->{value} .= '&';  
2032            redo A;
2033          } elsif ($self->{nc} == 0x002F) { # /
2034            !!!cp (122);
2035            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036            !!!next-input-character;
2037            redo A;
2038          } elsif ($self->{nc} == -1) {
2039            !!!parse-error (type => 'unclosed tag');
2040            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041              !!!cp (122.3);
2042              $self->{last_stag_name} = $self->{ct}->{tag_name};
2043            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044              if ($self->{ct}->{attributes}) {
2045                !!!cp (122.1);
2046                !!!parse-error (type => 'end tag attribute');
2047              } else {
2048                ## NOTE: This state should never be reached.
2049                !!!cp (122.2);
2050              }
2051            } else {
2052              die "$0: $self->{ct}->{type}: Unknown token type";
2053            }
2054            $self->{state} = DATA_STATE;
2055            ## Reconsume.
2056            !!!emit ($self->{ct}); # start tag or end tag
2057            redo A;
2058        } else {        } else {
2059          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2060          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2061            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2062            ## reconsume
2063            redo A;
2064        }        }
2065        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066          if ($self->{nc} == 0x003E) { # >
2067            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068              !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070              ## TODO: Different type than slash in start tag
2071              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072              if ($self->{ct}->{attributes}) {
2073                !!!cp ('124.4');
2074                !!!parse-error (type => 'end tag attribute');
2075              } else {
2076                !!!cp ('124.5');
2077              }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2085        # next-input-character is already done          !!!next-input-character;
       redo A;  
     } elsif ($self->{state} eq 'bogus comment') {  
       ## (only happen if PCDATA state)  
         
       my $token = {type => 'comment', data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = 'data';  
           !!!next-input-character;  
   
           !!!emit ($token);  
   
           redo A;  
         } elsif ($self->{next_input_character} == -1) {  
           $self->{state} = 'data';  
           ## reconsume  
2086    
2087            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;          redo A;
2090          } elsif ($self->{nc} == -1) {
2091            !!!parse-error (type => 'unclosed tag');
2092            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093              !!!cp (124.7);
2094              $self->{last_stag_name} = $self->{ct}->{tag_name};
2095            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096              if ($self->{ct}->{attributes}) {
2097                !!!cp (124.5);
2098                !!!parse-error (type => 'end tag attribute');
2099              } else {
2100                ## NOTE: This state should never be reached.
2101                !!!cp (124.6);
2102              }
2103          } else {          } else {
2104            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2105          }          }
2106        } # BC          $self->{state} = DATA_STATE;
2107      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2108            !!!emit ($self->{ct}); # start tag or end tag
2109            redo A;
2110          } else {
2111            !!!cp ('124.4');
2112            !!!parse-error (type => 'nestc');
2113            ## TODO: This error type is wrong.
2114            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2115            ## Reconsume.
2116            redo A;
2117          }
2118        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2120    
2121        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2123                
2124        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127          !!!next-input-character;          !!!next-input-character;
2128          push @next_char, $self->{next_input_character};  
2129          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2130            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2131            $self->{state} = 'comment';        } elsif ($self->{nc} == -1) {
2132            !!!next-input-character;          !!!cp (125);
2133            redo A;          $self->{state} = DATA_STATE;
2134          }          ## reconsume
2135        } elsif ($self->{next_input_character} == 0x0044 or # D  
2136                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2137            redo A;
2138          } else {
2139            !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            ## Stay in the state.
2146          !!!next-input-character;          !!!next-input-character;
2147          push @next_char, $self->{next_input_character};          redo A;
2148          if ($self->{next_input_character} == 0x004F or # O        }
2149              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150            !!!next-input-character;        ## (only happen if PCDATA state)
2151            push @next_char, $self->{next_input_character};        
2152            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2153                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2154              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2155              push @next_char, $self->{next_input_character};          !!!next-input-character;
2156              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2157                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2158                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2159                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2160                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2161                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2162                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2163                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2164                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2165                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2168                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2169                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2170                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2171                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2172                      !!!next-input-character;          redo A;
2173                      redo A;        } else {
2174                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment open');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180        $self->{state} = 'bogus comment';        ## Reconsume.
2181          $self->{state} = BOGUS_COMMENT_STATE;
2182          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190      } elsif ($self->{state} eq 'comment') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2192          $self->{state} = 'comment dash';                                    column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195            !!!next-input-character;
2196            redo A;
2197          } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297        } elsif ($self->{state} == COMMENT_START_STATE) {
2298          if ($self->{nc} == 0x002D) { # -
2299            !!!cp (137);
2300            $self->{state} = COMMENT_START_DASH_STATE;
2301            !!!next-input-character;
2302            redo A;
2303          } elsif ($self->{nc} == 0x003E) { # >
2304            !!!cp (138);
2305            !!!parse-error (type => 'bogus comment');
2306            $self->{state} = DATA_STATE;
2307            !!!next-input-character;
2308    
2309            !!!emit ($self->{ct}); # comment
2310    
2311            redo A;
2312          } elsif ($self->{nc} == -1) {
2313            !!!cp (139);
2314            !!!parse-error (type => 'unclosed comment');
2315            $self->{state} = DATA_STATE;
2316            ## reconsume
2317    
2318            !!!emit ($self->{ct}); # comment
2319    
2320            redo A;
2321          } else {
2322            !!!cp (140);
2323            $self->{ct}->{data} # comment
2324                .= chr ($self->{nc});
2325            $self->{state} = COMMENT_STATE;
2326            !!!next-input-character;
2327            redo A;
2328          }
2329        } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330          if ($self->{nc} == 0x002D) { # -
2331            !!!cp (141);
2332            $self->{state} = COMMENT_END_STATE;
2333            !!!next-input-character;
2334            redo A;
2335          } elsif ($self->{nc} == 0x003E) { # >
2336            !!!cp (142);
2337            !!!parse-error (type => 'bogus comment');
2338            $self->{state} = DATA_STATE;
2339            !!!next-input-character;
2340    
2341            !!!emit ($self->{ct}); # comment
2342    
2343            redo A;
2344          } elsif ($self->{nc} == -1) {
2345            !!!cp (143);
2346            !!!parse-error (type => 'unclosed comment');
2347            $self->{state} = DATA_STATE;
2348            ## reconsume
2349    
2350            !!!emit ($self->{ct}); # comment
2351    
2352            redo A;
2353          } else {
2354            !!!cp (144);
2355            $self->{ct}->{data} # comment
2356                .= '-' . chr ($self->{nc});
2357            $self->{state} = COMMENT_STATE;
2358          !!!next-input-character;          !!!next-input-character;
2359          redo A;          redo A;
2360        } elsif ($self->{next_input_character} == -1) {        }
2361        } elsif ($self->{state} == COMMENT_STATE) {
2362          if ($self->{nc} == 0x002D) { # -
2363            !!!cp (145);
2364            $self->{state} = COMMENT_END_DASH_STATE;
2365            !!!next-input-character;
2366            redo A;
2367          } elsif ($self->{nc} == -1) {
2368            !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2378            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} eq 'comment dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389          $self->{state} = 'comment end';          !!!cp (148);
2390            $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2394            !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2404          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405            $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411          $self->{state} = 'data';          !!!cp (151);
2412            $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2420          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2421                            line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2428            !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
         undef $self->{current_token};  
2434    
2435          redo A;          redo A;
2436        } else {        } else {
2437          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2438          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2439          $self->{state} = 'comment';                          line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442            $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2448            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2449            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'before DOCTYPE name';  
2450          !!!next-input-character;          !!!next-input-character;
2451          redo A;          redo A;
2452        } else {        } else {
2453            !!!cp (156);
2454          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2455          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2456          ## reconsume          ## reconsume
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2461            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif (0x0061 <= $self->{next_input_character} and        } elsif ($self->{nc} == 0x003E) { # >
2466                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (158);
2467  ## ISSUE: "Set the token's name name to the" in the spec          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{current_token} = {type => 'DOCTYPE',          $self->{state} = DATA_STATE;
                           name => chr ($self->{next_input_character} - 0x0020),  
                           error => 1};  
         $self->{state} = 'DOCTYPE name';  
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471            !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == -1) {
2475            !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2478            ## reconsume
2479    
2480            !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482            redo A;
2483          } else {
2484            !!!cp (160);
2485            $self->{ct}->{name} = chr $self->{nc};
2486            delete $self->{ct}->{quirks};
2487            $self->{state} = DOCTYPE_NAME_STATE;
2488            !!!next-input-character;
2489            redo A;
2490          }
2491        } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492    ## ISSUE: Redundant "First," in the spec.
2493          if ($is_space->{$self->{nc}}) {
2494            !!!cp (161);
2495            $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2496            !!!next-input-character;
2497            redo A;
2498          } elsif ($self->{nc} == 0x003E) { # >
2499            !!!cp (162);
2500            $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2507          !!!parse-error (type => 'no DOCTYPE name');          !!!cp (163);
2508          $self->{state} = 'data';          !!!parse-error (type => 'unclosed DOCTYPE');
2509            $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          !!!emit ({type => 'DOCTYPE', name => '', error => 1});          $self->{ct}->{quirks} = 1;
2513            !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          $self->{current_token} = {type => 'DOCTYPE',          !!!cp (164);
2518                            name => chr ($self->{next_input_character}),          $self->{ct}->{name}
2519                            error => 1};            .= chr ($self->{nc}); # DOCTYPE
2520  ## ISSUE: "Set the token's name name to the" in the spec          ## Stay in the state
         $self->{state} = 'DOCTYPE name';  
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2526            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
2527            $self->{next_input_character} == 0x000B or # VT          ## Stay in the state
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE  
         $self->{state} = 'after DOCTYPE name';  
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE          !!!cp (166);
2532          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536          undef $self->{current_token};  
2537            redo A;
2538          } elsif ($self->{nc} == -1) {
2539            !!!cp (167);
2540            !!!parse-error (type => 'unclosed DOCTYPE');
2541            $self->{state} = DATA_STATE;
2542            ## reconsume
2543    
2544            $self->{ct}->{quirks} = 1;
2545            !!!emit ($self->{ct}); # DOCTYPE
2546    
2547            redo A;
2548          } elsif ($self->{nc} == 0x0050 or # P
2549                   $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552            !!!next-input-character;
2553            redo A;
2554          } elsif ($self->{nc} == 0x0053 or # S
2555                   $self->{nc} == 0x0073) { # s
2556            $self->{state} = SYSTEM_STATE;
2557            $self->{s_kwd} = chr $self->{nc};
2558            !!!next-input-character;
2559            redo A;
2560          } else {
2561            !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566            !!!next-input-character;
2567            redo A;
2568          }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596          redo A;          redo A;
2597        } elsif (0x0061 <= $self->{next_input_character} and        } else {
2598                 $self->{next_input_character} <= 0x007A) { # a..z          !!!cp (169);
2599          $self->{current_token}->{name} .= chr ($self->{next_input_character} - 0x0020); # DOCTYPE          !!!parse-error (type => 'string after DOCTYPE name',
2600          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');                          line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604            $self->{state} = BOGUS_DOCTYPE_STATE;
2605            ## Reconsume.
2606            redo A;
2607          }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643            $self->{state} = BOGUS_DOCTYPE_STATE;
2644            ## Reconsume.
2645            redo A;
2646          }
2647        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648          if ($is_space->{$self->{nc}}) {
2649            !!!cp (181);
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} eq 0x0022) { # "
2654            !!!cp (182);
2655            $self->{ct}->{pubid} = ''; # DOCTYPE
2656            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657            !!!next-input-character;
2658            redo A;
2659          } elsif ($self->{nc} eq 0x0027) { # '
2660            !!!cp (183);
2661            $self->{ct}->{pubid} = ''; # DOCTYPE
2662            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663            !!!next-input-character;
2664            redo A;
2665          } elsif ($self->{nc} eq 0x003E) { # >
2666            !!!cp (184);
2667            !!!parse-error (type => 'no PUBLIC literal');
2668    
2669            $self->{state} = DATA_STATE;
2670            !!!next-input-character;
2671    
2672            $self->{ct}->{quirks} = 1;
2673            !!!emit ($self->{ct}); # DOCTYPE
2674    
2675            redo A;
2676          } elsif ($self->{nc} == -1) {
2677            !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679          $self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML'); # DOCTYPE  
2680          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2681            ## reconsume
2682    
2683            $self->{ct}->{quirks} = 1;
2684            !!!emit ($self->{ct}); # DOCTYPE
2685    
2686            redo A;
2687          } else {
2688            !!!cp (186);
2689            !!!parse-error (type => 'string after PUBLIC');
2690            $self->{ct}->{quirks} = 1;
2691    
2692            $self->{state} = BOGUS_DOCTYPE_STATE;
2693            !!!next-input-character;
2694            redo A;
2695          }
2696        } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697          if ($self->{nc} == 0x0022) { # "
2698            !!!cp (187);
2699            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700            !!!next-input-character;
2701            redo A;
2702          } elsif ($self->{nc} == 0x003E) { # >
2703            !!!cp (188);
2704            !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706            $self->{state} = DATA_STATE;
2707            !!!next-input-character;
2708    
2709            $self->{ct}->{quirks} = 1;
2710            !!!emit ($self->{ct}); # DOCTYPE
2711    
2712            redo A;
2713          } elsif ($self->{nc} == -1) {
2714            !!!cp (189);
2715            !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717            $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          !!!emit ($self->{current_token});          $self->{ct}->{quirks} = 1;
2721          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          $self->{current_token}->{name}          !!!cp (190);
2726            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{pubid} # DOCTYPE
2727          #$self->{current_token}->{error} = ($self->{current_token}->{name} ne 'HTML');              .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{nc} == 0x0027) { # '
2737            $self->{next_input_character} == 0x000A or # LF          !!!cp (191);
2738            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
2740            $self->{next_input_character} == 0x0020) { # SP          redo A;
2741          } elsif ($self->{nc} == 0x003E) { # >
2742            !!!cp (192);
2743            !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745            $self->{state} = DATA_STATE;
2746            !!!next-input-character;
2747    
2748            $self->{ct}->{quirks} = 1;
2749            !!!emit ($self->{ct}); # DOCTYPE
2750    
2751            redo A;
2752          } elsif ($self->{nc} == -1) {
2753            !!!cp (193);
2754            !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756            $self->{state} = DATA_STATE;
2757            ## reconsume
2758    
2759            $self->{ct}->{quirks} = 1;
2760            !!!emit ($self->{ct}); # DOCTYPE
2761    
2762            redo A;
2763          } else {
2764            !!!cp (194);
2765            $self->{ct}->{pubid} # DOCTYPE
2766                .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        } elsif ($self->{next_input_character} == 0x003E) { # >        }
2774          $self->{state} = 'data';      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775          if ($is_space->{$self->{nc}}) {
2776            !!!cp (195);
2777            ## Stay in the state
2778            !!!next-input-character;
2779            redo A;
2780          } elsif ($self->{nc} == 0x0022) { # "
2781            !!!cp (196);
2782            $self->{ct}->{sysid} = ''; # DOCTYPE
2783            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784            !!!next-input-character;
2785            redo A;
2786          } elsif ($self->{nc} == 0x0027) { # '
2787            !!!cp (197);
2788            $self->{ct}->{sysid} = ''; # DOCTYPE
2789            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790            !!!next-input-character;
2791            redo A;
2792          } elsif ($self->{nc} == 0x003E) { # >
2793            !!!cp (198);
2794            $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
         undef $self->{current_token};  
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2801            !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803          $self->{state} = 'data';  
2804            $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2808          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812          !!!parse-error (type => 'string after DOCTYPE name');          !!!cp (200);
2813          $self->{current_token}->{error} = 1; # DOCTYPE          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2815    
2816            $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ($self->{next_input_character} == 0x003E) { # >        if ($is_space->{$self->{nc}}) {
2822          $self->{state} = 'data';          !!!cp (201);
2823            ## Stay in the state
2824            !!!next-input-character;
2825            redo A;
2826          } elsif ($self->{nc} == 0x0022) { # "
2827            !!!cp (202);
2828            $self->{ct}->{sysid} = ''; # DOCTYPE
2829            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830            !!!next-input-character;
2831            redo A;
2832          } elsif ($self->{nc} == 0x0027) { # '
2833            !!!cp (203);
2834            $self->{ct}->{sysid} = ''; # DOCTYPE
2835            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836            !!!next-input-character;
2837            redo A;
2838          } elsif ($self->{nc} == 0x003E) { # >
2839            !!!cp (204);
2840            !!!parse-error (type => 'no SYSTEM literal');
2841            $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2845          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2849            !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851          $self->{state} = 'data';  
2852            $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          !!!emit ($self->{current_token}); # DOCTYPE          $self->{ct}->{quirks} = 1;
2856          undef $self->{current_token};          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860            !!!cp (206);
2861            !!!parse-error (type => 'string after SYSTEM');
2862            $self->{ct}->{quirks} = 1;
2863    
2864            $self->{state} = BOGUS_DOCTYPE_STATE;
2865            !!!next-input-character;
2866            redo A;
2867          }
2868        } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869          if ($self->{nc} == 0x0022) { # "
2870            !!!cp (207);
2871            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872            !!!next-input-character;
2873            redo A;
2874          } elsif ($self->{nc} == 0x003E) { # >
2875            !!!cp (208);
2876            !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878            $self->{state} = DATA_STATE;
2879            !!!next-input-character;
2880    
2881            $self->{ct}->{quirks} = 1;
2882            !!!emit ($self->{ct}); # DOCTYPE
2883    
2884            redo A;
2885          } elsif ($self->{nc} == -1) {
2886            !!!cp (209);
2887            !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889            $self->{state} = DATA_STATE;
2890            ## reconsume
2891    
2892            $self->{ct}->{quirks} = 1;
2893            !!!emit ($self->{ct}); # DOCTYPE
2894    
2895            redo A;
2896          } else {
2897            !!!cp (210);
2898            $self->{ct}->{sysid} # DOCTYPE
2899                .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } else {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        die "$0: $self->{state}: Unknown state";        if ($self->{nc} == 0x0027) { # '
2909      }          !!!cp (211);
2910    } # A            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911            !!!next-input-character;
2912            redo A;
2913          } elsif ($self->{nc} == 0x003E) { # >
2914            !!!cp (212);
2915            !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917    die "$0: _get_next_token: unexpected case";          $self->{state} = DATA_STATE;
2918  } # _get_next_token          !!!next-input-character;
2919    
2920  sub _tokenize_attempt_to_consume_an_entity ($) {          $self->{ct}->{quirks} = 1;
2921    my $self = shift;          !!!emit ($self->{ct}); # DOCTYPE
     
   if ($self->{next_input_character} == 0x0023) { # #  
     !!!next-input-character;  
     if ($self->{next_input_character} == 0x0078 or # x  
         $self->{next_input_character} == 0x0058) { # X  
       my $num;  
       X: {  
         my $x_char = $self->{next_input_character};  
         !!!next-input-character;  
         if (0x0030 <= $self->{next_input_character} and  
             $self->{next_input_character} <= 0x0039) { # 0..9  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0030;  
           redo X;  
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           ## ISSUE: the spec says U+0078, which is apparently incorrect  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           ## ISSUE: the spec says U+0058, which is apparently incorrect  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $num) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           $self->{next_input_character} = 0x0023; # #  
           !!!back-next-input-character ($x_char);  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
         } else {  
           !!!parse-error (type => 'no refc');  
         }  
2922    
2923          ## TODO: check the definition for |a valid Unicode character|.          redo A;
2924          ## <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8189>        } elsif ($self->{nc} == -1) {
2925          if ($num > 1114111 or $num == 0) {          !!!cp (213);
2926            $num = 0xFFFD; # REPLACEMENT CHARACTER          !!!parse-error (type => 'unclosed SYSTEM literal');
2927            ## ISSUE: Why this is not an error?  
2928          } elsif (0x80 <= $num and $num <= 0x9F) {          $self->{state} = DATA_STATE;
2929            !!!parse-error (type => sprintf 'c1 entity:U+%04X', $num);          ## reconsume
2930            $num = $c1_entity_char->{$num};  
2931          }          $self->{ct}->{quirks} = 1;
2932            !!!emit ($self->{ct}); # DOCTYPE
2933          return {type => 'character', data => chr $num};  
2934        } # X          redo A;
2935      } elsif (0x0030 <= $self->{next_input_character} and        } else {
2936               $self->{next_input_character} <= 0x0039) { # 0..9          !!!cp (214);
2937        my $code = $self->{next_input_character} - 0x0030;          $self->{ct}->{sysid} # DOCTYPE
2938        !!!next-input-character;              .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942            ## Stay in the state
2943            !!!next-input-character;
2944            redo A;
2945          }
2946        } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947          if ($is_space->{$self->{nc}}) {
2948            !!!cp (215);
2949            ## Stay in the state
2950            !!!next-input-character;
2951            redo A;
2952          } elsif ($self->{nc} == 0x003E) { # >
2953            !!!cp (216);
2954            $self->{state} = DATA_STATE;
2955            !!!next-input-character;
2956    
2957            !!!emit ($self->{ct}); # DOCTYPE
2958    
2959            redo A;
2960          } elsif ($self->{nc} == -1) {
2961            !!!cp (217);
2962            !!!parse-error (type => 'unclosed DOCTYPE');
2963            $self->{state} = DATA_STATE;
2964            ## reconsume
2965    
2966            $self->{ct}->{quirks} = 1;
2967            !!!emit ($self->{ct}); # DOCTYPE
2968    
2969            redo A;
2970          } else {
2971            !!!cp (218);
2972            !!!parse-error (type => 'string after SYSTEM literal');
2973            #$self->{ct}->{quirks} = 1;
2974    
2975            $self->{state} = BOGUS_DOCTYPE_STATE;
2976            !!!next-input-character;
2977            redo A;
2978          }
2979        } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980          if ($self->{nc} == 0x003E) { # >
2981            !!!cp (219);
2982            $self->{state} = DATA_STATE;
2983            !!!next-input-character;
2984    
2985            !!!emit ($self->{ct}); # DOCTYPE
2986    
2987            redo A;
2988          } elsif ($self->{nc} == -1) {
2989            !!!cp (220);
2990            $self->{state} = DATA_STATE;
2991            ## reconsume
2992    
2993            !!!emit ($self->{ct}); # DOCTYPE
2994    
2995            redo A;
2996          } else {
2997            !!!cp (221);
2998            my $s = '';
2999            $self->{read_until}->($s, q[>], 0);
3000    
3001            ## Stay in the state
3002            !!!next-input-character;
3003            redo A;
3004          }
3005        } elsif ($self->{state} == CDATA_SECTION_STATE) {
3006          ## NOTE: "CDATA section state" in the state is jointly implemented
3007          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3008          ## and |CDATA_SECTION_MSE2_STATE|.
3009                
3010        while (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x005D) { # ]
3011                  $self->{next_input_character} <= 0x0039) { # 0..9          !!!cp (221.1);
3012          $code *= 10;          $self->{state} = CDATA_SECTION_MSE1_STATE;
         $code += $self->{next_input_character} - 0x0030;  
           
3013          !!!next-input-character;          !!!next-input-character;
3014            redo A;
3015          } elsif ($self->{nc} == -1) {
3016            $self->{state} = DATA_STATE;
3017            !!!next-input-character;
3018            if (length $self->{ct}->{data}) { # character
3019              !!!cp (221.2);
3020              !!!emit ($self->{ct}); # character
3021            } else {
3022              !!!cp (221.3);
3023              ## No token to emit. $self->{ct} is discarded.
3024            }        
3025            redo A;
3026          } else {
3027            !!!cp (221.4);
3028            $self->{ct}->{data} .= chr $self->{nc};
3029            $self->{read_until}->($self->{ct}->{data},
3030                                  q<]>,
3031                                  length $self->{ct}->{data});
3032    
3033            ## Stay in the state.
3034            !!!next-input-character;
3035            redo A;
3036        }        }
3037    
3038        if ($self->{next_input_character} == 0x003B) { # ;        ## ISSUE: "text tokens" in spec.
3039        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3040          if ($self->{nc} == 0x005D) { # ]
3041            !!!cp (221.5);
3042            $self->{state} = CDATA_SECTION_MSE2_STATE;
3043          !!!next-input-character;          !!!next-input-character;
3044            redo A;
3045        } else {        } else {
3046          !!!parse-error (type => 'no refc');          !!!cp (221.6);
3047            $self->{ct}->{data} .= ']';
3048            $self->{state} = CDATA_SECTION_STATE;
3049            ## Reconsume.
3050            redo A;
3051          }
3052        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3053          if ($self->{nc} == 0x003E) { # >
3054            $self->{state} = DATA_STATE;
3055            !!!next-input-character;
3056            if (length $self->{ct}->{data}) { # character
3057              !!!cp (221.7);
3058              !!!emit ($self->{ct}); # character
3059            } else {
3060              !!!cp (221.8);
3061              ## No token to emit. $self->{ct} is discarded.
3062            }
3063            redo A;
3064          } elsif ($self->{nc} == 0x005D) { # ]
3065            !!!cp (221.9); # character
3066            $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3067            ## Stay in the state.
3068            !!!next-input-character;
3069            redo A;
3070          } else {
3071            !!!cp (221.11);
3072            $self->{ct}->{data} .= ']]'; # character
3073            $self->{state} = CDATA_SECTION_STATE;
3074            ## Reconsume.
3075            redo A;
3076          }
3077        } elsif ($self->{state} == ENTITY_STATE) {
3078          if ($is_space->{$self->{nc}} or
3079              {
3080                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3081                $self->{entity_add} => 1,
3082              }->{$self->{nc}}) {
3083            !!!cp (1001);
3084            ## Don't consume
3085            ## No error
3086            ## Return nothing.
3087            #
3088          } elsif ($self->{nc} == 0x0023) { # #
3089            !!!cp (999);
3090            $self->{state} = ENTITY_HASH_STATE;
3091            $self->{s_kwd} = '#';
3092            !!!next-input-character;
3093            redo A;
3094          } elsif ((0x0041 <= $self->{nc} and
3095                    $self->{nc} <= 0x005A) or # A..Z
3096                   (0x0061 <= $self->{nc} and
3097                    $self->{nc} <= 0x007A)) { # a..z
3098            !!!cp (998);
3099            require Whatpm::_NamedEntityList;
3100            $self->{state} = ENTITY_NAME_STATE;
3101            $self->{s_kwd} = chr $self->{nc};
3102            $self->{entity__value} = $self->{s_kwd};
3103            $self->{entity__match} = 0;
3104            !!!next-input-character;
3105            redo A;
3106          } else {
3107            !!!cp (1027);
3108            !!!parse-error (type => 'bare ero');
3109            ## Return nothing.
3110            #
3111        }        }
3112    
3113        ## TODO: check the definition for |a valid Unicode character|.        ## NOTE: No character is consumed by the "consume a character
3114        if ($code > 1114111 or $code == 0) {        ## reference" algorithm.  In other word, there is an "&" character
3115          $code = 0xFFFD; # REPLACEMENT CHARACTER        ## that does not introduce a character reference, which would be
3116          ## ISSUE: Why this is not an error?        ## appended to the parent element or the attribute value in later
3117        } elsif (0x80 <= $code and $code <= 0x9F) {        ## process of the tokenizer.
3118          !!!parse-error (type => sprintf 'c1 entity:U+%04X', $code);  
3119          $code = $c1_entity_char->{$code};        if ($self->{prev_state} == DATA_STATE) {
3120            !!!cp (997);
3121            $self->{state} = $self->{prev_state};
3122            ## Reconsume.
3123            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3124                      line => $self->{line_prev},
3125                      column => $self->{column_prev},
3126                     });
3127            redo A;
3128          } else {
3129            !!!cp (996);
3130            $self->{ca}->{value} .= '&';
3131            $self->{state} = $self->{prev_state};
3132            ## Reconsume.
3133            redo A;
3134          }
3135        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3136          if ($self->{nc} == 0x0078 or # x
3137              $self->{nc} == 0x0058) { # X
3138            !!!cp (995);
3139            $self->{state} = HEXREF_X_STATE;
3140            $self->{s_kwd} .= chr $self->{nc};
3141            !!!next-input-character;
3142            redo A;
3143          } elsif (0x0030 <= $self->{nc} and
3144                   $self->{nc} <= 0x0039) { # 0..9
3145            !!!cp (994);
3146            $self->{state} = NCR_NUM_STATE;
3147            $self->{s_kwd} = $self->{nc} - 0x0030;
3148            !!!next-input-character;
3149            redo A;
3150          } else {
3151            !!!parse-error (type => 'bare nero',
3152                            line => $self->{line_prev},
3153                            column => $self->{column_prev} - 1);
3154    
3155            ## NOTE: According to the spec algorithm, nothing is returned,
3156            ## and then "&#" is appended to the parent element or the attribute
3157            ## value in the later processing.
3158    
3159            if ($self->{prev_state} == DATA_STATE) {
3160              !!!cp (1019);
3161              $self->{state} = $self->{prev_state};
3162              ## Reconsume.
3163              !!!emit ({type => CHARACTER_TOKEN,
3164                        data => '&#',
3165                        line => $self->{line_prev},
3166                        column => $self->{column_prev} - 1,
3167                       });
3168              redo A;
3169            } else {
3170              !!!cp (993);
3171              $self->{ca}->{value} .= '&#';
3172              $self->{state} = $self->{prev_state};
3173              ## Reconsume.
3174              redo A;
3175            }
3176        }        }
3177              } elsif ($self->{state} == NCR_NUM_STATE) {
3178        return {type => 'character', data => chr $code};        if (0x0030 <= $self->{nc} and
3179      } else {            $self->{nc} <= 0x0039) { # 0..9
3180        !!!parse-error (type => 'bare nero');          !!!cp (1012);
3181        !!!back-next-input-character ($self->{next_input_character});          $self->{s_kwd} *= 10;
3182        $self->{next_input_character} = 0x0023; # #          $self->{s_kwd} += $self->{nc} - 0x0030;
3183        return undef;          
3184      }          ## Stay in the state.
3185    } elsif ((0x0041 <= $self->{next_input_character} and          !!!next-input-character;
3186              $self->{next_input_character} <= 0x005A) or          redo A;
3187             (0x0061 <= $self->{next_input_character} and        } elsif ($self->{nc} == 0x003B) { # ;
3188              $self->{next_input_character} <= 0x007A)) {          !!!cp (1013);
3189      my $entity_name = chr $self->{next_input_character};          !!!next-input-character;
3190      !!!next-input-character;          #
   
     my $value = $entity_name;  
     my $match;  
   
     while (length $entity_name < 10 and  
            ## NOTE: Some number greater than the maximum length of entity name  
            ((0x0041 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x005A) or  
             (0x0061 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x007A) or  
             (0x0030 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x0039))) {  
       $entity_name .= chr $self->{next_input_character};  
       if (defined $entity_char->{$entity_name}) {  
         $value = $entity_char->{$entity_name};  
         $match = 1;  
3191        } else {        } else {
3192          $value .= chr $self->{next_input_character};          !!!cp (1014);
3193            !!!parse-error (type => 'no refc');
3194            ## Reconsume.
3195            #
3196        }        }
3197        !!!next-input-character;  
3198      }        my $code = $self->{s_kwd};
3199              my $l = $self->{line_prev};
3200      if ($match) {        my $c = $self->{column_prev};
3201        if ($self->{next_input_character} == 0x003B) { # ;        if ($charref_map->{$code}) {
3202            !!!cp (1015);
3203            !!!parse-error (type => 'invalid character reference',
3204                            text => (sprintf 'U+%04X', $code),
3205                            line => $l, column => $c);
3206            $code = $charref_map->{$code};
3207          } elsif ($code > 0x10FFFF) {
3208            !!!cp (1016);
3209            !!!parse-error (type => 'invalid character reference',
3210                            text => (sprintf 'U-%08X', $code),
3211                            line => $l, column => $c);
3212            $code = 0xFFFD;
3213          }
3214    
3215          if ($self->{prev_state} == DATA_STATE) {
3216            !!!cp (992);
3217            $self->{state} = $self->{prev_state};
3218            ## Reconsume.
3219            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3220                      line => $l, column => $c,
3221                     });
3222            redo A;
3223          } else {
3224            !!!cp (991);
3225            $self->{ca}->{value} .= chr $code;
3226            $self->{ca}->{has_reference} = 1;
3227            $self->{state} = $self->{prev_state};
3228            ## Reconsume.
3229            redo A;
3230          }
3231        } elsif ($self->{state} == HEXREF_X_STATE) {
3232          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3233              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3234              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3235            # 0..9, A..F, a..f
3236            !!!cp (990);
3237            $self->{state} = HEXREF_HEX_STATE;
3238            $self->{s_kwd} = 0;
3239            ## Reconsume.
3240            redo A;
3241          } else {
3242            !!!parse-error (type => 'bare hcro',
3243                            line => $self->{line_prev},
3244                            column => $self->{column_prev} - 2);
3245    
3246            ## NOTE: According to the spec algorithm, nothing is returned,
3247            ## and then "&#" followed by "X" or "x" is appended to the parent
3248            ## element or the attribute value in the later processing.
3249    
3250            if ($self->{prev_state} == DATA_STATE) {
3251              !!!cp (1005);
3252              $self->{state} = $self->{prev_state};
3253              ## Reconsume.
3254              !!!emit ({type => CHARACTER_TOKEN,
3255                        data => '&' . $self->{s_kwd},
3256                        line => $self->{line_prev},
3257                        column => $self->{column_prev} - length $self->{s_kwd},
3258                       });
3259              redo A;
3260            } else {
3261              !!!cp (989);
3262              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3263              $self->{state} = $self->{prev_state};
3264              ## Reconsume.
3265              redo A;
3266            }
3267          }
3268        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3269          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3270            # 0..9
3271            !!!cp (1002);
3272            $self->{s_kwd} *= 0x10;
3273            $self->{s_kwd} += $self->{nc} - 0x0030;
3274            ## Stay in the state.
3275            !!!next-input-character;
3276            redo A;
3277          } elsif (0x0061 <= $self->{nc} and
3278                   $self->{nc} <= 0x0066) { # a..f
3279            !!!cp (1003);
3280            $self->{s_kwd} *= 0x10;
3281            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3282            ## Stay in the state.
3283            !!!next-input-character;
3284            redo A;
3285          } elsif (0x0041 <= $self->{nc} and
3286                   $self->{nc} <= 0x0046) { # A..F
3287            !!!cp (1004);
3288            $self->{s_kwd} *= 0x10;
3289            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3290            ## Stay in the state.
3291            !!!next-input-character;
3292            redo A;
3293          } elsif ($self->{nc} == 0x003B) { # ;
3294            !!!cp (1006);
3295          !!!next-input-character;          !!!next-input-character;
3296            #
3297        } else {        } else {
3298          !!!parse-error (type => 'refc');          !!!cp (1007);
3299            !!!parse-error (type => 'no refc',
3300                            line => $self->{line},
3301                            column => $self->{column});
3302            ## Reconsume.
3303            #
3304          }
3305    
3306          my $code = $self->{s_kwd};
3307          my $l = $self->{line_prev};
3308          my $c = $self->{column_prev};
3309          if ($charref_map->{$code}) {
3310            !!!cp (1008);
3311            !!!parse-error (type => 'invalid character reference',
3312                            text => (sprintf 'U+%04X', $code),
3313                            line => $l, column => $c);
3314            $code = $charref_map->{$code};
3315          } elsif ($code > 0x10FFFF) {
3316            !!!cp (1009);
3317            !!!parse-error (type => 'invalid character reference',
3318                            text => (sprintf 'U-%08X', $code),
3319                            line => $l, column => $c);
3320            $code = 0xFFFD;
3321          }
3322    
3323          if ($self->{prev_state} == DATA_STATE) {
3324            !!!cp (988);
3325            $self->{state} = $self->{prev_state};
3326            ## Reconsume.
3327            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3328                      line => $l, column => $c,
3329                     });
3330            redo A;
3331          } else {
3332            !!!cp (987);
3333            $self->{ca}->{value} .= chr $code;
3334            $self->{ca}->{has_reference} = 1;
3335            $self->{state} = $self->{prev_state};
3336            ## Reconsume.
3337            redo A;
3338          }
3339        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3340          if (length $self->{s_kwd} < 30 and
3341              ## NOTE: Some number greater than the maximum length of entity name
3342              ((0x0041 <= $self->{nc} and # a
3343                $self->{nc} <= 0x005A) or # x
3344               (0x0061 <= $self->{nc} and # a
3345                $self->{nc} <= 0x007A) or # z
3346               (0x0030 <= $self->{nc} and # 0
3347                $self->{nc} <= 0x0039) or # 9
3348               $self->{nc} == 0x003B)) { # ;
3349            our $EntityChar;
3350            $self->{s_kwd} .= chr $self->{nc};
3351            if (defined $EntityChar->{$self->{s_kwd}}) {
3352              if ($self->{nc} == 0x003B) { # ;
3353                !!!cp (1020);
3354                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3355                $self->{entity__match} = 1;
3356                !!!next-input-character;
3357                #
3358              } else {
3359                !!!cp (1021);
3360                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3361                $self->{entity__match} = -1;
3362                ## Stay in the state.
3363                !!!next-input-character;
3364                redo A;
3365              }
3366            } else {
3367              !!!cp (1022);
3368              $self->{entity__value} .= chr $self->{nc};
3369              $self->{entity__match} *= 2;
3370              ## Stay in the state.
3371              !!!next-input-character;
3372              redo A;
3373            }
3374        }        }
3375    
3376        return {type => 'character', data => $value};        my $data;
3377          my $has_ref;
3378          if ($self->{entity__match} > 0) {
3379            !!!cp (1023);
3380            $data = $self->{entity__value};
3381            $has_ref = 1;
3382            #
3383          } elsif ($self->{entity__match} < 0) {
3384            !!!parse-error (type => 'no refc');
3385            if ($self->{prev_state} != DATA_STATE and # in attribute
3386                $self->{entity__match} < -1) {
3387              !!!cp (1024);
3388              $data = '&' . $self->{s_kwd};
3389              #
3390            } else {
3391              !!!cp (1025);
3392              $data = $self->{entity__value};
3393              $has_ref = 1;
3394              #
3395            }
3396          } else {
3397            !!!cp (1026);
3398            !!!parse-error (type => 'bare ero',
3399                            line => $self->{line_prev},
3400                            column => $self->{column_prev} - length $self->{s_kwd});
3401            $data = '&' . $self->{s_kwd};
3402            #
3403          }
3404      
3405          ## NOTE: In these cases, when a character reference is found,
3406          ## it is consumed and a character token is returned, or, otherwise,
3407          ## nothing is consumed and returned, according to the spec algorithm.
3408          ## In this implementation, anything that has been examined by the
3409          ## tokenizer is appended to the parent element or the attribute value
3410          ## as string, either literal string when no character reference or
3411          ## entity-replaced string otherwise, in this stage, since any characters
3412          ## that would not be consumed are appended in the data state or in an
3413          ## appropriate attribute value state anyway.
3414    
3415          if ($self->{prev_state} == DATA_STATE) {
3416            !!!cp (986);
3417            $self->{state} = $self->{prev_state};
3418            ## Reconsume.
3419            !!!emit ({type => CHARACTER_TOKEN,
3420                      data => $data,
3421                      line => $self->{line_prev},
3422                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3423                     });
3424            redo A;
3425          } else {
3426            !!!cp (985);
3427            $self->{ca}->{value} .= $data;
3428            $self->{ca}->{has_reference} = 1 if $has_ref;
3429            $self->{state} = $self->{prev_state};
3430            ## Reconsume.
3431            redo A;
3432          }
3433      } else {      } else {
3434        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       !!!back-token ({type => 'character', data => $value});  
       return undef;  
3435      }      }
3436    } else {    } # A  
3437      ## no characters are consumed  
3438      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3439      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3440    
3441  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3442    my $self = shift;    my $self = shift;
# Line 1667  sub _initialize_tree_constructor ($) { Line 3444  sub _initialize_tree_constructor ($) {
3444    $self->{document}->strict_error_checking (0);    $self->{document}->strict_error_checking (0);
3445    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3446    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3447    ## TODO: Mark the Document as an HTML document # MUST    $self->{document}->manakai_is_html (1); # MUST
3448      $self->{document}->set_user_data (manakai_source_line => 1);
3449      $self->{document}->set_user_data (manakai_source_column => 1);
3450  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3451    
3452  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1687  sub _construct_tree ($) { Line 3466  sub _construct_tree ($) {
3466    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3467    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3468    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
3469        
3470    !!!next-token;    !!!next-token;
3471    
   $self->{insertion_mode} = 'before head';  
3472    undef $self->{form_element};    undef $self->{form_element};
3473    undef $self->{head_element};    undef $self->{head_element};
3474      undef $self->{head_element_inserted};
3475    $self->{open_elements} = [];    $self->{open_elements} = [];
3476    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3477    
3478      ## NOTE: The "initial" insertion mode.
3479    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3480    
3481      ## NOTE: The "before html" insertion mode.
3482    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3483      $self->{insertion_mode} = BEFORE_HEAD_IM;
3484    
3485      ## NOTE: The "before head" insertion mode and so on.
3486    $self->_tree_construction_main;    $self->_tree_construction_main;
3487  } # _construct_tree  } # _construct_tree
3488    
3489  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3490    my $self = shift;    my $self = shift;
3491    B: {  
3492        if ($token->{type} eq 'DOCTYPE') {    ## NOTE: "initial" insertion mode
3493          if ($token->{error}) {  
3494            ## ISSUE: Spec currently left this case undefined.    INITIAL: {
3495            !!!parse-error (type => 'bogus DOCTYPE');      if ($token->{type} == DOCTYPE_TOKEN) {
3496          }        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3497          my $doctype = $self->{document}->create_document_type_definition        ## error, switch to a conformance checking mode for another
3498            ($token->{name});        ## language.
3499          $self->{document}->append_child ($doctype);        my $doctype_name = $token->{name};
3500          #$phase = 'root element';        $doctype_name = '' unless defined $doctype_name;
3501          !!!next-token;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3502          #redo B;        if (not defined $token->{name} or # <!DOCTYPE>
3503          return;            defined $token->{sysid}) {
3504        } elsif ({          !!!cp ('t1');
3505                  comment => 1,          !!!parse-error (type => 'not HTML5', token => $token);
3506                  'start tag' => 1,        } elsif ($doctype_name ne 'HTML') {
3507                  'end tag' => 1,          !!!cp ('t2');
3508                  'end-of-file' => 1,          !!!parse-error (type => 'not HTML5', token => $token);
3509                 }->{$token->{type}}) {        } elsif (defined $token->{pubid}) {
3510          ## ISSUE: Spec currently left this case undefined.          if ($token->{pubid} eq 'XSLT-compat') {
3511          !!!parse-error (type => 'missing DOCTYPE');            !!!cp ('t1.2');
3512          #$phase = 'root element';            !!!parse-error (type => 'XSLT-compat', token => $token,
3513          ## reprocess                            level => $self->{level}->{should});
3514          #redo B;          } else {
3515          return;            !!!parse-error (type => 'not HTML5', token => $token);
3516        } elsif ($token->{type} eq 'character') {          }
3517          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {        } else {
3518            $self->{document}->manakai_append_text ($1);          !!!cp ('t3');
3519            ## ISSUE: DOM3 Core does not allow Document > Text          #
3520            unless (length $token->{data}) {        }
3521              ## Stay in the phase        
3522              !!!next-token;        my $doctype = $self->{document}->create_document_type_definition
3523              redo B;          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3524          ## NOTE: Default value for both |public_id| and |system_id| attributes
3525          ## are empty strings, so that we don't set any value in missing cases.
3526          $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3527          $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3528          ## NOTE: Other DocumentType attributes are null or empty lists.
3529          ## ISSUE: internalSubset = null??
3530          $self->{document}->append_child ($doctype);
3531          
3532          if ($token->{quirks} or $doctype_name ne 'HTML') {
3533            !!!cp ('t4');
3534            $self->{document}->manakai_compat_mode ('quirks');
3535          } elsif (defined $token->{pubid}) {
3536            my $pubid = $token->{pubid};
3537            $pubid =~ tr/a-z/A-z/;
3538            my $prefix = [
3539              "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3540              "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3541              "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3542              "-//IETF//DTD HTML 2.0 LEVEL 1//",
3543              "-//IETF//DTD HTML 2.0 LEVEL 2//",
3544              "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3545              "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3546              "-//IETF//DTD HTML 2.0 STRICT//",
3547              "-//IETF//DTD HTML 2.0//",
3548              "-//IETF//DTD HTML 2.1E//",
3549              "-//IETF//DTD HTML 3.0//",
3550              "-//IETF//DTD HTML 3.2 FINAL//",
3551              "-//IETF//DTD HTML 3.2//",
3552              "-//IETF//DTD HTML 3//",
3553              "-//IETF//DTD HTML LEVEL 0//",
3554              "-//IETF//DTD HTML LEVEL 1//",
3555              "-//IETF//DTD HTML LEVEL 2//",
3556              "-//IETF//DTD HTML LEVEL 3//",
3557              "-//IETF//DTD HTML STRICT LEVEL 0//",
3558              "-//IETF//DTD HTML STRICT LEVEL 1//",
3559              "-//IETF//DTD HTML STRICT LEVEL 2//",
3560              "-//IETF//DTD HTML STRICT LEVEL 3//",
3561              "-//IETF//DTD HTML STRICT//",
3562              "-//IETF//DTD HTML//",
3563              "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3564              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3565              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3566              "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3567              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3568              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3569              "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3570              "-//NETSCAPE COMM. CORP.//DTD HTML//",
3571              "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3572              "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3573              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3574              "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3575              "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3576              "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3577              "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3578              "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3579              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3580              "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3581              "-//W3C//DTD HTML 3 1995-03-24//",
3582              "-//W3C//DTD HTML 3.2 DRAFT//",
3583              "-//W3C//DTD HTML 3.2 FINAL//",
3584              "-//W3C//DTD HTML 3.2//",
3585              "-//W3C//DTD HTML 3.2S DRAFT//",
3586              "-//W3C//DTD HTML 4.0 FRAMESET//",
3587              "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3588              "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3589              "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3590              "-//W3C//DTD W3 HTML//",
3591              "-//W3O//DTD W3 HTML 3.0//",
3592              "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3593              "-//WEBTECHS//DTD MOZILLA HTML//",
3594            ]; # $prefix
3595            my $match;
3596            for (@$prefix) {
3597              if (substr ($prefix, 0, length $_) eq $_) {
3598                $match = 1;
3599                last;
3600              }
3601            }
3602            if ($match or
3603                $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3604                $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3605                $pubid eq "HTML") {
3606              !!!cp ('t5');
3607              $self->{document}->manakai_compat_mode ('quirks');
3608            } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3609                     $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3610              if (defined $token->{sysid}) {
3611                !!!cp ('t6');
3612                $self->{document}->manakai_compat_mode ('quirks');
3613              } else {
3614                !!!cp ('t7');
3615                $self->{document}->manakai_compat_mode ('limited quirks');
3616            }            }
3617            } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3618                     $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3619              !!!cp ('t8');
3620              $self->{document}->manakai_compat_mode ('limited quirks');
3621            } else {
3622              !!!cp ('t9');
3623            }
3624          } else {
3625            !!!cp ('t10');
3626          }
3627          if (defined $token->{sysid}) {
3628            my $sysid = $token->{sysid};
3629            $sysid =~ tr/A-Z/a-z/;
3630            if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3631              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3632              ## marked as quirks.
3633              $self->{document}->manakai_compat_mode ('quirks');
3634              !!!cp ('t11');
3635            } else {
3636              !!!cp ('t12');
3637            }
3638          } else {
3639            !!!cp ('t13');
3640          }
3641          
3642          ## Go to the "before html" insertion mode.
3643          !!!next-token;
3644          return;
3645        } elsif ({
3646                  START_TAG_TOKEN, 1,
3647                  END_TAG_TOKEN, 1,
3648                  END_OF_FILE_TOKEN, 1,
3649                 }->{$token->{type}}) {
3650          !!!cp ('t14');
3651          !!!parse-error (type => 'no DOCTYPE', token => $token);
3652          $self->{document}->manakai_compat_mode ('quirks');
3653          ## Go to the "before html" insertion mode.
3654          ## reprocess
3655          !!!ack-later;
3656          return;
3657        } elsif ($token->{type} == CHARACTER_TOKEN) {
3658          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3659            ## Ignore the token
3660    
3661            unless (length $token->{data}) {
3662              !!!cp ('t15');
3663              ## Stay in the insertion mode.
3664              !!!next-token;
3665              redo INITIAL;
3666            } else {
3667              !!!cp ('t16');
3668          }          }
         ## ISSUE: Spec currently left this case undefined.  
         !!!parse-error (type => 'missing DOCTYPE');  
         #$phase = 'root element';  
         ## reprocess  
         #redo B;  
         return;  
3669        } else {        } else {
3670          die "$0: $token->{type}: Unknown token";          !!!cp ('t17');
3671        }        }
3672      } # B  
3673          !!!parse-error (type => 'no DOCTYPE', token => $token);
3674          $self->{document}->manakai_compat_mode ('quirks');
3675          ## Go to the "before html" insertion mode.
3676          ## reprocess
3677          return;
3678        } elsif ($token->{type} == COMMENT_TOKEN) {
3679          !!!cp ('t18');
3680          my $comment = $self->{document}->create_comment ($token->{data});
3681          $self->{document}->append_child ($comment);
3682          
3683          ## Stay in the insertion mode.
3684          !!!next-token;
3685          redo INITIAL;
3686        } else {
3687          die "$0: $token->{type}: Unknown token type";
3688        }
3689      } # INITIAL
3690    
3691      die "$0: _tree_construction_initial: This should be never reached";
3692  } # _tree_construction_initial  } # _tree_construction_initial
3693    
3694  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3695    my $self = shift;    my $self = shift;
3696    
3697      ## NOTE: "before html" insertion mode.
3698        
3699    B: {    B: {
3700        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3701          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3702            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3703          ## Ignore the token          ## Ignore the token
3704          ## Stay in the phase          ## Stay in the insertion mode.
3705          !!!next-token;          !!!next-token;
3706          redo B;          redo B;
3707        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3708            !!!cp ('t20');
3709          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3710          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3711          ## Stay in the phase          ## Stay in the insertion mode.
3712          !!!next-token;          !!!next-token;
3713          redo B;          redo B;
3714        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3715          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3716            $self->{document}->manakai_append_text ($1);            ## Ignore the token.
3717            ## ISSUE: DOM3 Core does not allow Document > Text  
3718            unless (length $token->{data}) {            unless (length $token->{data}) {
3719              ## Stay in the phase              !!!cp ('t21');
3720                ## Stay in the insertion mode.
3721              !!!next-token;              !!!next-token;
3722              redo B;              redo B;
3723              } else {
3724                !!!cp ('t22');
3725            }            }
3726            } else {
3727              !!!cp ('t23');
3728          }          }
3729    
3730            $self->{application_cache_selection}->(undef);
3731    
3732          #          #
3733          } elsif ($token->{type} == START_TAG_TOKEN) {
3734            if ($token->{tag_name} eq 'html') {
3735              my $root_element;
3736              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3737              $self->{document}->append_child ($root_element);
3738              push @{$self->{open_elements}},
3739                  [$root_element, $el_category->{html}];
3740    
3741              if ($token->{attributes}->{manifest}) {
3742                !!!cp ('t24');
3743                $self->{application_cache_selection}
3744                    ->($token->{attributes}->{manifest}->{value});
3745                ## ISSUE: Spec is unclear on relative references.
3746                ## According to Hixie (#whatwg 2008-03-19), it should be
3747                ## resolved against the base URI of the document in HTML
3748                ## or xml:base of the element in XHTML.
3749              } else {
3750                !!!cp ('t25');
3751                $self->{application_cache_selection}->(undef);
3752              }
3753    
3754              !!!nack ('t25c');
3755    
3756              !!!next-token;
3757              return; ## Go to the "before head" insertion mode.
3758            } else {
3759              !!!cp ('t25.1');
3760              #
3761            }
3762        } elsif ({        } elsif ({
3763                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3764                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3765                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3766          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3767          #          #
3768        } else {        } else {
3769          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3770        }        }
3771        my $root_element; !!!create-element ($root_element, 'html');  
3772        $self->{document}->append_child ($root_element);      my $root_element;
3773        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3774        #$phase = 'main';      $self->{document}->append_child ($root_element);
3775        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3776        #redo B;  
3777        return;      $self->{application_cache_selection}->(undef);
3778    
3779        ## NOTE: Reprocess the token.
3780        !!!ack-later;
3781        return; ## Go to the "before head" insertion mode.
3782    } # B    } # B
3783    
3784      die "$0: _tree_construction_root_element: This should never be reached";
3785  } # _tree_construction_root_element  } # _tree_construction_root_element
3786    
3787  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 1813  sub _reset_insertion_mode ($) { Line 3796  sub _reset_insertion_mode ($) {
3796            
3797      ## Step 3      ## Step 3
3798      S3: {      S3: {
3799        $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3800        if (defined $self->{inner_html_node}) {          $last = 1;
3801          if ($self->{inner_html_node}->[1] eq 'td' or          if (defined $self->{inner_html_node}) {
3802              $self->{inner_html_node}->[1] eq 'th') {            !!!cp ('t28');
3803              $node = $self->{inner_html_node};
3804            } else {
3805              die "_reset_insertion_mode: t27";
3806            }
3807          }
3808          
3809          ## Step 4..14
3810          my $new_mode;
3811          if ($node->[1] & FOREIGN_EL) {
3812            !!!cp ('t28.1');
3813            ## NOTE: Strictly spaking, the line below only applies to MathML and
3814            ## SVG elements.  Currently the HTML syntax supports only MathML and
3815            ## SVG elements as foreigners.
3816            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3817          } elsif ($node->[1] & TABLE_CELL_EL) {
3818            if ($last) {
3819              !!!cp ('t28.2');
3820            #            #
3821          } else {          } else {
3822            $node = $self->{inner_html_node};            !!!cp ('t28.3');
3823              $new_mode = IN_CELL_IM;
3824          }          }
3825          } else {
3826            !!!cp ('t28.4');
3827            $new_mode = {
3828                          select => IN_SELECT_IM,
3829                          ## NOTE: |option| and |optgroup| do not set
3830                          ## insertion mode to "in select" by themselves.
3831                          tr => IN_ROW_IM,
3832                          tbody => IN_TABLE_BODY_IM,
3833                          thead => IN_TABLE_BODY_IM,
3834                          tfoot => IN_TABLE_BODY_IM,
3835                          caption => IN_CAPTION_IM,
3836                          colgroup => IN_COLUMN_GROUP_IM,
3837                          table => IN_TABLE_IM,
3838                          head => IN_BODY_IM, # not in head!
3839                          body => IN_BODY_IM,
3840                          frameset => IN_FRAMESET_IM,
3841                         }->{$node->[0]->manakai_local_name};
3842        }        }
       
       ## 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]};  
3843        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3844                
3845        ## Step 14        ## Step 15
3846        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3847          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3848            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3849              $self->{insertion_mode} = BEFORE_HEAD_IM;
3850          } else {          } else {
3851            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3852              !!!cp ('t30');
3853              $self->{insertion_mode} = AFTER_HEAD_IM;
3854          }          }
3855          return;          return;
3856          } else {
3857            !!!cp ('t31');
3858        }        }
3859                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3860        ## Step 16        ## Step 16
3861          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3862          
3863          ## Step 17
3864        $i--;        $i--;
3865        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3866                
3867        ## Step 17        ## Step 18
3868        redo S3;        redo S3;
3869      } # S3      } # S3
3870    
3871      die "$0: _reset_insertion_mode: This line should never be reached";
3872  } # _reset_insertion_mode  } # _reset_insertion_mode
3873    
3874  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3875    my $self = shift;    my $self = shift;
3876    
   my $phase = 'main';  
   
3877    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3878    
3879    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 1884  sub _tree_construction_main ($) { Line 3890  sub _tree_construction_main ($) {
3890      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3891      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3892        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3893            !!!cp ('t32');
3894          return;          return;
3895        }        }
3896      }      }
# Line 1898  sub _tree_construction_main ($) { Line 3905  sub _tree_construction_main ($) {
3905    
3906        ## Step 6        ## Step 6
3907        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3908            !!!cp ('t33_1');
3909          #          #
3910        } else {        } else {
3911          my $in_open_elements;          my $in_open_elements;
3912          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3913            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3914                !!!cp ('t33');
3915              $in_open_elements = 1;              $in_open_elements = 1;
3916              last OE;              last OE;
3917            }            }
3918          }          }
3919          if ($in_open_elements) {          if ($in_open_elements) {
3920              !!!cp ('t34');
3921            #            #
3922          } else {          } else {
3923              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3924              !!!cp ('t35');
3925            redo S4;            redo S4;
3926          }          }
3927        }        }
# Line 1932  sub _tree_construction_main ($) { Line 3944  sub _tree_construction_main ($) {
3944    
3945        ## Step 11        ## Step 11
3946        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3947            !!!cp ('t36');
3948          ## Step 7'          ## Step 7'
3949          $i++;          $i++;
3950          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3951                    
3952          redo S7;          redo S7;
3953        }        }
3954    
3955          !!!cp ('t37');
3956      } # S7      } # S7
3957    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3958    
3959    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3960      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3961        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3962            !!!cp ('t38');
3963          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3964          return;          return;
3965        }        }
3966      }      }
3967    
3968        !!!cp ('t39');
3969    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3970    
3971    my $style_start_tag = sub {    my $insert;
3972      my $style_el; !!!create-element ($style_el, 'style', $token->{attributes});  
3973      ## $self->{insertion_mode} eq 'in head' and ... (always true)    my $parse_rcdata = sub ($) {
3974      (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})      my ($content_model_flag) = @_;
3975       ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
3976        ->append_child ($style_el);      ## Step 1
3977      $self->{content_model_flag} = 'CDATA';      my $start_tag_name = $token->{tag_name};
3978        my $el;
3979        !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3980    
3981        ## Step 2
3982        $insert->($el);
3983    
3984        ## Step 3
3985        $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3986      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3987                  
3988        ## Step 4
3989      my $text = '';      my $text = '';
3990        !!!nack ('t40.1');
3991      !!!next-token;      !!!next-token;
3992      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3993          !!!cp ('t40');
3994        $text .= $token->{data};        $text .= $token->{data};
3995        !!!next-token;        !!!next-token;
3996      } # stop if non-character token or tokenizer stops tokenising      }
3997    
3998        ## Step 5
3999      if (length $text) {      if (length $text) {
4000        $style_el->manakai_append_text ($text);        !!!cp ('t41');
4001          my $text = $self->{document}->create_text_node ($text);
4002          $el->append_child ($text);
4003      }      }
4004        
4005      $self->{content_model_flag} = 'PCDATA';      ## Step 6
4006                      $self->{content_model} = PCDATA_CONTENT_MODEL;
4007      if ($token->{type} eq 'end tag' and $token->{tag_name} eq 'style') {  
4008        ## Step 7
4009        if ($token->{type} == END_TAG_TOKEN and
4010            $token->{tag_name} eq $start_tag_name) {
4011          !!!cp ('t42');
4012        ## Ignore the token        ## Ignore the token
4013      } else {      } else {
4014        !!!parse-error (type => 'in CDATA:#'.$token->{type});        ## NOTE: An end-of-file token.
4015        ## ISSUE: And ignore?        if ($content_model_flag == CDATA_CONTENT_MODEL) {
4016            !!!cp ('t43');
4017            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4018          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4019            !!!cp ('t44');
4020            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4021          } else {
4022            die "$0: $content_model_flag in parse_rcdata";
4023          }
4024      }      }
4025      !!!next-token;      !!!next-token;
4026    }; # $style_start_tag    }; # $parse_rcdata
4027    
4028    my $script_start_tag = sub {    my $script_start_tag = sub () {
4029      my $script_el;      my $script_el;
4030      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4031      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4032    
4033      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
4034      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4035            
4036      my $text = '';      my $text = '';
4037        !!!nack ('t45.1');
4038      !!!next-token;      !!!next-token;
4039      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
4040          !!!cp ('t45');
4041        $text .= $token->{data};        $text .= $token->{data};
4042        !!!next-token;        !!!next-token;
4043      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4044      if (length $text) {      if (length $text) {
4045          !!!cp ('t46');
4046        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4047      }      }
4048                                
4049      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
4050    
4051      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
4052          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4053          !!!cp ('t47');
4054        ## Ignore the token        ## Ignore the token
4055      } else {      } else {
4056        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4057          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4058        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4059        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4060      }      }
4061            
4062      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4063          !!!cp ('t49');
4064        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4065      } else {      } else {
4066          !!!cp ('t50');
4067        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4068        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4069          
4070        (($self->{insertion_mode} eq 'in head' and defined $self->{head_element})        $insert->($script_el);
        ? $self->{head_element} : $self->{open_elements}->[-1]->[0])->append_child ($script_el);  
4071                
4072        ## TODO: insertion point = $old_insertion_point (might be "undefined")        ## TODO: insertion point = $old_insertion_point (might be "undefined")
4073                
# Line 2026  sub _tree_construction_main ($) { Line 4077  sub _tree_construction_main ($) {
4077      !!!next-token;      !!!next-token;
4078    }; # $script_start_tag    }; # $script_start_tag
4079    
4080      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4081      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4082      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4083      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4084    
4085    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4086      my $tag_name = shift;      my $end_tag_token = shift;
4087        my $tag_name = $end_tag_token->{tag_name};
4088    
4089        ## NOTE: The adoption agency algorithm (AAA).
4090    
4091      FET: {      FET: {
4092        ## Step 1        ## Step 1
4093        my $formatting_element;        my $formatting_element;
4094        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4095        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4096          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4097              !!!cp ('t52');
4098              last AFE;
4099            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4100                         eq $tag_name) {
4101              !!!cp ('t51');
4102            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4103            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4104            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4105          }          }
4106        } # AFE        } # AFE
4107        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4108          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4109            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4110          ## Ignore the token          ## Ignore the token
4111          !!!next-token;          !!!next-token;
4112          return;          return;
# Line 2055  sub _tree_construction_main ($) { Line 4118  sub _tree_construction_main ($) {
4118          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4119          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4120            if ($in_scope) {            if ($in_scope) {
4121                !!!cp ('t54');
4122              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4123              last INSCOPE;              last INSCOPE;
4124            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4125              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4126                !!!parse-error (type => 'unmatched end tag',
4127                                text => $token->{tag_name},
4128                                token => $end_tag_token);
4129              ## Ignore the token              ## Ignore the token
4130              !!!next-token;              !!!next-token;
4131              return;              return;
4132            }            }
4133          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4134                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4135            $in_scope = 0;            $in_scope = 0;
4136          }          }
4137        } # INSCOPE        } # INSCOPE
4138        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4139          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4140            !!!parse-error (type => 'unmatched end tag',
4141                            text => $token->{tag_name},
4142                            token => $end_tag_token);
4143          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4144          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4145          return;          return;
4146        }        }
4147        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4148          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4149            !!!parse-error (type => 'not closed',
4150                            text => $self->{open_elements}->[-1]->[0]
4151                                ->manakai_local_name,
4152                            token => $end_tag_token);
4153        }        }
4154                
4155        ## Step 2        ## Step 2
# Line 2085  sub _tree_construction_main ($) { Line 4157  sub _tree_construction_main ($) {
4157        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4158        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4159          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4160          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4161              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4162              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4163               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4164              !!!cp ('t59');
4165            $furthest_block = $node;            $furthest_block = $node;
4166            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4167              ## NOTE: The topmost (eldest) node.
4168          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4169              !!!cp ('t60');
4170            last OE;            last OE;
4171          }          }
4172        } # OE        } # OE
4173                
4174        ## Step 3        ## Step 3
4175        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4176            !!!cp ('t61');
4177          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4178          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4179          !!!next-token;          !!!next-token;
# Line 2110  sub _tree_construction_main ($) { Line 4186  sub _tree_construction_main ($) {
4186        ## Step 5        ## Step 5
4187        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4188        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4189            !!!cp ('t62');
4190          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4191        }        }
4192                
# Line 2132  sub _tree_construction_main ($) { Line 4209  sub _tree_construction_main ($) {
4209          S7S2: {          S7S2: {
4210            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4211              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4212                  !!!cp ('t63');
4213                $node_i_in_active = $_;                $node_i_in_active = $_;
4214                last S7S2;                last S7S2;
4215              }              }
# Line 2145  sub _tree_construction_main ($) { Line 4223  sub _tree_construction_main ($) {
4223                    
4224          ## Step 4          ## Step 4
4225          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4226              !!!cp ('t64');
4227            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4228          }          }
4229                    
4230          ## Step 5          ## Step 5
4231          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4232              !!!cp ('t65');
4233            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4234            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4235            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2167  sub _tree_construction_main ($) { Line 4247  sub _tree_construction_main ($) {
4247        } # S7          } # S7  
4248                
4249        ## Step 8        ## Step 8
4250        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4251            my $foster_parent_element;
4252            my $next_sibling;
4253            OE: for (reverse 0..$#{$self->{open_elements}}) {
4254              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4255                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4256                                 if (defined $parent and $parent->node_type == 1) {
4257                                   !!!cp ('t65.1');
4258                                   $foster_parent_element = $parent;
4259                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4260                                 } else {
4261                                   !!!cp ('t65.2');
4262                                   $foster_parent_element
4263                                     = $self->{open_elements}->[$_ - 1]->[0];
4264                                 }
4265                                 last OE;
4266                               }
4267                             } # OE
4268                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4269                               unless defined $foster_parent_element;
4270            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4271            $open_tables->[-1]->[1] = 1; # tainted
4272          } else {
4273            !!!cp ('t65.3');
4274            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4275          }
4276                
4277        ## Step 9        ## Step 9
4278        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2184  sub _tree_construction_main ($) { Line 4289  sub _tree_construction_main ($) {
4289        my $i;        my $i;
4290        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4291          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4292              !!!cp ('t66');
4293            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4294            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4295          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4296              !!!cp ('t67');
4297            $i = $_;            $i = $_;
4298          }          }
4299        } # AFE        } # AFE
# Line 2196  sub _tree_construction_main ($) { Line 4303  sub _tree_construction_main ($) {
4303        undef $i;        undef $i;
4304        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4305          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4306              !!!cp ('t68');
4307            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4308            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4309          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4310              !!!cp ('t69');
4311            $i = $_;            $i = $_;
4312          }          }
4313        } # OE        } # OE
4314        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4315                
4316        ## Step 14        ## Step 14
4317        redo FET;        redo FET;
4318      } # FET      } # FET
4319    }; # $formatting_end_tag    }; # $formatting_end_tag
4320    
4321    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4322      $self->{open_elements}->[-1]->[0]->append_child (shift);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4323    }; # $insert_to_current    }; # $insert_to_current
4324    
4325    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4326                         my $child = shift;      my $child = shift;
4327                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4328                              table => 1, tbody => 1, tfoot => 1,        # MUST
4329                              thead => 1, tr => 1,        my $foster_parent_element;
4330                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4331                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4332                           my $foster_parent_element;          if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
                          my $next_sibling;  
                          OE: for (reverse 0..$#{$self->{open_elements}}) {  
                            if ($self->{open_elements}->[$_]->[1] eq 'table') {  
4333                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4334                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4335                                   !!!cp ('t70');
4336                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4337                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4338                               } else {                               } else {
4339                                   !!!cp ('t71');
4340                                 $foster_parent_element                                 $foster_parent_element
4341                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4342                               }                               }
# Line 2239  sub _tree_construction_main ($) { Line 4347  sub _tree_construction_main ($) {
4347                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4348                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4349                             ($child, $next_sibling);                             ($child, $next_sibling);
4350                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4351                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4352                         }        !!!cp ('t72');
4353          $self->{open_elements}->[-1]->[0]->append_child ($child);
4354        }
4355    }; # $insert_to_foster    }; # $insert_to_foster
4356    
4357    my $in_body = sub {    ## NOTE: Insert a character (MUST): When a character is inserted, if
4358      my $insert = shift;    ## the last node that was inserted by the parser is a Text node and
4359      if ($token->{type} eq 'start tag') {    ## the character has to be inserted after that node, then the
4360        if ($token->{tag_name} eq 'script') {    ## character is appended to the Text node.  However, if any other
4361          $script_start_tag->();    ## node is inserted by the parser, then a new Text node is created
4362          return;    ## and the character is appended as that Text node.  If I'm not
4363        } elsif ($token->{tag_name} eq 'style') {    ## wrong, for a parser with scripting disabled, there are only two
4364          $style_start_tag->();    ## cases where this occurs.  One is the case where an element node
4365          return;    ## is inserted to the |head| element.  This is covered by using the
4366        } elsif ({    ## |$self->{head_element_inserted}| flag.  Another is the case where
4367                  base => 1, link => 1, meta => 1,    ## an element or comment is inserted into the |table| subtree while
4368                 }->{$token->{tag_name}}) {    ## foster parenting happens.  This is covered by using the [2] flag
4369          !!!parse-error (type => 'in body:'.$token->{tag_name});    ## of the |$open_tables| structure.  All other cases are handled
4370          ## NOTE: This is an "as if in head" code clone    ## simply by calling |manakai_append_text| method.
4371          my $el;  
4372          !!!create-element ($el, $token->{tag_name}, $token->{attributes});    ## TODO: |<body><script>document.write("a<br>");
4373          if (defined $self->{head_element}) {    ## document.body.removeChild (document.body.lastChild);
4374            $self->{head_element}->append_child ($el);    ## document.write ("b")</script>|
4375          } else {  
4376            $insert->($el);    B: while (1) {
4377          }      if ($token->{type} == DOCTYPE_TOKEN) {
4378                  !!!cp ('t73');
4379          !!!next-token;        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4380          return;        ## Ignore the token
4381        } elsif ($token->{tag_name} eq 'title') {        ## Stay in the phase
4382          !!!parse-error (type => 'in body:title');        !!!next-token;
4383          ## NOTE: There is an "as if in head" code clone        next B;
4384          my $title_el;      } elsif ($token->{type} == START_TAG_TOKEN and
4385          !!!create-element ($title_el, 'title', $token->{attributes});               $token->{tag_name} eq 'html') {
4386          (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4387            ->append_child ($title_el);          !!!cp ('t79');
4388          $self->{content_model_flag} = 'RCDATA';          !!!parse-error (type => 'after html', text => 'html', token => $token);
4389          delete $self->{escape}; # MUST          $self->{insertion_mode} = AFTER_BODY_IM;
4390                  } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4391          my $text = '';          !!!cp ('t80');
4392          !!!next-token;          !!!parse-error (type => 'after html', text => 'html', token => $token);
4393          while ($token->{type} eq 'character') {          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4394            $text .= $token->{data};        } else {
4395            !!!next-token;          !!!cp ('t81');
4396          }        }
4397          if (length $text) {  
4398            $title_el->manakai_append_text ($text);        !!!cp ('t82');
4399          }        !!!parse-error (type => 'not first start tag', token => $token);
4400                  my $top_el = $self->{open_elements}->[0]->[0];
4401          $self->{content_model_flag} = 'PCDATA';        for my $attr_name (keys %{$token->{attributes}}) {
4402                    unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4403          if ($token->{type} eq 'end tag' and            !!!cp ('t84');
4404              $token->{tag_name} eq 'title') {            $top_el->set_attribute_ns
4405            ## Ignore the token              (undef, [undef, $attr_name],
4406          } else {               $token->{attributes}->{$attr_name}->{value});
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
           ## ISSUE: And ignore?  
         }  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error (type => 'in body:body');  
                 
         if (@{$self->{open_elements}} == 1 or  
             $self->{open_elements}->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $self->{open_elements}->[1]->[0];  
           for my $attr_name (keys %{$token->{attributes}}) {  
             unless ($body_el->has_attribute_ns (undef, $attr_name)) {  
               $body_el->set_attribute_ns  
                 (undef, [undef, $attr_name],  
                  $token->{attributes}->{$attr_name}->{value});  
             }  
           }  
4407          }          }
4408          }
4409          !!!nack ('t84.1');
4410          !!!next-token;
4411          next B;
4412        } elsif ($token->{type} == COMMENT_TOKEN) {
4413          my $comment = $self->{document}->create_comment ($token->{data});
4414          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4415            !!!cp ('t85');
4416            $self->{document}->append_child ($comment);
4417          } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4418            !!!cp ('t86');
4419            $self->{open_elements}->[0]->[0]->append_child ($comment);
4420          } else {
4421            !!!cp ('t87');
4422            $self->{open_elements}->[-1]->[0]->append_child ($comment);
4423            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4424          }
4425          !!!next-token;
4426          next B;
4427        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4428          if ($token->{type} == CHARACTER_TOKEN) {
4429            !!!cp ('t87.1');
4430            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4431          !!!next-token;          !!!next-token;
4432          return;          next B;
4433        } elsif ({        } elsif ($token->{type} == START_TAG_TOKEN) {
4434                  address => 1, blockquote => 1, center => 1, dir => 1,          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4435                  div => 1, dl => 1, fieldset => 1, listing => 1,               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4436                  menu => 1, ol => 1, p => 1, ul => 1,              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4437                  pre => 1,              ($token->{tag_name} eq 'svg' and
4438                 }->{$token->{tag_name}}) {               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4439          ## has a p element in scope            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4440          INSCOPE: for (reverse @{$self->{open_elements}}) {            !!!cp ('t87.2');
4441            if ($_->[1] eq 'p') {            #
4442              !!!back-token;          } elsif ({
4443              $token = {type => 'end tag', tag_name => 'p'};                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4444              return;                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4445            } elsif ({                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4446                      table => 1, caption => 1, td => 1, th => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4447                      button => 1, marquee => 1, object => 1, html => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4448                     }->{$_->[1]}) {                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4449              last INSCOPE;                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4450                      sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4451                     }->{$token->{tag_name}}) {
4452              !!!cp ('t87.2');
4453              !!!parse-error (type => 'not closed',
4454                              text => $self->{open_elements}->[-1]->[0]
4455                                  ->manakai_local_name,
4456                              token => $token);
4457    
4458              pop @{$self->{open_elements}}
4459                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4460    
4461              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4462              ## Reprocess.
4463              next B;
4464            } else {
4465              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4466              my $tag_name = $token->{tag_name};
4467              if ($nsuri eq $SVG_NS) {
4468                $tag_name = {
4469                   altglyph => 'altGlyph',
4470                   altglyphdef => 'altGlyphDef',
4471                   altglyphitem => 'altGlyphItem',
4472                   animatecolor => 'animateColor',
4473                   animatemotion => 'animateMotion',
4474                   animatetransform => 'animateTransform',
4475                   clippath => 'clipPath',
4476                   feblend => 'feBlend',
4477                   fecolormatrix => 'feColorMatrix',
4478                   fecomponenttransfer => 'feComponentTransfer',
4479                   fecomposite => 'feComposite',
4480                   feconvolvematrix => 'feConvolveMatrix',
4481                   fediffuselighting => 'feDiffuseLighting',
4482                   fedisplacementmap => 'feDisplacementMap',
4483                   fedistantlight => 'feDistantLight',
4484                   feflood => 'feFlood',
4485                   fefunca => 'feFuncA',
4486                   fefuncb => 'feFuncB',
4487                   fefuncg => 'feFuncG',
4488                   fefuncr => 'feFuncR',
4489                   fegaussianblur => 'feGaussianBlur',
4490                   feimage => 'feImage',
4491                   femerge => 'feMerge',
4492                   femergenode => 'feMergeNode',
4493                   femorphology => 'feMorphology',
4494                   feoffset => 'feOffset',
4495                   fepointlight => 'fePointLight',
4496                   fespecularlighting => 'feSpecularLighting',
4497                   fespotlight => 'feSpotLight',
4498                   fetile => 'feTile',
4499                   feturbulence => 'feTurbulence',
4500                   foreignobject => 'foreignObject',
4501                   glyphref => 'glyphRef',
4502                   lineargradient => 'linearGradient',
4503                   radialgradient => 'radialGradient',
4504                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4505                   textpath => 'textPath',  
4506                }->{$tag_name} || $tag_name;
4507            }            }
4508          } # INSCOPE  
4509                        ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4510          !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
4511          if ($token->{tag_name} eq 'pre') {            ## "adjust foreign attributes" - done in insert-element-f
4512            !!!next-token;  
4513            if ($token->{type} eq 'character') {            !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4514              $token->{data} =~ s/^\x0A//;  
4515              unless (length $token->{data}) {            if ($self->{self_closing}) {
4516                !!!next-token;              pop @{$self->{open_elements}};
4517              }              !!!ack ('t87.3');
4518              } else {
4519                !!!cp ('t87.4');
4520            }            }
4521          } else {  
           !!!next-token;  
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => 'end tag', tag_name => 'p'};  
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
4522            !!!next-token;            !!!next-token;
4523            return;            next B;
4524          }          }
4525        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{type} == END_TAG_TOKEN) {
4526          ## has a p element in scope          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4527          INSCOPE: for (reverse @{$self->{open_elements}}) {          !!!cp ('t87.5');
4528            if ($_->[1] eq 'p') {          #
4529              !!!back-token;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4530              $token = {type => 'end tag', tag_name => 'p'};          !!!cp ('t87.6');
4531              return;          !!!parse-error (type => 'not closed',
4532            } elsif ({                          text => $self->{open_elements}->[-1]->[0]
4533                      table => 1, caption => 1, td => 1, th => 1,                              ->manakai_local_name,
4534                      button => 1, marquee => 1, object => 1, html => 1,                          token => $token);
4535                     }->{$_->[1]}) {  
4536              last INSCOPE;          pop @{$self->{open_elements}}
4537            }              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4538          } # INSCOPE  
4539                      ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4540          ## Step 1  
4541          my $i = -1;          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4542          my $node = $self->{open_elements}->[$i];          ## Reprocess.
4543          LI: {          next B;
4544            ## Step 2        } else {
4545            if ($node->[1] eq 'li') {          die "$0: $token->{type}: Unknown token type";        
4546              if ($i != -1) {        }
4547                !!!parse-error (type => 'end tag missing:'.      }
4548                                $self->{open_elements}->[-1]->[1]);  
4549                ## TODO: test      if ($self->{insertion_mode} & HEAD_IMS) {
4550              }        if ($token->{type} == CHARACTER_TOKEN) {
4551              splice @{$self->{open_elements}}, $i;          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4552              last LI;            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4553            }              if ($self->{head_element_inserted}) {
4554                            !!!cp ('t88.3');
4555            ## Step 3                $self->{open_elements}->[-1]->[0]->append_child
4556            if (not $formatting_category->{$node->[1]} and                  ($self->{document}->create_text_node ($1));
4557                #not $phrasing_category->{$node->[1]} and                delete $self->{head_element_inserted};
4558                ($special_category->{$node->[1]} or                ## NOTE: |</head> <link> |
4559                 $scoping_category->{$node->[1]}) and                #
4560                $node->[1] ne 'address' and $node->[1] ne 'div') {              } else {
4561              last LI;                !!!cp ('t88.2');
4562            }                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4563                            ## NOTE: |</head> &#x20;|
4564            ## Step 4                #
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
               ## TODO: test  
4565              }              }
4566              splice @{$self->{open_elements}}, $i;            } else {
4567              last LI;              !!!cp ('t88.1');
4568            }              ## Ignore the token.
4569                          #
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model_flag} = 'PLAINTEXT';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4570            }            }
4571          } # INSCOPE            unless (length $token->{data}) {
4572                          !!!cp ('t88');
4573          ## has an element in scope              !!!next-token;
4574          my $i;              next B;
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4575            }            }
4576          } # INSCOPE  ## TODO: set $token->{column} appropriately
             
         if (defined $i) {  
           !!!parse-error (type => 'in hn:hn');  
           splice @{$self->{open_elements}}, $i;  
4577          }          }
             
         !!!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 (type => 'in a: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..$#{$self->{open_elements}}) {  
               if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {  
                 splice @{$self->{open_elements}}, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4578    
4579          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4580          push @$active_formatting_elements, $self->{open_elements}->[-1];            !!!cp ('t89');
4581              ## As if <head>
4582              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4583              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4584              push @{$self->{open_elements}},
4585                  [$self->{head_element}, $el_category->{head}];
4586    
4587          !!!next-token;            ## Reprocess in the "in head" insertion mode...
4588          return;            pop @{$self->{open_elements}};
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
4589    
4590          !!!next-token;            ## Reprocess in the "after head" insertion mode...
4591          return;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4592        } elsif ($token->{tag_name} eq 'marquee' or            !!!cp ('t90');
4593                 $token->{tag_name} eq 'object') {            ## As if </noscript>
4594          $reconstruct_active_formatting_elements->($insert_to_current);            pop @{$self->{open_elements}};
4595                      !!!parse-error (type => 'in noscript:#text', token => $token);
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{content_model_flag} = 'CDATA';  
         delete $self->{escape}; # MUST  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = 'in table';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,  
                 image => 1,  
                }->{$token->{tag_name}}) {  
         if ($token->{tag_name} eq 'image') {  
           !!!parse-error (type => 'image');  
           $token->{tag_name} = 'img';  
         }  
           
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
4596                        
4597          !!!next-token;            ## Reprocess in the "in head" insertion mode...
4598          return;            ## As if </head>
4599        } elsif ($token->{tag_name} eq 'input') {            pop @{$self->{open_elements}};
4600          $reconstruct_active_formatting_elements->($insert_to_current);  
4601                      ## Reprocess in the "after head" insertion mode...
4602          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4603          ## TODO: associate with $self->{form_element} if defined            !!!cp ('t91');
4604          pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4605            
4606          !!!next-token;            ## Reprocess in the "after head" insertion mode...
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                         {type => 'character',  
                          data => 'This is a searchable index. Insert your search keywords here: '}, # SHOULD  
                         ## TODO: make this configurable  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'},  
                        );  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ({  
                 textarea => 1,  
                 iframe => 1,  
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         if ($token->{tag_name} eq 'textarea') {  
           ## TODO: $self->{form_element} if defined  
           $self->{content_model_flag} = 'RCDATA';  
         } else {  
           $self->{content_model_flag} = 'CDATA';  
         }  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         if ($token->{tag_name} eq 'textarea') {  
           !!!next-token;  
           if ($token->{type} eq 'character') {  
             $token->{data} =~ s/^\x0A//;  
             unless (length $token->{data}) {  
               !!!next-token;  
             }  
           }  
4607          } else {          } else {
4608            !!!next-token;            !!!cp ('t92');
4609          }          }
4610          while ($token->{type} eq 'character') {  
4611            $text .= $token->{data};          ## "after head" insertion mode
4612            !!!next-token;          ## As if <body>
4613          }          !!!insert-element ('body',, $token);
4614          if (length $text) {          $self->{insertion_mode} = IN_BODY_IM;
4615            $el->manakai_append_text ($text);          ## reprocess
4616          }          next B;
4617                  } elsif ($token->{type} == START_TAG_TOKEN) {
4618          $self->{content_model_flag} = 'PCDATA';          if ($token->{tag_name} eq 'head') {
4619                      if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620          if ($token->{type} eq 'end tag' and              !!!cp ('t93');
4621              $token->{tag_name} eq $tag_name) {              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4622            ## Ignore the token              $self->{open_elements}->[-1]->[0]->append_child
4623          } else {                  ($self->{head_element});
4624            if ($token->{tag_name} eq 'textarea') {              push @{$self->{open_elements}},
4625              !!!parse-error (type => 'in RCDATA:#'.$token->{type});                  [$self->{head_element}, $el_category->{head}];
4626                $self->{insertion_mode} = IN_HEAD_IM;
4627                !!!nack ('t93.1');
4628                !!!next-token;
4629                next B;
4630              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4631                !!!cp ('t93.2');
4632                !!!parse-error (type => 'after head', text => 'head',
4633                                token => $token);
4634                ## Ignore the token
4635                !!!nack ('t93.3');
4636                !!!next-token;
4637                next B;
4638            } else {            } else {
4639              !!!parse-error (type => 'in CDATA:#'.$token->{type});              !!!cp ('t95');
4640                !!!parse-error (type => 'in head:head',
4641                                token => $token); # or in head noscript
4642                ## Ignore the token
4643                !!!nack ('t95.1');
4644                !!!next-token;
4645                next B;
4646            }            }
4647            ## ISSUE: And ignore?          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4648          }            !!!cp ('t96');
4649          !!!next-token;            ## As if <head>
4650          return;            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4651        } elsif ($token->{tag_name} eq 'select') {            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4652          $reconstruct_active_formatting_elements->($insert_to_current);            push @{$self->{open_elements}},
4653                          [$self->{head_element}, $el_category->{head}];
4654          !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
4655                      $self->{insertion_mode} = IN_HEAD_IM;
4656          $self->{insertion_mode} = 'in select';            ## Reprocess in the "in head" insertion mode...
4657          !!!next-token;          } else {
4658          return;            !!!cp ('t97');
4659        } elsif ({          }
4660                  caption => 1, col => 1, colgroup => 1, frame => 1,  
4661                  frameset => 1, head => 1, option => 1, optgroup => 1,          if ($token->{tag_name} eq 'base') {
4662                  tbody => 1, td => 1, tfoot => 1, th => 1,            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4663                  thead => 1, tr => 1,              !!!cp ('t98');
4664                 }->{$token->{tag_name}}) {              ## As if </noscript>
4665          !!!parse-error (type => 'in body:'.$token->{tag_name});              pop @{$self->{open_elements}};
4666          ## Ignore the token              !!!parse-error (type => 'in noscript', text => 'base',
4667          !!!next-token;                              token => $token);
4668          return;            
4669                        $self->{insertion_mode} = IN_HEAD_IM;
4670          ## ISSUE: An issue on HTML5 new elements in the spec.              ## Reprocess in the "in head" insertion mode...
4671        } else {            } else {
4672          $reconstruct_active_formatting_elements->($insert_to_current);              !!!cp ('t99');
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
       }  
     } elsif ($token->{type} eq 'end tag') {  
       if ($token->{tag_name} eq 'body') {  
         if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4673            }            }
4674            $self->{insertion_mode} = 'after body';  
4675            !!!next-token;            ## NOTE: There is a "as if in head" code clone.
4676            return;            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4677          } else {              !!!cp ('t100');
4678            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!parse-error (type => 'after head',
4679            ## Ignore the token                              text => $token->{tag_name}, token => $token);
4680            !!!next-token;              push @{$self->{open_elements}},
4681            return;                  [$self->{head_element}, $el_category->{head}];
4682          }              $self->{head_element_inserted} = 1;
4683        } elsif ($token->{tag_name} eq 'html') {            } else {
4684          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {              !!!cp ('t101');
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
4685            }            }
4686            $self->{insertion_mode} = 'after body';            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4687            ## reprocess            pop @{$self->{open_elements}};
4688            return;            pop @{$self->{open_elements}} # <head>
4689          } else {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4690            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!nack ('t101.1');
           ## Ignore the token  
4691            !!!next-token;            !!!next-token;
4692            return;            next B;
4693          }          } elsif ($token->{tag_name} eq 'link') {
4694        } elsif ({            ## NOTE: There is a "as if in head" code clone.
4695                  address => 1, blockquote => 1, center => 1, dir => 1,            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4696                  div => 1, dl => 1, fieldset => 1, listing => 1,              !!!cp ('t102');
4697                  menu => 1, ol => 1, pre => 1, ul => 1,              !!!parse-error (type => 'after head',
4698                  p => 1,                              text => $token->{tag_name}, token => $token);
4699                  dd => 1, dt => 1, li => 1,              push @{$self->{open_elements}},
4700                  button => 1, marquee => 1, object => 1,                  [$self->{head_element}, $el_category->{head}];
4701                 }->{$token->{tag_name}}) {              $self->{head_element_inserted} = 1;
4702          ## has an element in scope            } else {
4703          my $i;              !!!cp ('t103');
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         ## has an element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4704            }            }
4705          } # INSCOPE            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
4706            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4707          } else {            pop @{$self->{open_elements}} # <head>
4708            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                if $self->{insertion_mode} == AFTER_HEAD_IM;
4709          }            !!!ack ('t103.1');
4710              !!!next-token;
4711          undef $self->{form_element};            next B;
4712          !!!next-token;          } elsif ($token->{tag_name} eq 'command' or
4713          return;                   $token->{tag_name} eq 'eventsource') {
4714        } elsif ({            if ($self->{insertion_mode} == IN_HEAD_IM) {
4715                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,              ## NOTE: If the insertion mode at the time of the emission
4716                 }->{$token->{tag_name}}) {              ## of the token was "before head", $self->{insertion_mode}
4717          ## has an element in scope              ## is already changed to |IN_HEAD_IM|.
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
 ## TODO: <http://html5.org/tools/web-apps-tracker?from=883&to=884>  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
   
         ## Step 2  
         S2: {  
           if ($node->[1] eq $token->{tag_name}) {  
             ## Step 1  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
           
             ## Step 2  
             if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
             }  
               
             ## Step 3  
             splice @{$self->{open_elements}}, $node_i;  
4718    
4719                ## NOTE: There is a "as if in head" code clone.
4720                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4721                pop @{$self->{open_elements}};
4722                pop @{$self->{open_elements}} # <head>
4723                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4724                !!!ack ('t103.2');
4725              !!!next-token;              !!!next-token;
4726              last S2;              next B;
4727            } else {            } else {
4728              ## Step 3              ## NOTE: "in head noscript" or "after head" insertion mode
4729              if (not $formatting_category->{$node->[1]} and              ## - in these cases, these tags are treated as same as
4730                  #not $phrasing_category->{$node->[1]} and              ## normal in-body tags.
4731                  ($special_category->{$node->[1]} or              !!!cp ('t103.3');
4732                   $scoping_category->{$node->[1]})) {              #
               !!!parse-error (type => 'not closed:'.$node->[1]);  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
4733            }            }
4734                      } elsif ($token->{tag_name} eq 'meta') {
4735            ## Step 4            ## NOTE: There is a "as if in head" code clone.
4736            $node_i--;            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4737            $node = $self->{open_elements}->[$node_i];              !!!cp ('t104');
4738                          !!!parse-error (type => 'after head',
4739            ## Step 5;                              text => $token->{tag_name}, token => $token);
4740            redo S2;              push @{$self->{open_elements}},
4741          } # S2                  [$self->{head_element}, $el_category->{head}];
4742          return;              $self->{head_element_inserted} = 1;
4743        }            } else {
4744      }              !!!cp ('t105');
   }; # $in_body  
   
   B: {  
     if ($phase eq 'main') {  
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'in html:#DOCTYPE');  
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
         ## TODO: unless it is the first start tag token, parse-error  
         my $top_el = $self->{open_elements}->[0]->[0];  
         for my $attr_name (keys %{$token->{attributes}}) {  
           unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
             $top_el->set_attribute_ns  
               (undef, [undef, $attr_name],  
                $token->{attributes}->{$attr_name}->{value});  
4745            }            }
4746          }            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4747          !!!next-token;            my $meta_el = pop @{$self->{open_elements}};
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         ## Stop parsing  
         last B;  
4748    
4749          ## ISSUE: There is an issue in the spec.                unless ($self->{confident}) {
4750        } else {                  if ($token->{attributes}->{charset}) {
4751          if ($self->{insertion_mode} eq 'before head') {                    !!!cp ('t106');
4752            if ($token->{type} eq 'character') {                    ## NOTE: Whether the encoding is supported or not is handled
4753              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                    ## in the {change_encoding} callback.
4754                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                    $self->{change_encoding}
4755                unless (length $token->{data}) {                        ->($self, $token->{attributes}->{charset}->{value},
4756                  !!!next-token;                           $token);
4757                  redo B;                    
4758                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4759                          ->set_user_data (manakai_has_reference =>
4760                                               $token->{attributes}->{charset}
4761                                                   ->{has_reference});
4762                    } elsif ($token->{attributes}->{content}) {
4763                      if ($token->{attributes}->{content}->{value}
4764                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4765                              [\x09\x0A\x0C\x0D\x20]*=
4766                              [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4767                              ([^"'\x09\x0A\x0C\x0D\x20]
4768                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4769                        !!!cp ('t107');
4770                        ## NOTE: Whether the encoding is supported or not is handled
4771                        ## in the {change_encoding} callback.
4772                        $self->{change_encoding}
4773                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4774                               $token);
4775                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4776                            ->set_user_data (manakai_has_reference =>
4777                                                 $token->{attributes}->{content}
4778                                                       ->{has_reference});
4779                      } else {
4780                        !!!cp ('t108');
4781                      }
4782                    }
4783                  } else {
4784                    if ($token->{attributes}->{charset}) {
4785                      !!!cp ('t109');
4786                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4787                          ->set_user_data (manakai_has_reference =>
4788                                               $token->{attributes}->{charset}
4789                                                   ->{has_reference});
4790                    }
4791                    if ($token->{attributes}->{content}) {
4792                      !!!cp ('t110');
4793                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4794                          ->set_user_data (manakai_has_reference =>
4795                                               $token->{attributes}->{content}
4796                                                   ->{has_reference});
4797                    }
4798                }                }
4799              }  
4800              ## As if <head>                pop @{$self->{open_elements}} # <head>
4801              !!!create-element ($self->{head_element}, 'head');                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4802              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                !!!ack ('t110.1');
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             ## reprocess  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};  
             !!!create-element ($self->{head_element}, 'head', $attr);  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             if ($token->{tag_name} eq 'head') {  
               !!!next-token;  
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               ## As if <head>  
               !!!create-element ($self->{head_element}, 'head');  
               $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
               push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
4803                !!!next-token;                !!!next-token;
4804                redo B;                next B;
4805              }          } elsif ($token->{tag_name} eq 'title') {
4806              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4807                !!!cp ('t111');
4808                ## As if </noscript>
4809                pop @{$self->{open_elements}};
4810                !!!parse-error (type => 'in noscript', text => 'title',
4811                                token => $token);
4812              
4813                $self->{insertion_mode} = IN_HEAD_IM;
4814                ## Reprocess in the "in head" insertion mode...
4815              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4816                !!!cp ('t112');
4817                !!!parse-error (type => 'after head',
4818                                text => $token->{tag_name}, token => $token);
4819                push @{$self->{open_elements}},
4820                    [$self->{head_element}, $el_category->{head}];
4821                $self->{head_element_inserted} = 1;
4822            } else {            } else {
4823              die "$0: $token->{type}: Unknown type";              !!!cp ('t113');
4824            }            }
         } elsif ($self->{insertion_mode} eq 'in head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'title') {  
               ## NOTE: There is an "as if in head" code clone  
               my $title_el;  
               !!!create-element ($title_el, 'title', $token->{attributes});  
               (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])  
                 ->append_child ($title_el);  
               $self->{content_model_flag} = 'RCDATA';  
               delete $self->{escape}; # MUST  
4825    
4826                my $text = '';            ## NOTE: There is a "as if in head" code clone.
4827                !!!next-token;            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4828                while ($token->{type} eq 'character') {            pop @{$self->{open_elements}} # <head>
4829                  $text .= $token->{data};                if $self->{insertion_mode} == AFTER_HEAD_IM;
4830              next B;
4831            } elsif ($token->{tag_name} eq 'style' or
4832                     $token->{tag_name} eq 'noframes') {
4833              ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4834              ## insertion mode IN_HEAD_IM)
4835              ## NOTE: There is a "as if in head" code clone.
4836              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4837                !!!cp ('t114');
4838                !!!parse-error (type => 'after head',
4839                                text => $token->{tag_name}, token => $token);
4840                push @{$self->{open_elements}},
4841                    [$self->{head_element}, $el_category->{head}];
4842                $self->{head_element_inserted} = 1;
4843              } else {
4844                !!!cp ('t115');
4845              }
4846              $parse_rcdata->(CDATA_CONTENT_MODEL);
4847              pop @{$self->{open_elements}} # <head>
4848                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4849              next B;
4850                } elsif ($token->{tag_name} eq 'noscript') {
4851                  if ($self->{insertion_mode} == IN_HEAD_IM) {
4852                    !!!cp ('t116');
4853                    ## NOTE: and scripting is disalbed
4854                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4855                    $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4856                    !!!nack ('t116.1');
4857                  !!!next-token;                  !!!next-token;
4858                }                  next B;
4859                if (length $text) {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4860                  $title_el->manakai_append_text ($text);                  !!!cp ('t117');
4861                }                  !!!parse-error (type => 'in noscript', text => 'noscript',
4862                                                  token => $token);
               $self->{content_model_flag} = 'PCDATA';  
                 
               if ($token->{type} eq 'end tag' and  
                   $token->{tag_name} eq 'title') {  
4863                  ## Ignore the token                  ## Ignore the token
4864                    !!!nack ('t117.1');
4865                    !!!next-token;
4866                    next B;
4867                } else {                } else {
4868                  !!!parse-error (type => 'in RCDATA:#'.$token->{type});                  !!!cp ('t118');
4869                  ## ISSUE: And ignore?                  #
4870                }                }
4871                !!!next-token;          } elsif ($token->{tag_name} eq 'script') {
4872                redo B;            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4873              } elsif ($token->{tag_name} eq 'style') {              !!!cp ('t119');
4874                $style_start_tag->();              ## As if </noscript>
4875                redo B;              pop @{$self->{open_elements}};
4876              } elsif ($token->{tag_name} eq 'script') {              !!!parse-error (type => 'in noscript', text => 'script',
4877                $script_start_tag->();                              token => $token);
4878                redo B;            
4879              } elsif ({base => 1, link => 1, meta => 1}->{$token->{tag_name}}) {              $self->{insertion_mode} = IN_HEAD_IM;
4880                ## NOTE: There are "as if in head" code clones              ## Reprocess in the "in head" insertion mode...
4881                my $el;            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4882                !!!create-element ($el, $token->{tag_name}, $token->{attributes});              !!!cp ('t120');
4883                (defined $self->{head_element} ? $self->{head_element} : $self->{open_elements}->[-1]->[0])              !!!parse-error (type => 'after head',
4884                  ->append_child ($el);                              text => $token->{tag_name}, token => $token);
4885                push @{$self->{open_elements}},
4886                    [$self->{head_element}, $el_category->{head}];
4887                $self->{head_element_inserted} = 1;
4888              } else {
4889                !!!cp ('t121');
4890              }
4891    
4892                !!!next-token;            ## NOTE: There is a "as if in head" code clone.
4893                redo B;            $script_start_tag->();
4894              } elsif ($token->{tag_name} eq 'head') {            pop @{$self->{open_elements}} # <head>
4895                !!!parse-error (type => 'in head:head');                if $self->{insertion_mode} == AFTER_HEAD_IM;
4896                ## Ignore the token            next B;
4897                !!!next-token;          } elsif ($token->{tag_name} eq 'body' or
4898                redo B;                   $token->{tag_name} eq 'frameset') {
4899              } else {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4900                #                  !!!cp ('t122');
4901              }                  ## As if </noscript>
4902            } elsif ($token->{type} eq 'end tag') {                  pop @{$self->{open_elements}};
4903              if ($token->{tag_name} eq 'head') {                  !!!parse-error (type => 'in noscript',
4904                if ($self->{open_elements}->[-1]->[1] eq 'head') {                                  text => $token->{tag_name}, token => $token);
4905                    
4906                    ## Reprocess in the "in head" insertion mode...
4907                    ## As if </head>
4908                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4909                    
4910                    ## Reprocess in the "after head" insertion mode...
4911                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4912                    !!!cp ('t124');
4913                    pop @{$self->{open_elements}};
4914                    
4915                    ## Reprocess in the "after head" insertion mode...
4916                } else {                } else {
4917                  !!!parse-error (type => 'unmatched end tag:head');                  !!!cp ('t125');
4918                }                }
               $self->{insertion_mode} = 'after head';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'html') {  
               #  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             }  
           } else {  
             #  
           }  
   
           if ($self->{open_elements}->[-1]->[1] eq 'head') {  
             ## As if </head>  
             pop @{$self->{open_elements}};  
           }  
           $self->{insertion_mode} = 'after head';  
           ## reprocess  
           redo B;  
4919    
4920            ## ISSUE: An issue in the spec.                ## "after head" insertion mode
4921          } elsif ($self->{insertion_mode} eq 'after head') {                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4922            if ($token->{type} eq 'character') {                if ($token->{tag_name} eq 'body') {
4923              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                  !!!cp ('t126');
4924                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                  $self->{insertion_mode} = IN_BODY_IM;
4925                unless (length $token->{data}) {                } elsif ($token->{tag_name} eq 'frameset') {
4926                  !!!next-token;                  !!!cp ('t127');
4927                  redo B;                  $self->{insertion_mode} = IN_FRAMESET_IM;
4928                  } else {
4929                    die "$0: tag name: $self->{tag_name}";
4930                }                }
4931              }                !!!nack ('t127.1');
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $self->{insertion_mode} = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'frameset') {  
               !!!insert-element ('frameset', $token->{attributes});  
               $self->{insertion_mode} = 'in frameset';  
4932                !!!next-token;                !!!next-token;
4933                redo B;                next B;
             } elsif ({  
                       base => 1, link => 1, meta => 1,  
                       script => 1, style => 1, title => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'after head:'.$token->{tag_name});  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
4934              } else {              } else {
4935                #                !!!cp ('t128');
4936              }                #
4937            } else {              }
             #  
           }  
             
           ## As if <body>  
           !!!insert-element ('body');  
           $self->{insertion_mode} = 'in body';  
           ## reprocess  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in body') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There is a code clone of "character in body".  
             $reconstruct_active_formatting_elements->($insert_to_current);  
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
4938    
4939              !!!next-token;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4940              redo B;                !!!cp ('t129');
4941            } elsif ($token->{type} eq 'comment') {                ## As if </noscript>
4942              ## NOTE: There is a code clone of "comment in body".                pop @{$self->{open_elements}};
4943              my $comment = $self->{document}->create_comment ($token->{data});                !!!parse-error (type => 'in noscript:/',
4944              $self->{open_elements}->[-1]->[0]->append_child ($comment);                                text => $token->{tag_name}, token => $token);
             !!!next-token;  
             redo B;  
           } else {  
             $in_body->($insert_to_current);  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There are "character in table" code clones.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
4945                                
4946                unless (length $token->{data}) {                ## Reprocess in the "in head" insertion mode...
4947                  !!!next-token;                ## As if </head>
4948                  redo B;                pop @{$self->{open_elements}};
4949                }  
4950                  ## Reprocess in the "after head" insertion mode...
4951                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4952                  !!!cp ('t130');
4953                  ## As if </head>
4954                  pop @{$self->{open_elements}};
4955    
4956                  ## Reprocess in the "after head" insertion mode...
4957                } else {
4958                  !!!cp ('t131');
4959              }              }
4960    
4961              !!!parse-error (type => 'in table:#character');              ## "after head" insertion mode
4962                ## As if <body>
4963                !!!insert-element ('body',, $token);
4964                $self->{insertion_mode} = IN_BODY_IM;
4965                ## reprocess
4966                !!!ack-later;
4967                next B;
4968              } elsif ($token->{type} == END_TAG_TOKEN) {
4969                if ($token->{tag_name} eq 'head') {
4970                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4971                    !!!cp ('t132');
4972                    ## As if <head>
4973                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4974                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4975                    push @{$self->{open_elements}},
4976                        [$self->{head_element}, $el_category->{head}];
4977    
4978              ## As if in body, but insert into foster parent element                  ## Reprocess in the "in head" insertion mode...
4979              ## ISSUE: Spec says that "whenever a node would be inserted                  pop @{$self->{open_elements}};
4980              ## into the current node" while characters might not be                  $self->{insertion_mode} = AFTER_HEAD_IM;
4981              ## result in a new Text node.                  !!!next-token;
4982              $reconstruct_active_formatting_elements->($insert_to_foster);                  next B;
4983                              } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4984              if ({                  !!!cp ('t133');
4985                   table => 1, tbody => 1, tfoot => 1,                  ## As if </noscript>
4986                   thead => 1, tr => 1,                  pop @{$self->{open_elements}};
4987                  }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'in noscript:/',
4988                # MUST                                  text => 'head', token => $token);
4989                my $foster_parent_element;                  
4990                my $next_sibling;                  ## Reprocess in the "in head" insertion mode...
4991                my $prev_sibling;                  pop @{$self->{open_elements}};
4992                OE: for (reverse 0..$#{$self->{open_elements}}) {                  $self->{insertion_mode} = AFTER_HEAD_IM;
4993                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  !!!next-token;
4994                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  next B;
4995                    if (defined $parent and $parent->node_type == 1) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4996                      $foster_parent_element = $parent;                  !!!cp ('t134');
4997                      $next_sibling = $self->{open_elements}->[$_]->[0];                  pop @{$self->{open_elements}};
4998                      $prev_sibling = $next_sibling->previous_sibling;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4999                    } else {                  !!!next-token;
5000                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                  next B;
5001                      $prev_sibling = $foster_parent_element->last_child;                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5002                    }                  !!!cp ('t134.1');
5003                    last OE;                  !!!parse-error (type => 'unmatched end tag', text => 'head',
5004                  }                                  token => $token);
5005                } # OE                  ## Ignore the token
5006                $foster_parent_element = $self->{open_elements}->[0]->[0] and                  !!!next-token;
5007                $prev_sibling = $foster_parent_element->last_child                  next B;
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
5008                } else {                } else {
5009                  $foster_parent_element->insert_before                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
5010                }                }
5011              } else {              } elsif ($token->{tag_name} eq 'noscript') {
5012                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5013              }                  !!!cp ('t136');
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({  
                  caption => 1,  
                  colgroup => 1,  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5014                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5015                    $self->{insertion_mode} = IN_HEAD_IM;
5016                    !!!next-token;
5017                    next B;
5018                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5019                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5020                    !!!cp ('t137');
5021                    !!!parse-error (type => 'unmatched end tag',
5022                                    text => 'noscript', token => $token);
5023                    ## Ignore the token ## ISSUE: An issue in the spec.
5024                    !!!next-token;
5025                    next B;
5026                  } else {
5027                    !!!cp ('t138');
5028                    #
5029                }                }
   
               push @$active_formatting_elements, ['#marker', '']  
                 if $token->{tag_name} eq 'caption';  
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = {  
                                  caption => 'in caption',  
                                  colgroup => 'in column group',  
                                  tbody => 'in table body',  
                                  tfoot => 'in table body',  
                                  thead => 'in table body',  
                                 }->{$token->{tag_name}};  
               !!!next-token;  
               redo B;  
5030              } elsif ({              } elsif ({
5031                        col => 1,                        body => 1, html => 1,
                       td => 1, th => 1, tr => 1,  
5032                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5033                ## Clear back to table context                ## TODO: This branch is entirely redundant.
5034                while ($self->{open_elements}->[-1]->[1] ne 'table' and                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5035                       $self->{open_elements}->[-1]->[1] ne 'html') {                    $self->{insertion_mode} == IN_HEAD_IM or
5036                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5037                  pop @{$self->{open_elements}};                  !!!cp ('t140');
5038                    !!!parse-error (type => 'unmatched end tag',
5039                                    text => $token->{tag_name}, token => $token);
5040                    ## Ignore the token
5041                    !!!next-token;
5042                    next B;
5043                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5044                    !!!cp ('t140.1');
5045                    !!!parse-error (type => 'unmatched end tag',
5046                                    text => $token->{tag_name}, token => $token);
5047                    ## Ignore the token
5048                    !!!next-token;
5049                    next B;
5050                  } else {
5051                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5052                }                }
5053                } elsif ($token->{tag_name} eq 'p') {
5054                  !!!cp ('t142');
5055                  !!!parse-error (type => 'unmatched end tag',
5056                                  text => $token->{tag_name}, token => $token);
5057                  ## Ignore the token
5058                  !!!next-token;
5059                  next B;
5060                } elsif ($token->{tag_name} eq 'br') {
5061                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5062                    !!!cp ('t142.2');
5063                    ## (before head) as if <head>, (in head) as if </head>
5064                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5065                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5066                    $self->{insertion_mode} = AFTER_HEAD_IM;
5067      
5068                    ## Reprocess in the "after head" insertion mode...
5069                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5070                    !!!cp ('t143.2');
5071                    ## As if </head>
5072                    pop @{$self->{open_elements}};
5073                    $self->{insertion_mode} = AFTER_HEAD_IM;
5074      
5075                    ## Reprocess in the "after head" insertion mode...
5076                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5077                    !!!cp ('t143.3');
5078                    ## ISSUE: Two parse errors for <head><noscript></br>
5079                    !!!parse-error (type => 'unmatched end tag',
5080                                    text => 'br', token => $token);
5081                    ## As if </noscript>
5082                    pop @{$self->{open_elements}};
5083                    $self->{insertion_mode} = IN_HEAD_IM;
5084    
5085                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                  ## Reprocess in the "in head" insertion mode...
5086                $self->{insertion_mode} = $token->{tag_name} eq 'col'                  ## As if </head>
5087                  ? 'in column group' : 'in table body';                  pop @{$self->{open_elements}};
5088                ## reprocess                  $self->{insertion_mode} = AFTER_HEAD_IM;
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: There are code clones for this "table in table"  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5089    
5090                ## As if </table>                  ## Reprocess in the "after head" insertion mode...
5091                ## have a table element in table scope                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5092                my $i;                  !!!cp ('t143.4');
5093                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  #
5094                  my $node = $self->{open_elements}->[$_];                } else {
5095                  if ($node->[1] eq 'table') {                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5096                }                }
5097    
5098                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5099                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'unmatched end tag',
5100                }                                text => 'br', token => $token);
5101                  ## Ignore the token
5102                  !!!next-token;
5103                  next B;
5104                } else {
5105                  !!!cp ('t145');
5106                  !!!parse-error (type => 'unmatched end tag',
5107                                  text => $token->{tag_name}, token => $token);
5108                  ## Ignore the token
5109                  !!!next-token;
5110                  next B;
5111                }
5112    
5113                splice @{$self->{open_elements}}, $i;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5114                  !!!cp ('t146');
5115                  ## As if </noscript>
5116                  pop @{$self->{open_elements}};
5117                  !!!parse-error (type => 'in noscript:/',
5118                                  text => $token->{tag_name}, token => $token);
5119                  
5120                  ## Reprocess in the "in head" insertion mode...
5121                  ## As if </head>
5122                  pop @{$self->{open_elements}};
5123    
5124                $self->_reset_insertion_mode;                ## Reprocess in the "after head" insertion mode...
5125                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5126                  !!!cp ('t147');
5127                  ## As if </head>
5128                  pop @{$self->{open_elements}};
5129    
5130                ## reprocess                ## Reprocess in the "after head" insertion mode...
5131                redo B;              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5132    ## ISSUE: This case cannot be reached?
5133                  !!!cp ('t148');
5134                  !!!parse-error (type => 'unmatched end tag',
5135                                  text => $token->{tag_name}, token => $token);
5136                  ## Ignore the token ## ISSUE: An issue in the spec.
5137                  !!!next-token;
5138                  next B;
5139              } else {              } else {
5140                #                !!!cp ('t149');
5141              }              }
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'table') {  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
5142    
5143                if ($self->{open_elements}->[-1]->[1] ne 'table') {              ## "after head" insertion mode
5144                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## As if <body>
5145                }              !!!insert-element ('body',, $token);
5146                $self->{insertion_mode} = IN_BODY_IM;
5147                ## reprocess
5148                next B;
5149          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5150            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5151              !!!cp ('t149.1');
5152    
5153              ## NOTE: As if <head>
5154              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5155              $self->{open_elements}->[-1]->[0]->append_child
5156                  ($self->{head_element});
5157              #push @{$self->{open_elements}},
5158              #    [$self->{head_element}, $el_category->{head}];
5159              #$self->{insertion_mode} = IN_HEAD_IM;
5160              ## NOTE: Reprocess.
5161    
5162              ## NOTE: As if </head>
5163              #pop @{$self->{open_elements}};
5164              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5165              ## NOTE: Reprocess.
5166              
5167              #
5168            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5169              !!!cp ('t149.2');
5170    
5171                splice @{$self->{open_elements}}, $i;            ## NOTE: As if </head>
5172              pop @{$self->{open_elements}};
5173              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5174              ## NOTE: Reprocess.
5175    
5176                $self->_reset_insertion_mode;            #
5177            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5178              !!!cp ('t149.3');
5179    
5180                !!!next-token;            !!!parse-error (type => 'in noscript:#eof', token => $token);
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
5181    
5182            !!!parse-error (type => 'in table:'.$token->{tag_name});            ## As if </noscript>
5183            $in_body->($insert_to_foster);            pop @{$self->{open_elements}};
5184            redo B;            #$self->{insertion_mode} = IN_HEAD_IM;
5185          } elsif ($self->{insertion_mode} eq 'in caption') {            ## NOTE: Reprocess.
5186            if ($token->{type} eq 'character') {  
5187              ## NOTE: This is a code clone of "character in body".            ## NOTE: As if </head>
5188              pop @{$self->{open_elements}};
5189              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5190              ## NOTE: Reprocess.
5191    
5192              #
5193            } else {
5194              !!!cp ('t149.4');
5195              #
5196            }
5197    
5198            ## NOTE: As if <body>
5199            !!!insert-element ('body',, $token);
5200            $self->{insertion_mode} = IN_BODY_IM;
5201            ## NOTE: Reprocess.
5202            next B;
5203          } else {
5204            die "$0: $token->{type}: Unknown token type";
5205          }
5206        } elsif ($self->{insertion_mode} & BODY_IMS) {
5207              if ($token->{type} == CHARACTER_TOKEN) {
5208                !!!cp ('t150');
5209                ## NOTE: There is a code clone of "character in body".
5210              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5211                            
5212              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5213    
5214              !!!next-token;              !!!next-token;
5215              redo B;              next B;
5216            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} == START_TAG_TOKEN) {
             ## NOTE: This is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
5217              if ({              if ({
5218                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5219                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5220                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5221                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
5222                    ## have an element in table scope
5223                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
5224                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
5225                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
5226                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
5227                  my $node = $self->{open_elements}->[$_];  
5228                  if ($node->[1] eq 'caption') {                      ## Close the cell
5229                    $i = $_;                      !!!back-token; # <x>
5230                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
5231                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
5232                            table => 1, html => 1,                                line => $token->{line},
5233                           }->{$node->[1]}) {                                column => $token->{column}};
5234                    last INSCOPE;                      next B;
5235                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5236                        !!!cp ('t152');
5237                        ## ISSUE: This case can never be reached, maybe.
5238                        last;
5239                      }
5240                  }                  }
5241                } # INSCOPE  
5242                unless (defined $i) {                  !!!cp ('t153');
5243                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
5244                        text => $token->{tag_name}, token => $token);
5245                  ## Ignore the token                  ## Ignore the token
5246                    !!!nack ('t153.1');
5247                  !!!next-token;                  !!!next-token;
5248                  redo B;                  next B;
5249                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5250                                  !!!parse-error (type => 'not closed', text => 'caption',
5251                ## generate implied end tags                                  token => $token);
5252                if ({                  
5253                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5254                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5255                    }->{$self->{open_elements}->[-1]->[1]}) {                  my $i;
5256                  !!!back-token; # <?>                  INSCOPE: {
5257                  $token = {type => 'end tag', tag_name => 'caption'};                    for (reverse 0..$#{$self->{open_elements}}) {
5258                  !!!back-token;                      my $node = $self->{open_elements}->[$_];
5259                  $token = {type => 'end tag',                      if ($node->[1] & CAPTION_EL) {
5260                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        !!!cp ('t155');
5261                  redo B;                        $i = $_;
5262                }                        last INSCOPE;
5263                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5264                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        !!!cp ('t156');
5265                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                        last;
5266                }                      }
5267                      }
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
5268    
5269                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
5270                      !!!parse-error (type => 'start tag not allowed',
5271                                      text => $token->{tag_name}, token => $token);
5272                      ## Ignore the token
5273                      !!!nack ('t157.1');
5274                      !!!next-token;
5275                      next B;
5276                    } # INSCOPE
5277                    
5278                    ## generate implied end tags
5279                    while ($self->{open_elements}->[-1]->[1]
5280                               & END_TAG_OPTIONAL_EL) {
5281                      !!!cp ('t158');
5282                      pop @{$self->{open_elements}};
5283                    }
5284    
5285                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5286                redo B;                    !!!cp ('t159');
5287                      !!!parse-error (type => 'not closed',
5288                                      text => $self->{open_elements}->[-1]->[0]
5289                                          ->manakai_local_name,
5290                                      token => $token);
5291                    } else {
5292                      !!!cp ('t160');
5293                    }
5294                    
5295                    splice @{$self->{open_elements}}, $i;
5296                    
5297                    $clear_up_to_marker->();
5298                    
5299                    $self->{insertion_mode} = IN_TABLE_IM;
5300                    
5301                    ## reprocess
5302                    !!!ack-later;
5303                    next B;
5304                  } else {
5305                    !!!cp ('t161');
5306                    #
5307                  }
5308              } else {              } else {
5309                  !!!cp ('t162');
5310                #                #
5311              }              }
5312            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5313              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5314                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
5315                my $i;                  ## have an element in table scope
5316                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
5317                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5318                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5319                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5320                    last INSCOPE;                      !!!cp ('t163');
5321                  } elsif ({                      $i = $_;
5322                            table => 1, html => 1,                      last INSCOPE;
5323                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5324                    last INSCOPE;                      !!!cp ('t164');
5325                        last INSCOPE;
5326                      }
5327                    } # INSCOPE
5328                      unless (defined $i) {
5329                        !!!cp ('t165');
5330                        !!!parse-error (type => 'unmatched end tag',
5331                                        text => $token->{tag_name},
5332                                        token => $token);
5333                        ## Ignore the token
5334                        !!!next-token;
5335                        next B;
5336                      }
5337                    
5338                    ## generate implied end tags
5339                    while ($self->{open_elements}->[-1]->[1]
5340                               & END_TAG_OPTIONAL_EL) {
5341                      !!!cp ('t166');
5342                      pop @{$self->{open_elements}};
5343                  }                  }
5344                } # INSCOPE  
5345                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5346                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5347                      !!!cp ('t167');
5348                      !!!parse-error (type => 'not closed',
5349                                      text => $self->{open_elements}->[-1]->[0]
5350                                          ->manakai_local_name,
5351                                      token => $token);
5352                    } else {
5353                      !!!cp ('t168');
5354                    }
5355                    
5356                    splice @{$self->{open_elements}}, $i;
5357                    
5358                    $clear_up_to_marker->();
5359                    
5360                    $self->{insertion_mode} = IN_ROW_IM;
5361                    
5362                    !!!next-token;
5363                    next B;
5364                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5365                    !!!cp ('t169');
5366                    !!!parse-error (type => 'unmatched end tag',
5367                                    text => $token->{tag_name}, token => $token);
5368                  ## Ignore the token                  ## Ignore the token
5369                  !!!next-token;                  !!!next-token;
5370                  redo B;                  next B;
5371                }                } else {
5372                                  !!!cp ('t170');
5373                ## generate implied end tags                  #
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5374                }                }
5375                } elsif ($token->{tag_name} eq 'caption') {
5376                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5377                    ## have a table element in table scope
5378                    my $i;
5379                    INSCOPE: {
5380                      for (reverse 0..$#{$self->{open_elements}}) {
5381                        my $node = $self->{open_elements}->[$_];
5382                        if ($node->[1] & CAPTION_EL) {
5383                          !!!cp ('t171');
5384                          $i = $_;
5385                          last INSCOPE;
5386                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5387                          !!!cp ('t172');
5388                          last;
5389                        }
5390                      }
5391    
5392                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5393                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5394                                      text => $token->{tag_name}, token => $token);
5395                      ## Ignore the token
5396                      !!!next-token;
5397                      next B;
5398                    } # INSCOPE
5399                    
5400                    ## generate implied end tags
5401                    while ($self->{open_elements}->[-1]->[1]
5402                               & END_TAG_OPTIONAL_EL) {
5403                      !!!cp ('t174');
5404                      pop @{$self->{open_elements}};
5405                    }
5406                    
5407                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5408                      !!!cp ('t175');
5409                      !!!parse-error (type => 'not closed',
5410                                      text => $self->{open_elements}->[-1]->[0]
5411                                          ->manakai_local_name,
5412                                      token => $token);
5413                    } else {
5414                      !!!cp ('t176');
5415                    }
5416                    
5417                    splice @{$self->{open_elements}}, $i;
5418                    
5419                    $clear_up_to_marker->();
5420                    
5421                    $self->{insertion_mode} = IN_TABLE_IM;
5422                    
5423                    !!!next-token;
5424                    next B;
5425                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5426                    !!!cp ('t177');
5427                    !!!parse-error (type => 'unmatched end tag',
5428                                    text => $token->{tag_name}, token => $token);
5429                    ## Ignore the token
5430                    !!!next-token;
5431                    next B;
5432                  } else {
5433                    !!!cp ('t178');
5434                    #
5435                }                }
5436                } elsif ({
5437                          table => 1, tbody => 1, tfoot => 1,
5438                          thead => 1, tr => 1,
5439                         }->{$token->{tag_name}} and
5440                         $self->{insertion_mode} == IN_CELL_IM) {
5441                  ## have an element in table scope
5442                  my $i;
5443                  my $tn;
5444                  INSCOPE: {
5445                    for (reverse 0..$#{$self->{open_elements}}) {
5446                      my $node = $self->{open_elements}->[$_];
5447                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5448                        !!!cp ('t179');
5449                        $i = $_;
5450    
5451                        ## Close the cell
5452                        !!!back-token; # </x>
5453                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5454                                  line => $token->{line},
5455                                  column => $token->{column}};
5456                        next B;
5457                      } elsif ($node->[1] & TABLE_CELL_EL) {
5458                        !!!cp ('t180');
5459                        $tn = $node->[0]->manakai_local_name;
5460                        ## NOTE: There is exactly one |td| or |th| element
5461                        ## in scope in the stack of open elements by definition.
5462                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5463                        ## ISSUE: Can this be reached?
5464                        !!!cp ('t181');
5465                        last;
5466                      }
5467                    }
5468    
5469                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5470                    !!!parse-error (type => 'unmatched end tag',
5471                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5472                    ## Ignore the token
5473                $self->{insertion_mode} = 'in table';                  !!!next-token;
5474                    next B;
5475                !!!next-token;                } # INSCOPE
5476                redo B;              } elsif ($token->{tag_name} eq 'table' and
5477              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5478                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5479                                  token => $token);
5480    
5481                ## As if </caption>                ## As if </caption>
5482                ## have a table element in table scope                ## have a table element in table scope
5483                my $i;                my $i;
5484                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5485                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5486                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5487                      !!!cp ('t184');
5488                    $i = $_;                    $i = $_;
5489                    last INSCOPE;                    last INSCOPE;
5490                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5491                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5492                    last INSCOPE;                    last INSCOPE;
5493                  }                  }
5494                } # INSCOPE                } # INSCOPE
5495                unless (defined $i) {                unless (defined $i) {
5496                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5497                    !!!parse-error (type => 'unmatched end tag',
5498                                    text => 'caption', token => $token);
5499                  ## Ignore the token                  ## Ignore the token
5500                  !!!next-token;                  !!!next-token;
5501                  redo B;                  next B;
5502                }                }
5503                                
5504                ## generate implied end tags                ## generate implied end tags
5505                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5506                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5507                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5508                }                }
5509    
5510                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5511                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5512                    !!!parse-error (type => 'not closed',
5513                                    text => $self->{open_elements}->[-1]->[0]
5514                                        ->manakai_local_name,
5515                                    token => $token);
5516                  } else {
5517                    !!!cp ('t189');
5518                }                }
5519    
5520                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5521    
5522                $clear_up_to_marker->();                $clear_up_to_marker->();
5523    
5524                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5525    
5526                ## reprocess                ## reprocess
5527                redo B;                next B;
5528              } elsif ({              } elsif ({
5529                        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,  
5530                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5531                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5532                ## Ignore the token                  !!!cp ('t190');
5533                redo B;                  !!!parse-error (type => 'unmatched end tag',
5534              } else {                                  text => $token->{tag_name}, token => $token);
               #  
             }  
           } else {  
             #  
           }  
                 
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in column group') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'col') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'colgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html') {  
                 !!!parse-error (type => 'unmatched end tag:colgroup');  
5535                  ## Ignore the token                  ## Ignore the token
5536                  !!!next-token;                  !!!next-token;
5537                  redo B;                  next B;
5538                } else {                } else {
5539                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
5540                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5541                }                }
5542              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5543                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
5544                          thead => 1, tr => 1,
5545                         }->{$token->{tag_name}} and
5546                         $self->{insertion_mode} == IN_CAPTION_IM) {
5547                  !!!cp ('t192');
5548                  !!!parse-error (type => 'unmatched end tag',
5549                                  text => $token->{tag_name}, token => $token);
5550                ## Ignore the token                ## Ignore the token
5551                !!!next-token;                !!!next-token;
5552                redo B;                next B;
5553              } else {              } else {
5554                #                !!!cp ('t193');
5555                  #
5556              }              }
5557            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5558              #          for my $entry (@{$self->{open_elements}}) {
5559              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5560                !!!cp ('t75');
5561                !!!parse-error (type => 'in body:#eof', token => $token);
5562                last;
5563            }            }
5564            }
5565    
5566            ## As if </colgroup>          ## Stop parsing.
5567            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
5568              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
5569              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5570          }
5571    
5572          $insert = $insert_to_current;
5573          #
5574        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5575          if ($token->{type} == CHARACTER_TOKEN) {
5576            if (not $open_tables->[-1]->[1] and # tainted
5577                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5578              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5579                  
5580              unless (length $token->{data}) {
5581                !!!cp ('t194');
5582              !!!next-token;              !!!next-token;
5583              redo B;              next B;
5584            } else {            } else {
5585              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
5586            }            }
5587          } elsif ($self->{insertion_mode} eq 'in table body') {          }
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
5588    
5589              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table:#text', token => $token);
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5590    
5591              if ({          ## NOTE: As if in body, but insert into the foster parent element.
5592                   table => 1, tbody => 1, tfoot => 1,          $reconstruct_active_formatting_elements->($insert_to_foster);
5593                   thead => 1, tr => 1,              
5594                  }->{$self->{open_elements}->[-1]->[1]}) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5595                # MUST            # MUST
5596                my $foster_parent_element;            my $foster_parent_element;
5597                my $next_sibling;            my $next_sibling;
5598                my $prev_sibling;            my $prev_sibling;
5599                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
5600                  if ($self->{open_elements}->[$_]->[1] eq 'table') {              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5601                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5602                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
5603                      $foster_parent_element = $parent;                  $foster_parent_element = $parent;
5604                      $next_sibling = $self->{open_elements}->[$_]->[0];                  !!!cp ('t196');
5605                      $prev_sibling = $next_sibling->previous_sibling;                  $next_sibling = $self->{open_elements}->[$_]->[0];
5606                    } else {                  $prev_sibling = $next_sibling->previous_sibling;
5607                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                  #
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
5608                } else {                } else {
5609                  $foster_parent_element->insert_before                  !!!cp ('t197');
5610                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5611                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5612                }                  #
5613              } else {                }
5614                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                last OE;
5615                }
5616              } # OE
5617              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5618              $prev_sibling = $foster_parent_element->last_child
5619                  unless defined $foster_parent_element;
5620              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5621              if (defined $prev_sibling and
5622                  $prev_sibling->node_type == 3) {
5623                !!!cp ('t198');
5624                $prev_sibling->manakai_append_text ($token->{data});
5625              } else {
5626                !!!cp ('t199');
5627                $foster_parent_element->insert_before
5628                    ($self->{document}->create_text_node ($token->{data}),
5629                     $next_sibling);
5630              }
5631              $open_tables->[-1]->[1] = 1; # tainted
5632              $open_tables->[-1]->[2] = 1; # ~node inserted
5633            } else {
5634              ## NOTE: Fragment case or in a foster parent'ed element
5635              ## (e.g. |<table><span>a|).  In fragment case, whether the
5636              ## character is appended to existing node or a new node is
5637              ## created is irrelevant, since the foster parent'ed nodes
5638              ## are discarded and fragment parsing does not invoke any
5639              ## script.
5640              !!!cp ('t200');
5641              $self->{open_elements}->[-1]->[0]->manakai_append_text
5642                  ($token->{data});
5643            }
5644                
5645            !!!next-token;
5646            next B;
5647          } elsif ($token->{type} == START_TAG_TOKEN) {
5648            if ({
5649                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5650                 th => 1, td => 1,
5651                }->{$token->{tag_name}}) {
5652              if ($self->{insertion_mode} == IN_TABLE_IM) {
5653                ## Clear back to table context
5654                while (not ($self->{open_elements}->[-1]->[1]
5655                                & TABLE_SCOPING_EL)) {
5656                  !!!cp ('t201');
5657                  pop @{$self->{open_elements}};
5658              }              }
5659                            
5660              !!!next-token;              !!!insert-element ('tbody',, $token);
5661              redo B;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5662            } elsif ($token->{type} eq 'comment') {              ## reprocess in the "in table body" insertion mode...
5663              ## Copied from 'in table'            }
5664              my $comment = $self->{document}->create_comment ($token->{data});            
5665              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5666              !!!next-token;              unless ($token->{tag_name} eq 'tr') {
5667              redo B;                !!!cp ('t202');
5668            } elsif ($token->{type} eq 'start tag') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
5669              if ({              }
5670                   tr => 1,                  
5671                   th => 1, td => 1,              ## Clear back to table body context
5672                  }->{$token->{tag_name}}) {              while (not ($self->{open_elements}->[-1]->[1]
5673                unless ($token->{tag_name} eq 'tr') {                              & TABLE_ROWS_SCOPING_EL)) {
5674                  !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t203');
5675                }                ## ISSUE: Can this case be reached?
5676                  pop @{$self->{open_elements}};
5677                }
5678                    
5679                $self->{insertion_mode} = IN_ROW_IM;
5680                if ($token->{tag_name} eq 'tr') {
5681                  !!!cp ('t204');
5682                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5683                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5684                  !!!nack ('t204');
5685                  !!!next-token;
5686                  next B;
5687                } else {
5688                  !!!cp ('t205');
5689                  !!!insert-element ('tr',, $token);
5690                  ## reprocess in the "in row" insertion mode
5691                }
5692              } else {
5693                !!!cp ('t206');
5694              }
5695    
5696                ## Clear back to table body context                ## Clear back to table row context
5697                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5698                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5699                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5700                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5701                }                }
5702                                
5703                $self->{insertion_mode} = 'in row';            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5704                if ($token->{tag_name} eq 'tr') {            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5705                  !!!insert-element ($token->{tag_name}, $token->{attributes});            $self->{insertion_mode} = IN_CELL_IM;
5706                  !!!next-token;  
5707                } else {            push @$active_formatting_elements, ['#marker', ''];
5708                  !!!insert-element ('tr');                
5709                  ## reprocess            !!!nack ('t207.1');
5710                }            !!!next-token;
5711                redo B;            next B;
5712              } elsif ({          } elsif ({
5713                        caption => 1, col => 1, colgroup => 1,                    caption => 1, col => 1, colgroup => 1,
5714                        tbody => 1, tfoot => 1, thead => 1,                    tbody => 1, tfoot => 1, thead => 1,
5715                       }->{$token->{tag_name}}) {                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5716                ## have an element in table scope                   }->{$token->{tag_name}}) {
5717                my $i;            if ($self->{insertion_mode} == IN_ROW_IM) {
5718                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              ## As if </tr>
5719                  my $node = $self->{open_elements}->[$_];              ## have an element in table scope
5720                  if ({              my $i;
5721                       tbody => 1, thead => 1, tfoot => 1,              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5722                      }->{$node->[1]}) {                my $node = $self->{open_elements}->[$_];
5723                    $i = $_;                if ($node->[1] & TABLE_ROW_EL) {
5724                    last INSCOPE;                  !!!cp ('t208');
5725                  } elsif ({                  $i = $_;
5726                            table => 1, html => 1,                  last INSCOPE;
5727                           }->{$node->[1]}) {                } elsif ($node->[1] & TABLE_SCOPING_EL) {
5728                    last INSCOPE;                  !!!cp ('t209');
5729                    last INSCOPE;
5730                  }
5731                } # INSCOPE
5732                unless (defined $i) {
5733                  !!!cp ('t210');
5734                  ## TODO: This type is wrong.
5735                  !!!parse-error (type => 'unmacthed end tag',
5736                                  text => $token->{tag_name}, token => $token);
5737                  ## Ignore the token
5738                  !!!nack ('t210.1');
5739                  !!!next-token;
5740                  next B;
5741                }
5742                    
5743                    ## Clear back to table row context
5744                    while (not ($self->{open_elements}->[-1]->[1]
5745                                    & TABLE_ROW_SCOPING_EL)) {
5746                      !!!cp ('t211');
5747                      ## ISSUE: Can this case be reached?
5748                      pop @{$self->{open_elements}};
5749                    }
5750                    
5751                    pop @{$self->{open_elements}}; # tr
5752                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5753                    if ($token->{tag_name} eq 'tr') {
5754                      !!!cp ('t212');
5755                      ## reprocess
5756                      !!!ack-later;
5757                      next B;
5758                    } else {
5759                      !!!cp ('t213');
5760                      ## reprocess in the "in table body" insertion mode...
5761                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5762                }                }
5763    
5764                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5765                while (not {                  ## have an element in table scope
5766                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5767                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5768                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5769                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5770                        !!!cp ('t214');
5771                        $i = $_;
5772                        last INSCOPE;
5773                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5774                        !!!cp ('t215');
5775                        last INSCOPE;
5776                      }
5777                    } # INSCOPE
5778                    unless (defined $i) {
5779                      !!!cp ('t216');
5780    ## TODO: This erorr type is wrong.
5781                      !!!parse-error (type => 'unmatched end tag',
5782                                      text => $token->{tag_name}, token => $token);
5783                      ## Ignore the token
5784                      !!!nack ('t216.1');
5785                      !!!next-token;
5786                      next B;
5787                    }
5788    
5789                    ## Clear back to table body context
5790                    while (not ($self->{open_elements}->[-1]->[1]
5791                                    & TABLE_ROWS_SCOPING_EL)) {
5792                      !!!cp ('t217');
5793                      ## ISSUE: Can this state be reached?
5794                      pop @{$self->{open_elements}};
5795                    }
5796                    
5797                    ## As if <{current node}>
5798                    ## have an element in table scope
5799                    ## true by definition
5800                    
5801                    ## Clear back to table body context
5802                    ## nop by definition
5803                    
5804                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5805                    $self->{insertion_mode} = IN_TABLE_IM;
5806                    ## reprocess in "in table" insertion mode...
5807                  } else {
5808                    !!!cp ('t218');
5809                }                }
5810    
5811                ## As if <{current node}>            if ($token->{tag_name} eq 'col') {
5812                ## have an element in table scope              ## Clear back to table context
5813                ## true by definition              while (not ($self->{open_elements}->[-1]->[1]
5814                                & TABLE_SCOPING_EL)) {
5815                ## Clear back to table body context                !!!cp ('t219');
5816                ## nop by definition                ## ISSUE: Can this state be reached?
   
5817                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5818                $self->{insertion_mode} = 'in table';              }
5819                ## reprocess              
5820                redo B;              !!!insert-element ('colgroup',, $token);
5821                $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5822                ## reprocess
5823                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5824                !!!ack-later;
5825                next B;
5826              } elsif ({
5827                        caption => 1,
5828                        colgroup => 1,
5829                        tbody => 1, tfoot => 1, thead => 1,
5830                       }->{$token->{tag_name}}) {
5831                ## Clear back to table context
5832                    while (not ($self->{open_elements}->[-1]->[1]
5833                                    & TABLE_SCOPING_EL)) {
5834                      !!!cp ('t220');
5835                      ## ISSUE: Can this state be reached?
5836                      pop @{$self->{open_elements}};
5837                    }
5838                    
5839                push @$active_formatting_elements, ['#marker', '']
5840                    if $token->{tag_name} eq 'caption';
5841                    
5842                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5843                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5844                $self->{insertion_mode} = {
5845                                           caption => IN_CAPTION_IM,
5846                                           colgroup => IN_COLUMN_GROUP_IM,
5847                                           tbody => IN_TABLE_BODY_IM,
5848                                           tfoot => IN_TABLE_BODY_IM,
5849                                           thead => IN_TABLE_BODY_IM,
5850                                          }->{$token->{tag_name}};
5851                !!!next-token;
5852                !!!nack ('t220.1');
5853                next B;
5854              } else {
5855                die "$0: in table: <>: $token->{tag_name}";
5856              }
5857              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5858                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5859                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5860                                      ->manakai_local_name,
5861                                  token => $token);
5862    
5863                ## As if </table>                ## As if </table>
5864                ## have a table element in table scope                ## have a table element in table scope
5865                my $i;                my $i;
5866                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5867                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5868                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5869                      !!!cp ('t221');
5870                    $i = $_;                    $i = $_;
5871                    last INSCOPE;                    last INSCOPE;
5872                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5873                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5874                    last INSCOPE;                    last INSCOPE;
5875                  }                  }
5876                } # INSCOPE                } # INSCOPE
5877                unless (defined $i) {                unless (defined $i) {
5878                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5879    ## TODO: The following is wrong, maybe.
5880                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5881                                    token => $token);
5882                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5883                    !!!nack ('t223.1');
5884                  !!!next-token;                  !!!next-token;
5885                  redo B;                  next B;
5886                }                }
5887                                
5888    ## TODO: Followings are removed from the latest spec.
5889                ## generate implied end tags                ## generate implied end tags
5890                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5891                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5892                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5893                }                }
5894    
5895                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5896                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5897                    ## NOTE: |<table><tr><table>|
5898                    !!!parse-error (type => 'not closed',
5899                                    text => $self->{open_elements}->[-1]->[0]
5900                                        ->manakai_local_name,
5901                                    token => $token);
5902                  } else {
5903                    !!!cp ('t226');
5904                }                }
5905    
5906                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5907                  pop @{$open_tables};
5908    
5909                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5910    
5911                ## reprocess            ## reprocess
5912                redo B;            !!!ack-later;
5913              } else {            next B;
5914                #          } elsif ($token->{tag_name} eq 'style') {
5915              }            if (not $open_tables->[-1]->[1]) { # tainted
5916            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5917              if ({              ## NOTE: This is a "as if in head" code clone.
5918                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5919                  }->{$token->{tag_name}}) {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5920                ## have an element in table scope              next B;
5921                my $i;            } else {
5922                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!cp ('t227.7');
5923                  my $node = $self->{open_elements}->[$_];              #
5924                  if ($node->[1] eq $token->{tag_name}) {            }
5925                    $i = $_;          } elsif ($token->{tag_name} eq 'script') {
5926                    last INSCOPE;            if (not $open_tables->[-1]->[1]) { # tainted
5927                  } elsif ({              !!!cp ('t227.6');
5928                            table => 1, html => 1,              ## NOTE: This is a "as if in head" code clone.
5929                           }->{$node->[1]}) {              $script_start_tag->();
5930                    last INSCOPE;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5931                  }              next B;
5932                } # INSCOPE            } else {
5933                unless (defined $i) {              !!!cp ('t227.5');
5934                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              #
5935                  ## Ignore the token            }
5936                  !!!next-token;          } elsif ($token->{tag_name} eq 'input') {
5937                  redo B;            if (not $open_tables->[-1]->[1]) { # tainted
5938                }              if ($token->{attributes}->{type}) { ## TODO: case
5939                  my $type = lc $token->{attributes}->{type}->{value};
5940                  if ($type eq 'hidden') {
5941                    !!!cp ('t227.3');
5942                    !!!parse-error (type => 'in table',
5943                                    text => $token->{tag_name}, token => $token);
5944    
5945                ## Clear back to table body context                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5946                while (not {                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
5947    
5948                pop @{$self->{open_elements}};                  ## TODO: form element pointer
               $self->{insertion_mode} = 'in table';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ({  
                      tbody => 1, thead => 1, tfoot => 1,  
                     }->{$node->[1]}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
5949    
               ## Clear back to table body context  
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5950                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
5951    
5952                ## As if <{current node}>                  !!!next-token;
5953                ## have an element in table scope                  !!!ack ('t227.2.1');
5954                ## true by definition                  next B;
5955                  } else {
5956                ## Clear back to table body context                  !!!cp ('t227.2');
5957                ## nop by definition                  #
5958                  }
               pop @{$self->{open_elements}};  
               $self->{insertion_mode} = 'in table';  
               ## reprocess  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
5959              } else {              } else {
5960                  !!!cp ('t227.1');
5961                #                #
5962              }              }
5963            } else {            } else {
5964                !!!cp ('t227.4');
5965              #              #
5966            }            }
5967                      } else {
5968            ## As if in table            !!!cp ('t227');
5969            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5970            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in row') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
5971    
5972              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table', text => $token->{tag_name},
5973                            token => $token);
5974    
5975              ## As if in body, but insert into foster parent element          $insert = $insert_to_foster;
5976              ## ISSUE: Spec says that "whenever a node would be inserted          #
5977              ## into the current node" while characters might not be        } elsif ($token->{type} == END_TAG_TOKEN) {
5978              ## result in a new Text node.              if ($token->{tag_name} eq 'tr' and
5979              $reconstruct_active_formatting_elements->($insert_to_foster);                  $self->{insertion_mode} == IN_ROW_IM) {
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
   
               push @$active_formatting_elements, ['#marker', ''];  
                 
               !!!next-token;  
               redo B;  
             } elsif ({  
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
5980                ## have an element in table scope                ## have an element in table scope
5981                my $i;                my $i;
5982                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5983                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5984                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5985                      !!!cp ('t228');
5986                    $i = $_;                    $i = $_;
5987                    last INSCOPE;                    last INSCOPE;
5988                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5989                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5990                    last INSCOPE;                    last INSCOPE;
5991                  }                  }
5992                } # INSCOPE                } # INSCOPE
5993                unless (defined $i) {                unless (defined $i) {
5994                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
5995                    !!!parse-error (type => 'unmatched end tag',
5996                                    text => $token->{tag_name}, token => $token);
5997                  ## Ignore the token                  ## Ignore the token
5998                    !!!nack ('t230.1');
5999                  !!!next-token;                  !!!next-token;
6000                  redo B;                  next B;
6001                  } else {
6002                    !!!cp ('t232');
6003                }                }
6004    
6005                ## Clear back to table row context                ## Clear back to table row context
6006                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6007                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6008                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6009                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6010                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6011                }                }
6012    
6013                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6014                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6015                ## reprocess                !!!next-token;
6016                redo B;                !!!nack ('t231.1');
6017                  next B;
6018              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6019                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
6020                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
6021                    ## have an element in table scope
6022                ## As if </table>                  my $i;
6023                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6024                my $i;                    my $node = $self->{open_elements}->[$_];
6025                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
6026                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
6027                  if ($node->[1] eq 'table') {                      $i = $_;
6028                    $i = $_;                      last INSCOPE;
6029                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6030                  } elsif ({                      !!!cp ('t234');
6031                            table => 1, html => 1,                      last INSCOPE;
6032                           }->{$node->[1]}) {                    }
6033                    last INSCOPE;                  } # INSCOPE
6034                    unless (defined $i) {
6035                      !!!cp ('t235');
6036    ## TODO: The following is wrong.
6037                      !!!parse-error (type => 'unmatched end tag',
6038                                      text => $token->{type}, token => $token);
6039                      ## Ignore the token
6040                      !!!nack ('t236.1');
6041                      !!!next-token;
6042                      next B;
6043                  }                  }
6044                } # INSCOPE                  
6045                unless (defined $i) {                  ## Clear back to table row context
6046                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
6047                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
6048                  !!!next-token;                    !!!cp ('t236');
6049                  redo B;  ## ISSUE: Can this state be reached?
6050                }                    pop @{$self->{open_elements}};
6051                                  }
6052                ## generate implied end tags                  
6053                if ({                  pop @{$self->{open_elements}}; # tr
6054                     dd => 1, dt => 1, li => 1, p => 1,                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6055                     td => 1, th => 1, tr => 1,                  ## reprocess in the "in table body" insertion mode...
6056                    }->{$self->{open_elements}->[-1]->[1]}) {                }
6057                  !!!back-token; # <table>  
6058                  $token = {type => 'end tag', tag_name => 'table'};                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6059                  !!!back-token;                  ## have an element in table scope
6060                  $token = {type => 'end tag',                  my $i;
6061                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6062                  redo B;                    my $node = $self->{open_elements}->[$_];
6063                }                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6064                        !!!cp ('t237');
6065                if ($self->{open_elements}->[-1]->[1] ne 'table') {                      $i = $_;
6066                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      last INSCOPE;
6067                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6068                        !!!cp ('t238');
6069                        last INSCOPE;
6070                      }
6071                    } # INSCOPE
6072                    unless (defined $i) {
6073                      !!!cp ('t239');
6074                      !!!parse-error (type => 'unmatched end tag',
6075                                      text => $token->{tag_name}, token => $token);
6076                      ## Ignore the token
6077                      !!!nack ('t239.1');
6078                      !!!next-token;
6079                      next B;
6080                    }
6081                    
6082                    ## Clear back to table body context
6083                    while (not ($self->{open_elements}->[-1]->[1]
6084                                    & TABLE_ROWS_SCOPING_EL)) {
6085                      !!!cp ('t240');
6086                      pop @{$self->{open_elements}};
6087                    }
6088                    
6089                    ## As if <{current node}>
6090                    ## have an element in table scope
6091                    ## true by definition
6092                    
6093                    ## Clear back to table body context
6094                    ## nop by definition
6095                    
6096                    pop @{$self->{open_elements}};
6097                    $self->{insertion_mode} = IN_TABLE_IM;
6098                    ## reprocess in the "in table" insertion mode...
6099                }                }
6100    
6101                splice @{$self->{open_elements}}, $i;                ## NOTE: </table> in the "in table" insertion mode.
6102                  ## When you edit the code fragment below, please ensure that
6103                $self->_reset_insertion_mode;                ## the code for <table> in the "in table" insertion mode
6104                  ## is synced with it.
6105    
6106                ## reprocess                ## have a table element in table scope
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
6107                my $i;                my $i;
6108                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6109                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6110                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6111                      !!!cp ('t241');
6112                    $i = $_;                    $i = $_;
6113                    last INSCOPE;                    last INSCOPE;
6114                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6115                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6116                    last INSCOPE;                    last INSCOPE;
6117                  }                  }
6118                } # INSCOPE                } # INSCOPE
6119                unless (defined $i) {                unless (defined $i) {
6120                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6121                    !!!parse-error (type => 'unmatched end tag',
6122                                    text => $token->{tag_name}, token => $token);
6123                  ## Ignore the token                  ## Ignore the token
6124                    !!!nack ('t243.1');
6125                  !!!next-token;                  !!!next-token;
6126                  redo B;                  next B;
6127                }                }
6128                    
6129                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6130                while (not {                pop @{$open_tables};
6131                  tr => 1, html => 1,                
6132                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6133                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6134                  pop @{$self->{open_elements}};                !!!next-token;
6135                  next B;
6136                } elsif ({
6137                          tbody => 1, tfoot => 1, thead => 1,
6138                         }->{$token->{tag_name}} and
6139                         $self->{insertion_mode} & ROW_IMS) {
6140                  if ($self->{insertion_mode} == IN_ROW_IM) {
6141                    ## have an element in table scope
6142                    my $i;
6143                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6144                      my $node = $self->{open_elements}->[$_];
6145                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6146                        !!!cp ('t247');
6147                        $i = $_;
6148                        last INSCOPE;
6149                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6150                        !!!cp ('t248');
6151                        last INSCOPE;
6152                      }
6153                    } # INSCOPE
6154                      unless (defined $i) {
6155                        !!!cp ('t249');
6156                        !!!parse-error (type => 'unmatched end tag',
6157                                        text => $token->{tag_name}, token => $token);
6158                        ## Ignore the token
6159                        !!!nack ('t249.1');
6160                        !!!next-token;
6161                        next B;
6162                      }
6163                    
6164                    ## As if </tr>
6165                    ## have an element in table scope
6166                    my $i;
6167                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6168                      my $node = $self->{open_elements}->[$_];
6169                      if ($node->[1] & TABLE_ROW_EL) {
6170                        !!!cp ('t250');
6171                        $i = $_;
6172                        last INSCOPE;
6173                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6174                        !!!cp ('t251');
6175                        last INSCOPE;
6176                      }
6177                    } # INSCOPE
6178                      unless (defined $i) {
6179                        !!!cp ('t252');
6180                        !!!parse-error (type => 'unmatched end tag',
6181                                        text => 'tr', token => $token);
6182                        ## Ignore the token
6183                        !!!nack ('t252.1');
6184                        !!!next-token;
6185                        next B;
6186                      }
6187                    
6188                    ## Clear back to table row context
6189                    while (not ($self->{open_elements}->[-1]->[1]
6190                                    & TABLE_ROW_SCOPING_EL)) {
6191                      !!!cp ('t253');
6192    ## ISSUE: Can this case be reached?
6193                      pop @{$self->{open_elements}};
6194                    }
6195                    
6196                    pop @{$self->{open_elements}}; # tr
6197                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
6198                    ## reprocess in the "in table body" insertion mode...
6199                }                }
6200    
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </tr>  
6201                ## have an element in table scope                ## have an element in table scope
6202                my $i;                my $i;
6203                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6204                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6205                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6206                      !!!cp ('t254');
6207                    $i = $_;                    $i = $_;
6208                    last INSCOPE;                    last INSCOPE;
6209                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6210                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6211                    last INSCOPE;                    last INSCOPE;
6212                  }                  }
6213                } # INSCOPE                } # INSCOPE
6214                unless (defined $i) {                unless (defined $i) {
6215                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t256');
6216                    !!!parse-error (type => 'unmatched end tag',
6217                                    text => $token->{tag_name}, token => $token);
6218                  ## Ignore the token                  ## Ignore the token
6219                    !!!nack ('t256.1');
6220                  !!!next-token;                  !!!next-token;
6221                  redo B;                  next B;
6222                }                }
6223    
6224                ## Clear back to table row context                ## Clear back to table body context
6225                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6226                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6227                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6228                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6229                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6230                }                }
6231    
6232                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6233                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6234                ## reprocess                !!!nack ('t257.1');
6235                redo B;                !!!next-token;
6236                  next B;
6237              } elsif ({              } elsif ({
6238                        tbody => 1, tfoot => 1, thead => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6239                          html => 1, td => 1, th => 1,
6240                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6241                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6242                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6243                ## have an element in table scope            !!!cp ('t258');
6244                my $i;            !!!parse-error (type => 'unmatched end tag',
6245                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
6246                  my $node = $self->{open_elements}->[$_];            ## Ignore the token
6247                  if ($node->[1] eq $token->{tag_name}) {            !!!nack ('t258.1');
6248                    $i = $_;             !!!next-token;
6249                    last INSCOPE;            next B;
6250                  } elsif ({          } else {
6251                            table => 1, html => 1,            !!!cp ('t259');
6252                           }->{$node->[1]}) {            !!!parse-error (type => 'in table:/',
6253                    last INSCOPE;                            text => $token->{tag_name}, token => $token);
6254                  }  
6255                } # INSCOPE            $insert = $insert_to_foster;
6256                unless (defined $i) {            #
6257                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          }
6258                  ## Ignore the token        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6259            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6260                    @{$self->{open_elements}} == 1) { # redundant, maybe
6261              !!!parse-error (type => 'in body:#eof', token => $token);
6262              !!!cp ('t259.1');
6263              #
6264            } else {
6265              !!!cp ('t259.2');
6266              #
6267            }
6268    
6269            ## Stop parsing
6270            last B;
6271          } else {
6272            die "$0: $token->{type}: Unknown token type";
6273          }
6274        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6275              if ($token->{type} == CHARACTER_TOKEN) {
6276                if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6277                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6278                  unless (length $token->{data}) {
6279                    !!!cp ('t260');
6280                  !!!next-token;                  !!!next-token;
6281                  redo B;                  next B;
6282                }                }
6283                }
6284                ## As if </tr>              
6285                ## have an element in table scope              !!!cp ('t261');
6286                my $i;              #
6287                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6288                  my $node = $self->{open_elements}->[$_];              if ($token->{tag_name} eq 'col') {
6289                  if ($node->[1] eq 'tr') {                !!!cp ('t262');
6290                    $i = $_;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6291                    last INSCOPE;                pop @{$self->{open_elements}};
6292                  } elsif ({                !!!ack ('t262.1');
6293                            table => 1, html => 1,                !!!next-token;
6294                           }->{$node->[1]}) {                next B;
6295                    last INSCOPE;              } else {
6296                  }                !!!cp ('t263');
6297                } # INSCOPE                #
6298                unless (defined $i) {              }
6299                  !!!parse-error (type => 'unmatched end tag:tr');            } elsif ($token->{type} == END_TAG_TOKEN) {
6300                if ($token->{tag_name} eq 'colgroup') {
6301                  if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6302                    !!!cp ('t264');
6303                    !!!parse-error (type => 'unmatched end tag',
6304                                    text => 'colgroup', token => $token);
6305                  ## Ignore the token                  ## Ignore the token
6306                  !!!next-token;                  !!!next-token;
6307                  redo B;                  next B;
6308                }                } else {
6309                    !!!cp ('t265');
6310                ## Clear back to table row context                  pop @{$self->{open_elements}}; # colgroup
6311                while (not {                  $self->{insertion_mode} = IN_TABLE_IM;
6312                  tr => 1, html => 1,                  !!!next-token;
6313                }->{$self->{open_elements}->[-1]->[1]}) {                  next B;            
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
6314                }                }
6315                } elsif ($token->{tag_name} eq 'col') {
6316                pop @{$self->{open_elements}}; # tr                !!!cp ('t266');
6317                $self->{insertion_mode} = 'in table body';                !!!parse-error (type => 'unmatched end tag',
6318                ## reprocess                                text => 'col', token => $token);
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1, td => 1, th => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
6319                ## Ignore the token                ## Ignore the token
6320                !!!next-token;                !!!next-token;
6321                redo B;                next B;
6322              } else {              } else {
6323                #                !!!cp ('t267');
6324                  #
6325              }              }
6326          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6327            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6328                @{$self->{open_elements}} == 1) { # redundant, maybe
6329              !!!cp ('t270.2');
6330              ## Stop parsing.
6331              last B;
6332            } else {
6333              ## NOTE: As if </colgroup>.
6334              !!!cp ('t270.1');
6335              pop @{$self->{open_elements}}; # colgroup
6336              $self->{insertion_mode} = IN_TABLE_IM;
6337              ## Reprocess.
6338              next B;
6339            }
6340          } else {
6341            die "$0: $token->{type}: Unknown token type";
6342          }
6343    
6344              ## As if </colgroup>
6345              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6346                !!!cp ('t269');
6347    ## TODO: Wrong error type?
6348                !!!parse-error (type => 'unmatched end tag',
6349                                text => 'colgroup', token => $token);
6350                ## Ignore the token
6351                !!!nack ('t269.1');
6352                !!!next-token;
6353                next B;
6354            } else {            } else {
6355              #              !!!cp ('t270');
6356                pop @{$self->{open_elements}}; # colgroup
6357                $self->{insertion_mode} = IN_TABLE_IM;
6358                !!!ack-later;
6359                ## reprocess
6360                next B;
6361              }
6362        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6363          if ($token->{type} == CHARACTER_TOKEN) {
6364            !!!cp ('t271');
6365            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6366            !!!next-token;
6367            next B;
6368          } elsif ($token->{type} == START_TAG_TOKEN) {
6369            if ($token->{tag_name} eq 'option') {
6370              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6371                !!!cp ('t272');
6372                ## As if </option>
6373                pop @{$self->{open_elements}};
6374              } else {
6375                !!!cp ('t273');
6376            }            }
6377    
6378            ## As if in table            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6379            !!!parse-error (type => 'in table:'.$token->{tag_name});            !!!nack ('t273.1');
6380            $in_body->($insert_to_foster);            !!!next-token;
6381            redo B;            next B;
6382          } elsif ($self->{insertion_mode} eq 'in cell') {          } elsif ($token->{tag_name} eq 'optgroup') {
6383            if ($token->{type} eq 'character') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6384              ## NOTE: This is a code clone of "character in body".              !!!cp ('t274');
6385              $reconstruct_active_formatting_elements->($insert_to_current);              ## As if </option>
6386                            pop @{$self->{open_elements}};
6387              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            } else {
6388                !!!cp ('t275');
6389              }
6390    
6391              if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6392                !!!cp ('t276');
6393                ## As if </optgroup>
6394                pop @{$self->{open_elements}};
6395              } else {
6396                !!!cp ('t277');
6397              }
6398    
6399              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6400              !!!nack ('t277.1');
6401              !!!next-token;
6402              next B;
6403            } elsif ({
6404                       select => 1, input => 1, textarea => 1,
6405                     }->{$token->{tag_name}} or
6406                     ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6407                      {
6408                       caption => 1, table => 1,
6409                       tbody => 1, tfoot => 1, thead => 1,
6410                       tr => 1, td => 1, th => 1,
6411                      }->{$token->{tag_name}})) {
6412              ## TODO: The type below is not good - <select> is replaced by </select>
6413              !!!parse-error (type => 'not closed', text => 'select',
6414                              token => $token);
6415              ## NOTE: As if the token were </select> (<select> case) or
6416              ## as if there were </select> (otherwise).
6417              ## have an element in table scope
6418              my $i;
6419              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6420                my $node = $self->{open_elements}->[$_];
6421                if ($node->[1] & SELECT_EL) {
6422                  !!!cp ('t278');
6423                  $i = $_;
6424                  last INSCOPE;
6425                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6426                  !!!cp ('t279');
6427                  last INSCOPE;
6428                }
6429              } # INSCOPE
6430              unless (defined $i) {
6431                !!!cp ('t280');
6432                !!!parse-error (type => 'unmatched end tag',
6433                                text => 'select', token => $token);
6434                ## Ignore the token
6435                !!!nack ('t280.1');
6436              !!!next-token;              !!!next-token;
6437              redo B;              next B;
6438            } elsif ($token->{type} eq 'comment') {            }
6439              ## NOTE: This is a code clone of "comment in body".                
6440              my $comment = $self->{document}->create_comment ($token->{data});            !!!cp ('t281');
6441              $self->{open_elements}->[-1]->[0]->append_child ($comment);            splice @{$self->{open_elements}}, $i;
6442    
6443              $self->_reset_insertion_mode;
6444    
6445              if ($token->{tag_name} eq 'select') {
6446                !!!nack ('t281.2');
6447              !!!next-token;              !!!next-token;
6448              redo B;              next B;
6449            } elsif ($token->{type} eq 'start tag') {            } else {
6450              if ({              !!!cp ('t281.1');
6451                   caption => 1, col => 1, colgroup => 1,              !!!ack-later;
6452                   tbody => 1, td => 1, tfoot => 1, th => 1,              ## Reprocess the token.
6453                   thead => 1, tr => 1,              next B;
6454                  }->{$token->{tag_name}}) {            }
6455                ## have an element in table scope          } else {
6456                my $tn;            !!!cp ('t282');
6457                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            !!!parse-error (type => 'in select',
6458                  my $node = $self->{open_elements}->[$_];                            text => $token->{tag_name}, token => $token);
6459                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {            ## Ignore the token
6460                    $tn = $node->[1];            !!!nack ('t282.1');
6461                    last INSCOPE;            !!!next-token;
6462                  } elsif ({            next B;
6463                            table => 1, html => 1,          }
6464                           }->{$node->[1]}) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6465                    last INSCOPE;          if ($token->{tag_name} eq 'optgroup') {
6466                  }            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6467                } # INSCOPE                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6468                unless (defined $tn) {              !!!cp ('t283');
6469                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              ## As if </option>
6470                  ## Ignore the token              splice @{$self->{open_elements}}, -2;
6471                  !!!next-token;            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6472                  redo B;              !!!cp ('t284');
6473                }              pop @{$self->{open_elements}};
6474              } else {
6475                !!!cp ('t285');
6476                !!!parse-error (type => 'unmatched end tag',
6477                                text => $token->{tag_name}, token => $token);
6478                ## Ignore the token
6479              }
6480              !!!nack ('t285.1');
6481              !!!next-token;
6482              next B;
6483            } elsif ($token->{tag_name} eq 'option') {
6484              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6485                !!!cp ('t286');
6486                pop @{$self->{open_elements}};
6487              } else {
6488                !!!cp ('t287');
6489                !!!parse-error (type => 'unmatched end tag',
6490                                text => $token->{tag_name}, token => $token);
6491                ## Ignore the token
6492              }
6493              !!!nack ('t287.1');
6494              !!!next-token;
6495              next B;
6496            } elsif ($token->{tag_name} eq 'select') {
6497              ## have an element in table scope
6498              my $i;
6499              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6500                my $node = $self->{open_elements}->[$_];
6501                if ($node->[1] & SELECT_EL) {
6502                  !!!cp ('t288');
6503                  $i = $_;
6504                  last INSCOPE;
6505                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6506                  !!!cp ('t289');
6507                  last INSCOPE;
6508                }
6509              } # INSCOPE
6510              unless (defined $i) {
6511                !!!cp ('t290');
6512                !!!parse-error (type => 'unmatched end tag',
6513                                text => $token->{tag_name}, token => $token);
6514                ## Ignore the token
6515                !!!nack ('t290.1');
6516                !!!next-token;
6517                next B;
6518              }
6519                  
6520              !!!cp ('t291');
6521              splice @{$self->{open_elements}}, $i;
6522    
6523                ## Close the cell            $self->_reset_insertion_mode;
6524                !!!back-token; # <?>  
6525                $token = {type => 'end tag', tag_name => $tn};            !!!nack ('t291.1');
6526                redo B;            !!!next-token;
6527              } else {            next B;
6528                #          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6529                     {
6530                      caption => 1, table => 1, tbody => 1,
6531                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6532                     }->{$token->{tag_name}}) {
6533    ## TODO: The following is wrong?
6534              !!!parse-error (type => 'unmatched end tag',
6535                              text => $token->{tag_name}, token => $token);
6536                  
6537              ## have an element in table scope
6538              my $i;
6539              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6540                my $node = $self->{open_elements}->[$_];
6541                if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6542                  !!!cp ('t292');
6543                  $i = $_;
6544                  last INSCOPE;
6545                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6546                  !!!cp ('t293');
6547                  last INSCOPE;
6548              }              }
6549            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6550              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {            unless (defined $i) {
6551                ## have an element in table scope              !!!cp ('t294');
6552                my $i;              ## Ignore the token
6553                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!nack ('t294.1');
6554                  my $node = $self->{open_elements}->[$_];              !!!next-token;
6555                  if ($node->[1] eq $token->{tag_name}) {              next B;
6556                    $i = $_;            }
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6557                                
6558                ## generate implied end tags            ## As if </select>
6559                if ({            ## have an element in table scope
6560                     dd => 1, dt => 1, li => 1, p => 1,            undef $i;
6561                     td => ($token->{tag_name} eq 'th'),            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6562                     th => ($token->{tag_name} eq 'td'),              my $node = $self->{open_elements}->[$_];
6563                     tr => 1,              if ($node->[1] & SELECT_EL) {
6564                    }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t295');
6565                  !!!back-token;                $i = $_;
6566                  $token = {type => 'end tag',                last INSCOPE;
6567                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6568                  redo B;  ## ISSUE: Can this state be reached?
6569                }                !!!cp ('t296');
6570                  last INSCOPE;
6571                }
6572              } # INSCOPE
6573              unless (defined $i) {
6574                !!!cp ('t297');
6575    ## TODO: The following error type is correct?
6576                !!!parse-error (type => 'unmatched end tag',
6577                                text => 'select', token => $token);
6578                ## Ignore the </select> token
6579                !!!nack ('t297.1');
6580                !!!next-token; ## TODO: ok?
6581                next B;
6582              }
6583                  
6584              !!!cp ('t298');
6585              splice @{$self->{open_elements}}, $i;
6586    
6587                if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            $self->_reset_insertion_mode;
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
6588    
6589                splice @{$self->{open_elements}}, $i;            !!!ack-later;
6590              ## reprocess
6591              next B;
6592            } else {
6593              !!!cp ('t299');
6594              !!!parse-error (type => 'in select:/',
6595                              text => $token->{tag_name}, token => $token);
6596              ## Ignore the token
6597              !!!nack ('t299.3');
6598              !!!next-token;
6599              next B;
6600            }
6601          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6602            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6603                    @{$self->{open_elements}} == 1) { # redundant, maybe
6604              !!!cp ('t299.1');
6605              !!!parse-error (type => 'in body:#eof', token => $token);
6606            } else {
6607              !!!cp ('t299.2');
6608            }
6609    
6610                $clear_up_to_marker->();          ## Stop parsing.
6611            last B;
6612          } else {
6613            die "$0: $token->{type}: Unknown token type";
6614          }
6615        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6616          if ($token->{type} == CHARACTER_TOKEN) {
6617            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6618              my $data = $1;
6619              ## As if in body
6620              $reconstruct_active_formatting_elements->($insert_to_current);
6621                  
6622              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6623              
6624              unless (length $token->{data}) {
6625                !!!cp ('t300');
6626                !!!next-token;
6627                next B;
6628              }
6629            }
6630            
6631            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6632              !!!cp ('t301');
6633              !!!parse-error (type => 'after html:#text', token => $token);
6634              #
6635            } else {
6636              !!!cp ('t302');
6637              ## "after body" insertion mode
6638              !!!parse-error (type => 'after body:#text', token => $token);
6639              #
6640            }
6641    
6642                $self->{insertion_mode} = 'in row';          $self->{insertion_mode} = IN_BODY_IM;
6643            ## reprocess
6644            next B;
6645          } elsif ($token->{type} == START_TAG_TOKEN) {
6646            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6647              !!!cp ('t303');
6648              !!!parse-error (type => 'after html',
6649                              text => $token->{tag_name}, token => $token);
6650              #
6651            } else {
6652              !!!cp ('t304');
6653              ## "after body" insertion mode
6654              !!!parse-error (type => 'after body',
6655                              text => $token->{tag_name}, token => $token);
6656              #
6657            }
6658    
6659                !!!next-token;          $self->{insertion_mode} = IN_BODY_IM;
6660                redo B;          !!!ack-later;
6661              } elsif ({          ## reprocess
6662                        body => 1, caption => 1, col => 1,          next B;
6663                        colgroup => 1, html => 1,        } elsif ($token->{type} == END_TAG_TOKEN) {
6664                       }->{$token->{tag_name}}) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6665                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t305');
6666                ## Ignore the token            !!!parse-error (type => 'after html:/',
6667                !!!next-token;                            text => $token->{tag_name}, token => $token);
6668                redo B;            
6669              } elsif ({            $self->{insertion_mode} = IN_BODY_IM;
6670                        table => 1, tbody => 1, tfoot => 1,            ## Reprocess.
6671                        thead => 1, tr => 1,            next B;
6672                       }->{$token->{tag_name}}) {          } else {
6673                ## have an element in table scope            !!!cp ('t306');
6674                my $i;          }
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   ## NOTE: There is exactly one |td| or |th| element  
                   ## in scope in the stack of open elements by definition.  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6675    
6676                ## Close the cell          ## "after body" insertion mode
6677                !!!back-token; # </?>          if ($token->{tag_name} eq 'html') {
6678                $token = {type => 'end tag', tag_name => $tn};            if (defined $self->{inner_html_node}) {
6679                redo B;              !!!cp ('t307');
6680              } else {              !!!parse-error (type => 'unmatched end tag',
6681                #                              text => 'html', token => $token);
6682              }              ## Ignore the token
6683                !!!next-token;
6684                next B;
6685            } else {            } else {
6686              #              !!!cp ('t308');
6687                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6688                !!!next-token;
6689                next B;
6690            }            }
6691            } else {
6692              !!!cp ('t309');
6693              !!!parse-error (type => 'after body:/',
6694                              text => $token->{tag_name}, token => $token);
6695    
6696              $self->{insertion_mode} = IN_BODY_IM;
6697              ## reprocess
6698              next B;
6699            }
6700          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6701            !!!cp ('t309.2');
6702            ## Stop parsing
6703            last B;
6704          } else {
6705            die "$0: $token->{type}: Unknown token type";
6706          }
6707        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6708          if ($token->{type} == CHARACTER_TOKEN) {
6709            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6710              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6711                        
6712            $in_body->($insert_to_current);            unless (length $token->{data}) {
6713            redo B;              !!!cp ('t310');
         } elsif ($self->{insertion_mode} eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
6714              !!!next-token;              !!!next-token;
6715              redo B;              next B;
6716            } elsif ($token->{type} eq 'comment') {            }
6717              my $comment = $self->{document}->create_comment ($token->{data});          }
6718              $self->{open_elements}->[-1]->[0]->append_child ($comment);          
6719            if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6720              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6721                !!!cp ('t311');
6722                !!!parse-error (type => 'in frameset:#text', token => $token);
6723              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6724                !!!cp ('t312');
6725                !!!parse-error (type => 'after frameset:#text', token => $token);
6726              } else { # "after after frameset"
6727                !!!cp ('t313');
6728                !!!parse-error (type => 'after html:#text', token => $token);
6729              }
6730              
6731              ## Ignore the token.
6732              if (length $token->{data}) {
6733                !!!cp ('t314');
6734                ## reprocess the rest of characters
6735              } else {
6736                !!!cp ('t315');
6737              !!!next-token;              !!!next-token;
6738              redo B;            }
6739            } elsif ($token->{type} eq 'start tag') {            next B;
6740              if ($token->{tag_name} eq 'option') {          }
6741                if ($self->{open_elements}->[-1]->[1] eq 'option') {          
6742                  ## As if </option>          die qq[$0: Character "$token->{data}"];
6743                  pop @{$self->{open_elements}};        } elsif ($token->{type} == START_TAG_TOKEN) {
6744                }          if ($token->{tag_name} eq 'frameset' and
6745                $self->{insertion_mode} == IN_FRAMESET_IM) {
6746              !!!cp ('t318');
6747              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6748              !!!nack ('t318.1');
6749              !!!next-token;
6750              next B;
6751            } elsif ($token->{tag_name} eq 'frame' and
6752                     $self->{insertion_mode} == IN_FRAMESET_IM) {
6753              !!!cp ('t319');
6754              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6755              pop @{$self->{open_elements}};
6756              !!!ack ('t319.1');
6757              !!!next-token;
6758              next B;
6759            } elsif ($token->{tag_name} eq 'noframes') {
6760              !!!cp ('t320');
6761              ## NOTE: As if in head.
6762              $parse_rcdata->(CDATA_CONTENT_MODEL);
6763              next B;
6764    
6765              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6766              ## has no parse error.
6767            } else {
6768              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6769                !!!cp ('t321');
6770                !!!parse-error (type => 'in frameset',
6771                                text => $token->{tag_name}, token => $token);
6772              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6773                !!!cp ('t322');
6774                !!!parse-error (type => 'after frameset',
6775                                text => $token->{tag_name}, token => $token);
6776              } else { # "after after frameset"
6777                !!!cp ('t322.2');
6778                !!!parse-error (type => 'after after frameset',
6779                                text => $token->{tag_name}, token => $token);
6780              }
6781              ## Ignore the token
6782              !!!nack ('t322.1');
6783              !!!next-token;
6784              next B;
6785            }
6786          } elsif ($token->{type} == END_TAG_TOKEN) {
6787            if ($token->{tag_name} eq 'frameset' and
6788                $self->{insertion_mode} == IN_FRAMESET_IM) {
6789              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6790                  @{$self->{open_elements}} == 1) {
6791                !!!cp ('t325');
6792                !!!parse-error (type => 'unmatched end tag',
6793                                text => $token->{tag_name}, token => $token);
6794                ## Ignore the token
6795                !!!next-token;
6796              } else {
6797                !!!cp ('t326');
6798                pop @{$self->{open_elements}};
6799                !!!next-token;
6800              }
6801    
6802                !!!insert-element ($token->{tag_name}, $token->{attributes});            if (not defined $self->{inner_html_node} and
6803                !!!next-token;                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6804                redo B;              !!!cp ('t327');
6805              } elsif ($token->{tag_name} eq 'optgroup') {              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6806                if ($self->{open_elements}->[-1]->[1] eq 'option') {            } else {
6807                  ## As if </option>              !!!cp ('t328');
6808                  pop @{$self->{open_elements}};            }
6809                }            next B;
6810            } elsif ($token->{tag_name} eq 'html' and
6811                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6812              !!!cp ('t329');
6813              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6814              !!!next-token;
6815              next B;
6816            } else {
6817              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6818                !!!cp ('t330');
6819                !!!parse-error (type => 'in frameset:/',
6820                                text => $token->{tag_name}, token => $token);
6821              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6822                !!!cp ('t330.1');
6823                !!!parse-error (type => 'after frameset:/',
6824                                text => $token->{tag_name}, token => $token);
6825              } else { # "after after html"
6826                !!!cp ('t331');
6827                !!!parse-error (type => 'after after frameset:/',
6828                                text => $token->{tag_name}, token => $token);
6829              }
6830              ## Ignore the token
6831              !!!next-token;
6832              next B;
6833            }
6834          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6835            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6836                    @{$self->{open_elements}} == 1) { # redundant, maybe
6837              !!!cp ('t331.1');
6838              !!!parse-error (type => 'in body:#eof', token => $token);
6839            } else {
6840              !!!cp ('t331.2');
6841            }
6842            
6843            ## Stop parsing
6844            last B;
6845          } else {
6846            die "$0: $token->{type}: Unknown token type";
6847          }
6848        } else {
6849          die "$0: $self->{insertion_mode}: Unknown insertion mode";
6850        }
6851    
6852                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {      ## "in body" insertion mode
6853                  ## As if </optgroup>      if ($token->{type} == START_TAG_TOKEN) {
6854                  pop @{$self->{open_elements}};        if ($token->{tag_name} eq 'script') {
6855                }          !!!cp ('t332');
6856            ## NOTE: This is an "as if in head" code clone
6857            $script_start_tag->();
6858            next B;
6859          } elsif ($token->{tag_name} eq 'style') {
6860            !!!cp ('t333');
6861            ## NOTE: This is an "as if in head" code clone
6862            $parse_rcdata->(CDATA_CONTENT_MODEL);
6863            next B;
6864          } elsif ({
6865                    base => 1, command => 1, eventsource => 1, link => 1,
6866                   }->{$token->{tag_name}}) {
6867            !!!cp ('t334');
6868            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6869            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6870            pop @{$self->{open_elements}};
6871            !!!ack ('t334.1');
6872            !!!next-token;
6873            next B;
6874          } elsif ($token->{tag_name} eq 'meta') {
6875            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6876            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6877            my $meta_el = pop @{$self->{open_elements}};
6878    
6879            unless ($self->{confident}) {
6880              if ($token->{attributes}->{charset}) {
6881                !!!cp ('t335');
6882                ## NOTE: Whether the encoding is supported or not is handled
6883                ## in the {change_encoding} callback.
6884                $self->{change_encoding}
6885                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6886                
6887                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6888                    ->set_user_data (manakai_has_reference =>
6889                                         $token->{attributes}->{charset}
6890                                             ->{has_reference});
6891              } elsif ($token->{attributes}->{content}) {
6892                if ($token->{attributes}->{content}->{value}
6893                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6894                        [\x09\x0A\x0C\x0D\x20]*=
6895                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6896                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6897                       /x) {
6898                  !!!cp ('t336');
6899                  ## NOTE: Whether the encoding is supported or not is handled
6900                  ## in the {change_encoding} callback.
6901                  $self->{change_encoding}
6902                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6903                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6904                      ->set_user_data (manakai_has_reference =>
6905                                           $token->{attributes}->{content}
6906                                                 ->{has_reference});
6907                }
6908              }
6909            } else {
6910              if ($token->{attributes}->{charset}) {
6911                !!!cp ('t337');
6912                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6913                    ->set_user_data (manakai_has_reference =>
6914                                         $token->{attributes}->{charset}
6915                                             ->{has_reference});
6916              }
6917              if ($token->{attributes}->{content}) {
6918                !!!cp ('t338');
6919                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6920                    ->set_user_data (manakai_has_reference =>
6921                                         $token->{attributes}->{content}
6922                                             ->{has_reference});
6923              }
6924            }
6925    
6926                !!!insert-element ($token->{tag_name}, $token->{attributes});          !!!ack ('t338.1');
6927                !!!next-token;          !!!next-token;
6928                redo B;          next B;
6929              } elsif ($token->{tag_name} eq 'select') {        } elsif ($token->{tag_name} eq 'title') {
6930                !!!parse-error (type => 'not closed:select');          !!!cp ('t341');
6931                ## As if </select> instead          ## NOTE: This is an "as if in head" code clone
6932                ## have an element in table scope          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6933                my $i;          next B;
6934                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {        } elsif ($token->{tag_name} eq 'body') {
6935                  my $node = $self->{open_elements}->[$_];          !!!parse-error (type => 'in body', text => 'body', token => $token);
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:select');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6936                                
6937                splice @{$self->{open_elements}}, $i;          if (@{$self->{open_elements}} == 1 or
6938                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6939              !!!cp ('t342');
6940              ## Ignore the token
6941            } else {
6942              my $body_el = $self->{open_elements}->[1]->[0];
6943              for my $attr_name (keys %{$token->{attributes}}) {
6944                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6945                  !!!cp ('t343');
6946                  $body_el->set_attribute_ns
6947                    (undef, [undef, $attr_name],
6948                     $token->{attributes}->{$attr_name}->{value});
6949                }
6950              }
6951            }
6952            !!!nack ('t343.1');
6953            !!!next-token;
6954            next B;
6955          } elsif ({
6956                    ## NOTE: Start tags for non-phrasing flow content elements
6957    
6958                $self->_reset_insertion_mode;                  ## NOTE: The normal one
6959                    address => 1, article => 1, aside => 1, blockquote => 1,
6960                    center => 1, datagrid => 1, details => 1, dialog => 1,
6961                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6962                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6963                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6964                    section => 1, ul => 1,
6965                    ## NOTE: As normal, but drops leading newline
6966                    pre => 1, listing => 1,
6967                    ## NOTE: As normal, but interacts with the form element pointer
6968                    form => 1,
6969                    
6970                    table => 1,
6971                    hr => 1,
6972                   }->{$token->{tag_name}}) {
6973            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6974              !!!cp ('t350');
6975              !!!parse-error (type => 'in form:form', token => $token);
6976              ## Ignore the token
6977              !!!nack ('t350.1');
6978              !!!next-token;
6979              next B;
6980            }
6981    
6982            ## has a p element in scope
6983            INSCOPE: for (reverse @{$self->{open_elements}}) {
6984              if ($_->[1] & P_EL) {
6985                !!!cp ('t344');
6986                !!!back-token; # <form>
6987                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6988                          line => $token->{line}, column => $token->{column}};
6989                next B;
6990              } elsif ($_->[1] & SCOPING_EL) {
6991                !!!cp ('t345');
6992                last INSCOPE;
6993              }
6994            } # INSCOPE
6995              
6996            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6997            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6998              !!!nack ('t346.1');
6999              !!!next-token;
7000              if ($token->{type} == CHARACTER_TOKEN) {
7001                $token->{data} =~ s/^\x0A//;
7002                unless (length $token->{data}) {
7003                  !!!cp ('t346');
7004                !!!next-token;                !!!next-token;
               redo B;  
7005              } else {              } else {
7006                #                !!!cp ('t349');
7007              }              }
7008            } elsif ($token->{type} eq 'end tag') {            } else {
7009              if ($token->{tag_name} eq 'optgroup') {              !!!cp ('t348');
7010                if ($self->{open_elements}->[-1]->[1] eq 'option' and            }
7011                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {          } elsif ($token->{tag_name} eq 'form') {
7012                  ## As if </option>            !!!cp ('t347.1');
7013                  splice @{$self->{open_elements}}, -2;            $self->{form_element} = $self->{open_elements}->[-1]->[0];
7014                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {  
7015                  pop @{$self->{open_elements}};            !!!nack ('t347.2');
7016              !!!next-token;
7017            } elsif ($token->{tag_name} eq 'table') {
7018              !!!cp ('t382');
7019              push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7020              
7021              $self->{insertion_mode} = IN_TABLE_IM;
7022    
7023              !!!nack ('t382.1');
7024              !!!next-token;
7025            } elsif ($token->{tag_name} eq 'hr') {
7026              !!!cp ('t386');
7027              pop @{$self->{open_elements}};
7028            
7029              !!!nack ('t386.1');
7030              !!!next-token;
7031            } else {
7032              !!!nack ('t347.1');
7033              !!!next-token;
7034            }
7035            next B;
7036          } elsif ($token->{tag_name} eq 'li') {
7037            ## NOTE: As normal, but imply </li> when there's another <li> ...
7038    
7039            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7040              ## Interpreted as <li><foo/></li><li/> (non-conforming)
7041              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7042              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7043              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7044              ## object (Fx)
7045              ## Generate non-tree (non-conforming)
7046              ## basefont (IE7 (where basefont is non-void)), center (IE),
7047              ## form (IE), hn (IE)
7048            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7049              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7050              ## div (Fx, S)
7051    
7052            my $non_optional;
7053            my $i = -1;
7054    
7055            ## 1.
7056            for my $node (reverse @{$self->{open_elements}}) {
7057              if ($node->[1] & LI_EL) {
7058                ## 2. (a) As if </li>
7059                {
7060                  ## If no </li> - not applied
7061                  #
7062    
7063                  ## Otherwise
7064    
7065                  ## 1. generate implied end tags, except for </li>
7066                  #
7067    
7068                  ## 2. If current node != "li", parse error
7069                  if ($non_optional) {
7070                    !!!parse-error (type => 'not closed',
7071                                    text => $non_optional->[0]->manakai_local_name,
7072                                    token => $token);
7073                    !!!cp ('t355');
7074                } else {                } else {
7075                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t356');
                 ## Ignore the token  
7076                }                }
7077                !!!next-token;  
7078                redo B;                ## 3. Pop
7079              } elsif ($token->{tag_name} eq 'option') {                splice @{$self->{open_elements}}, $i;
7080                if ($self->{open_elements}->[-1]->[1] eq 'option') {              }
7081                  pop @{$self->{open_elements}};  
7082                last; ## 2. (b) goto 5.
7083              } elsif (
7084                       ## NOTE: not "formatting" and not "phrasing"
7085                       ($node->[1] & SPECIAL_EL or
7086                        $node->[1] & SCOPING_EL) and
7087                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7088    
7089                       (not $node->[1] & ADDRESS_EL) &
7090                       (not $node->[1] & DIV_EL) &
7091                       (not $node->[1] & P_EL)) {
7092                ## 3.
7093                !!!cp ('t357');
7094                last; ## goto 5.
7095              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7096                !!!cp ('t358');
7097                #
7098              } else {
7099                !!!cp ('t359');
7100                $non_optional ||= $node;
7101                #
7102              }
7103              ## 4.
7104              ## goto 2.
7105              $i--;
7106            }
7107    
7108            ## 5. (a) has a |p| element in scope
7109            INSCOPE: for (reverse @{$self->{open_elements}}) {
7110              if ($_->[1] & P_EL) {
7111                !!!cp ('t353');
7112    
7113                ## NOTE: |<p><li>|, for example.
7114    
7115                !!!back-token; # <x>
7116                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7117                          line => $token->{line}, column => $token->{column}};
7118                next B;
7119              } elsif ($_->[1] & SCOPING_EL) {
7120                !!!cp ('t354');
7121                last INSCOPE;
7122              }
7123            } # INSCOPE
7124    
7125            ## 5. (b) insert
7126            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7127            !!!nack ('t359.1');
7128            !!!next-token;
7129            next B;
7130          } elsif ($token->{tag_name} eq 'dt' or
7131                   $token->{tag_name} eq 'dd') {
7132            ## NOTE: As normal, but imply </dt> or </dd> when ...
7133    
7134            my $non_optional;
7135            my $i = -1;
7136    
7137            ## 1.
7138            for my $node (reverse @{$self->{open_elements}}) {
7139              if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7140                ## 2. (a) As if </li>
7141                {
7142                  ## If no </li> - not applied
7143                  #
7144    
7145                  ## Otherwise
7146    
7147                  ## 1. generate implied end tags, except for </dt> or </dd>
7148                  #
7149    
7150                  ## 2. If current node != "dt"|"dd", parse error
7151                  if ($non_optional) {
7152                    !!!parse-error (type => 'not closed',
7153                                    text => $non_optional->[0]->manakai_local_name,
7154                                    token => $token);
7155                    !!!cp ('t355.1');
7156                } else {                } else {
7157                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t356.1');
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
7158                }                }
7159                  
7160                  ## 3. Pop
7161                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
7162                }
7163    
7164                $self->_reset_insertion_mode;              last; ## 2. (b) goto 5.
7165              } elsif (
7166                       ## NOTE: not "formatting" and not "phrasing"
7167                       ($node->[1] & SPECIAL_EL or
7168                        $node->[1] & SCOPING_EL) and
7169                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7170    
7171                       (not $node->[1] & ADDRESS_EL) &
7172                       (not $node->[1] & DIV_EL) &
7173                       (not $node->[1] & P_EL)) {
7174                ## 3.
7175                !!!cp ('t357.1');
7176                last; ## goto 5.
7177              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7178                !!!cp ('t358.1');
7179                #
7180              } else {
7181                !!!cp ('t359.1');
7182                $non_optional ||= $node;
7183                #
7184              }
7185              ## 4.
7186              ## goto 2.
7187              $i--;
7188            }
7189    
7190                !!!next-token;          ## 5. (a) has a |p| element in scope
7191                redo B;          INSCOPE: for (reverse @{$self->{open_elements}}) {
7192              } elsif ({            if ($_->[1] & P_EL) {
7193                        caption => 1, table => 1, tbody => 1,              !!!cp ('t353.1');
7194                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,              !!!back-token; # <x>
7195                       }->{$token->{tag_name}}) {              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7196                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                        line => $token->{line}, column => $token->{column}};
7197                              next B;
7198                ## have an element in table scope            } elsif ($_->[1] & SCOPING_EL) {
7199                my $i;              !!!cp ('t354.1');
7200                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              last INSCOPE;
7201                  my $node = $self->{open_elements}->[$_];            }
7202                  if ($node->[1] eq $token->{tag_name}) {          } # INSCOPE
7203                    $i = $_;  
7204                    last INSCOPE;          ## 5. (b) insert
7205                  } elsif ({          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7206                            table => 1, html => 1,          !!!nack ('t359.2');
7207                           }->{$node->[1]}) {          !!!next-token;
7208                    last INSCOPE;          next B;
7209                  }        } elsif ($token->{tag_name} eq 'plaintext') {
7210                } # INSCOPE          ## NOTE: As normal, but effectively ends parsing
7211                unless (defined $i) {  
7212                  ## Ignore the token          ## has a p element in scope
7213                  !!!next-token;          INSCOPE: for (reverse @{$self->{open_elements}}) {
7214                  redo B;            if ($_->[1] & P_EL) {
7215                !!!cp ('t367');
7216                !!!back-token; # <plaintext>
7217                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7218                          line => $token->{line}, column => $token->{column}};
7219                next B;
7220              } elsif ($_->[1] & SCOPING_EL) {
7221                !!!cp ('t368');
7222                last INSCOPE;
7223              }
7224            } # INSCOPE
7225              
7226            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7227              
7228            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7229              
7230            !!!nack ('t368.1');
7231            !!!next-token;
7232            next B;
7233          } elsif ($token->{tag_name} eq 'a') {
7234            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7235              my $node = $active_formatting_elements->[$i];
7236              if ($node->[1] & A_EL) {
7237                !!!cp ('t371');
7238                !!!parse-error (type => 'in a:a', token => $token);
7239                
7240                !!!back-token; # <a>
7241                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7242                          line => $token->{line}, column => $token->{column}};
7243                $formatting_end_tag->($token);
7244                
7245                AFE2: for (reverse 0..$#$active_formatting_elements) {
7246                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7247                    !!!cp ('t372');
7248                    splice @$active_formatting_elements, $_, 1;
7249                    last AFE2;
7250                }                }
7251                              } # AFE2
7252                ## As if </select>              OE: for (reverse 0..$#{$self->{open_elements}}) {
7253                ## have an element in table scope                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7254                undef $i;                  !!!cp ('t373');
7255                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  splice @{$self->{open_elements}}, $_, 1;
7256                  my $node = $self->{open_elements}->[$_];                  last OE;
                 if ($node->[1] eq 'select') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:select');  
                 ## Ignore the </select> token  
                 !!!next-token; ## TODO: ok?  
                 redo B;  
7257                }                }
7258                              } # OE
7259                splice @{$self->{open_elements}}, $i;              last AFE;
7260              } elsif ($node->[0] eq '#marker') {
7261                !!!cp ('t374');
7262                last AFE;
7263              }
7264            } # AFE
7265              
7266            $reconstruct_active_formatting_elements->($insert_to_current);
7267    
7268                $self->_reset_insertion_mode;          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7269            push @$active_formatting_elements, $self->{open_elements}->[-1];
7270    
7271                ## reprocess          !!!nack ('t374.1');
7272                redo B;          !!!next-token;
7273              } else {          next B;
7274                #        } elsif ($token->{tag_name} eq 'nobr') {
7275              }          $reconstruct_active_formatting_elements->($insert_to_current);
7276            } else {  
7277              #          ## has a |nobr| element in scope
7278            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7279              my $node = $self->{open_elements}->[$_];
7280              if ($node->[1] & NOBR_EL) {
7281                !!!cp ('t376');
7282                !!!parse-error (type => 'in nobr:nobr', token => $token);
7283                !!!back-token; # <nobr>
7284                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7285                          line => $token->{line}, column => $token->{column}};
7286                next B;
7287              } elsif ($node->[1] & SCOPING_EL) {
7288                !!!cp ('t377');
7289                last INSCOPE;
7290              }
7291            } # INSCOPE
7292            
7293            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7294            push @$active_formatting_elements, $self->{open_elements}->[-1];
7295            
7296            !!!nack ('t377.1');
7297            !!!next-token;
7298            next B;
7299          } elsif ($token->{tag_name} eq 'button') {
7300            ## has a button element in scope
7301            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7302              my $node = $self->{open_elements}->[$_];
7303              if ($node->[1] & BUTTON_EL) {
7304                !!!cp ('t378');
7305                !!!parse-error (type => 'in button:button', token => $token);
7306                !!!back-token; # <button>
7307                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7308                          line => $token->{line}, column => $token->{column}};
7309                next B;
7310              } elsif ($node->[1] & SCOPING_EL) {
7311                !!!cp ('t379');
7312                last INSCOPE;
7313            }            }
7314            } # INSCOPE
7315              
7316            $reconstruct_active_formatting_elements->($insert_to_current);
7317              
7318            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7319    
7320            ## TODO: associate with $self->{form_element} if defined
7321    
7322            push @$active_formatting_elements, ['#marker', ''];
7323    
7324            !!!parse-error (type => 'in select:'.$token->{tag_name});          !!!nack ('t379.1');
7325            !!!next-token;
7326            next B;
7327          } elsif ({
7328                    xmp => 1,
7329                    iframe => 1,
7330                    noembed => 1,
7331                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7332                    noscript => 0, ## TODO: 1 if scripting is enabled
7333                   }->{$token->{tag_name}}) {
7334            if ($token->{tag_name} eq 'xmp') {
7335              !!!cp ('t381');
7336              $reconstruct_active_formatting_elements->($insert_to_current);
7337            } else {
7338              !!!cp ('t399');
7339            }
7340            ## NOTE: There is an "as if in body" code clone.
7341            $parse_rcdata->(CDATA_CONTENT_MODEL);
7342            next B;
7343          } elsif ($token->{tag_name} eq 'isindex') {
7344            !!!parse-error (type => 'isindex', token => $token);
7345            
7346            if (defined $self->{form_element}) {
7347              !!!cp ('t389');
7348            ## Ignore the token            ## Ignore the token
7349              !!!nack ('t389'); ## NOTE: Not acknowledged.
7350            !!!next-token;            !!!next-token;
7351            redo B;            next B;
7352          } elsif ($self->{insertion_mode} eq 'after body') {          } else {
7353            if ($token->{type} eq 'character') {            !!!ack ('t391.1');
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               ## As if in body  
               $reconstruct_active_formatting_elements->($insert_to_current);  
                 
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
7354    
7355                unless (length $token->{data}) {            my $at = $token->{attributes};
7356                  !!!next-token;            my $form_attrs;
7357                  redo B;            $form_attrs->{action} = $at->{action} if $at->{action};
7358                }            my $prompt_attr = $at->{prompt};
7359              }            $at->{name} = {name => 'name', value => 'isindex'};
7360                          delete $at->{action};
7361              #            delete $at->{prompt};
7362              !!!parse-error (type => 'after body:#'.$token->{type});            my @tokens = (
7363            } elsif ($token->{type} eq 'comment') {                          {type => START_TAG_TOKEN, tag_name => 'form',
7364              my $comment = $self->{document}->create_comment ($token->{data});                           attributes => $form_attrs,
7365              $self->{open_elements}->[0]->[0]->append_child ($comment);                           line => $token->{line}, column => $token->{column}},
7366                            {type => START_TAG_TOKEN, tag_name => 'hr',
7367                             line => $token->{line}, column => $token->{column}},
7368                            {type => START_TAG_TOKEN, tag_name => 'p',
7369                             line => $token->{line}, column => $token->{column}},
7370                            {type => START_TAG_TOKEN, tag_name => 'label',
7371                             line => $token->{line}, column => $token->{column}},
7372                           );
7373              if ($prompt_attr) {
7374                !!!cp ('t390');
7375                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7376                               #line => $token->{line}, column => $token->{column},
7377                              };
7378              } else {
7379                !!!cp ('t391');
7380                push @tokens, {type => CHARACTER_TOKEN,
7381                               data => 'This is a searchable index. Insert your search keywords here: ',
7382                               #line => $token->{line}, column => $token->{column},
7383                              }; # SHOULD
7384                ## TODO: make this configurable
7385              }
7386              push @tokens,
7387                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7388                             line => $token->{line}, column => $token->{column}},
7389                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7390                            {type => END_TAG_TOKEN, tag_name => 'label',
7391                             line => $token->{line}, column => $token->{column}},
7392                            {type => END_TAG_TOKEN, tag_name => 'p',
7393                             line => $token->{line}, column => $token->{column}},
7394                            {type => START_TAG_TOKEN, tag_name => 'hr',
7395                             line => $token->{line}, column => $token->{column}},
7396                            {type => END_TAG_TOKEN, tag_name => 'form',
7397                             line => $token->{line}, column => $token->{column}};
7398              !!!back-token (@tokens);
7399              !!!next-token;
7400              next B;
7401            }
7402          } elsif ($token->{tag_name} eq 'textarea') {
7403            my $tag_name = $token->{tag_name};
7404            my $el;
7405            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7406            
7407            ## TODO: $self->{form_element} if defined
7408            $self->{content_model} = RCDATA_CONTENT_MODEL;
7409            delete $self->{escape}; # MUST
7410            
7411            $insert->($el);
7412            
7413            my $text = '';
7414            !!!nack ('t392.1');
7415            !!!next-token;
7416            if ($token->{type} == CHARACTER_TOKEN) {
7417              $token->{data} =~ s/^\x0A//;
7418              unless (length $token->{data}) {
7419                !!!cp ('t392');
7420              !!!next-token;              !!!next-token;
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             !!!parse-error (type => 'after body:'.$token->{tag_name});  
             #  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               if (defined $self->{inner_html_node}) {  
                 !!!parse-error (type => 'unmatched end tag:html');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               } else {  
                 $phase = 'trailing end';  
                 !!!next-token;  
                 redo B;  
               }  
             } else {  
               !!!parse-error (type => 'after body:/'.$token->{tag_name});  
             }  
7421            } else {            } else {
7422              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t393');
7423            }            }
7424            } else {
7425              !!!cp ('t394');
7426            }
7427            while ($token->{type} == CHARACTER_TOKEN) {
7428              !!!cp ('t395');
7429              $text .= $token->{data};
7430              !!!next-token;
7431            }
7432            if (length $text) {
7433              !!!cp ('t396');
7434              $el->manakai_append_text ($text);
7435            }
7436            
7437            $self->{content_model} = PCDATA_CONTENT_MODEL;
7438            
7439            if ($token->{type} == END_TAG_TOKEN and
7440                $token->{tag_name} eq $tag_name) {
7441              !!!cp ('t397');
7442              ## Ignore the token
7443            } else {
7444              !!!cp ('t398');
7445              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7446            }
7447            !!!next-token;
7448            next B;
7449          } elsif ($token->{tag_name} eq 'optgroup' or
7450                   $token->{tag_name} eq 'option') {
7451            ## has an |option| element in scope
7452            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7453              my $node = $self->{open_elements}->[$_];
7454              if ($node->[1] & OPTION_EL) {
7455                !!!cp ('t397.1');
7456                ## NOTE: As if </option>
7457                !!!back-token; # <option> or <optgroup>
7458                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7459                          line => $token->{line}, column => $token->{column}};
7460                next B;
7461              } elsif ($node->[1] & SCOPING_EL) {
7462                !!!cp ('t397.2');
7463                last INSCOPE;
7464              }
7465            } # INSCOPE
7466    
7467            $self->{insertion_mode} = 'in body';          $reconstruct_active_formatting_elements->($insert_to_current);
           ## reprocess  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in frameset') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
7468    
7469                unless (length $token->{data}) {          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
                 !!!next-token;  
                 redo B;  
               }  
             }  
7470    
7471              #          !!!nack ('t397.3');
7472            } elsif ($token->{type} eq 'comment') {          !!!next-token;
7473              my $comment = $self->{document}->create_comment ($token->{data});          redo B;
7474              $self->{open_elements}->[-1]->[0]->append_child ($comment);        } elsif ($token->{tag_name} eq 'rt' or
7475              !!!next-token;                 $token->{tag_name} eq 'rp') {
7476              redo B;          ## has a |ruby| element in scope
7477            } elsif ($token->{type} eq 'start tag') {          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7478              if ($token->{tag_name} eq 'frameset') {            my $node = $self->{open_elements}->[$_];
7479                !!!insert-element ($token->{tag_name}, $token->{attributes});            if ($node->[1] & RUBY_EL) {
7480                !!!next-token;              !!!cp ('t398.1');
7481                redo B;              ## generate implied end tags
7482              } elsif ($token->{tag_name} eq 'frame') {              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7483                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t398.2');
7484                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
7485              }              }
7486            } elsif ($token->{type} eq 'end tag') {              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7487              if ($token->{tag_name} eq 'frameset') {                !!!cp ('t398.3');
7488                if ($self->{open_elements}->[-1]->[1] eq 'html' and                !!!parse-error (type => 'not closed',
7489                    @{$self->{open_elements}} == 1) {                                text => $self->{open_elements}->[-1]->[0]
7490                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                                    ->manakai_local_name,
7491                  ## Ignore the token                                token => $token);
7492                  !!!next-token;                pop @{$self->{open_elements}}
7493                } else {                    while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
                 pop @{$self->{open_elements}};  
                 !!!next-token;  
               }  
                 
               ## if not inner_html and  
               if ($self->{open_elements}->[-1]->[1] ne 'frameset') {  
                 $self->{insertion_mode} = 'after frameset';  
               }  
               redo B;  
             } else {  
               #  
7494              }              }
7495                last INSCOPE;
7496              } elsif ($node->[1] & SCOPING_EL) {
7497                !!!cp ('t398.4');
7498                last INSCOPE;
7499              }
7500            } # INSCOPE
7501    
7502            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7503    
7504            !!!nack ('t398.5');
7505            !!!next-token;
7506            redo B;
7507          } elsif ($token->{tag_name} eq 'math' or
7508                   $token->{tag_name} eq 'svg') {
7509            $reconstruct_active_formatting_elements->($insert_to_current);
7510    
7511            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7512    
7513            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7514    
7515            ## "adjust foreign attributes" - done in insert-element-f
7516            
7517            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7518            
7519            if ($self->{self_closing}) {
7520              pop @{$self->{open_elements}};
7521              !!!ack ('t398.6');
7522            } else {
7523              !!!cp ('t398.7');
7524              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7525              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7526              ## mode, "in body" (not "in foreign content") secondary insertion
7527              ## mode, maybe.
7528            }
7529    
7530            !!!next-token;
7531            next B;
7532          } elsif ({
7533                    caption => 1, col => 1, colgroup => 1, frame => 1,
7534                    frameset => 1, head => 1,
7535                    tbody => 1, td => 1, tfoot => 1, th => 1,
7536                    thead => 1, tr => 1,
7537                   }->{$token->{tag_name}}) {
7538            !!!cp ('t401');
7539            !!!parse-error (type => 'in body',
7540                            text => $token->{tag_name}, token => $token);
7541            ## Ignore the token
7542            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7543            !!!next-token;
7544            next B;
7545          } elsif ($token->{tag_name} eq 'param' or
7546                   $token->{tag_name} eq 'source') {
7547            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7548            pop @{$self->{open_elements}};
7549    
7550            !!!ack ('t398.5');
7551            !!!next-token;
7552            redo B;
7553          } else {
7554            if ($token->{tag_name} eq 'image') {
7555              !!!cp ('t384');
7556              !!!parse-error (type => 'image', token => $token);
7557              $token->{tag_name} = 'img';
7558            } else {
7559              !!!cp ('t385');
7560            }
7561    
7562            ## NOTE: There is an "as if <br>" code clone.
7563            $reconstruct_active_formatting_elements->($insert_to_current);
7564            
7565            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7566    
7567            if ({
7568                 applet => 1, marquee => 1, object => 1,
7569                }->{$token->{tag_name}}) {
7570              !!!cp ('t380');
7571              push @$active_formatting_elements, ['#marker', ''];
7572              !!!nack ('t380.1');
7573            } elsif ({
7574                      b => 1, big => 1, em => 1, font => 1, i => 1,
7575                      s => 1, small => 1, strike => 1,
7576                      strong => 1, tt => 1, u => 1,
7577                     }->{$token->{tag_name}}) {
7578              !!!cp ('t375');
7579              push @$active_formatting_elements, $self->{open_elements}->[-1];
7580              !!!nack ('t375.1');
7581            } elsif ($token->{tag_name} eq 'input') {
7582              !!!cp ('t388');
7583              ## TODO: associate with $self->{form_element} if defined
7584              pop @{$self->{open_elements}};
7585              !!!ack ('t388.2');
7586            } elsif ({
7587                      area => 1, basefont => 1, bgsound => 1, br => 1,
7588                      embed => 1, img => 1, spacer => 1, wbr => 1,
7589                     }->{$token->{tag_name}}) {
7590              !!!cp ('t388.1');
7591              pop @{$self->{open_elements}};
7592              !!!ack ('t388.3');
7593            } elsif ($token->{tag_name} eq 'select') {
7594              ## TODO: associate with $self->{form_element} if defined
7595            
7596              if ($self->{insertion_mode} & TABLE_IMS or
7597                  $self->{insertion_mode} & BODY_TABLE_IMS or
7598                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7599                !!!cp ('t400.1');
7600                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7601            } else {            } else {
7602              #              !!!cp ('t400.2');
7603                $self->{insertion_mode} = IN_SELECT_IM;
7604            }            }
7605                        !!!nack ('t400.3');
7606            if (defined $token->{tag_name}) {          } else {
7607              !!!parse-error (type => 'in frameset:'.$token->{tag_name});            !!!nack ('t402');
7608            }
7609            
7610            !!!next-token;
7611            next B;
7612          }
7613        } elsif ($token->{type} == END_TAG_TOKEN) {
7614          if ($token->{tag_name} eq 'body') {
7615            ## has a |body| element in scope
7616            my $i;
7617            INSCOPE: {
7618              for (reverse @{$self->{open_elements}}) {
7619                if ($_->[1] & BODY_EL) {
7620                  !!!cp ('t405');
7621                  $i = $_;
7622                  last INSCOPE;
7623                } elsif ($_->[1] & SCOPING_EL) {
7624                  !!!cp ('t405.1');
7625                  last;
7626                }
7627              }
7628    
7629              ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7630    
7631              !!!parse-error (type => 'unmatched end tag',
7632                              text => $token->{tag_name}, token => $token);
7633              ## NOTE: Ignore the token.
7634              !!!next-token;
7635              next B;
7636            } # INSCOPE
7637    
7638            for (@{$self->{open_elements}}) {
7639              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7640                !!!cp ('t403');
7641                !!!parse-error (type => 'not closed',
7642                                text => $_->[0]->manakai_local_name,
7643                                token => $token);
7644                last;
7645              } else {
7646                !!!cp ('t404');
7647              }
7648            }
7649    
7650            $self->{insertion_mode} = AFTER_BODY_IM;
7651            !!!next-token;
7652            next B;
7653          } elsif ($token->{tag_name} eq 'html') {
7654            ## TODO: Update this code.  It seems that the code below is not
7655            ## up-to-date, though it has same effect as speced.
7656            if (@{$self->{open_elements}} > 1 and
7657                $self->{open_elements}->[1]->[1] & BODY_EL) {
7658              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7659                !!!cp ('t406');
7660                !!!parse-error (type => 'not closed',
7661                                text => $self->{open_elements}->[1]->[0]
7662                                    ->manakai_local_name,
7663                                token => $token);
7664            } else {            } else {
7665              !!!parse-error (type => 'in frameset:#'.$token->{type});              !!!cp ('t407');
7666            }            }
7667              $self->{insertion_mode} = AFTER_BODY_IM;
7668              ## reprocess
7669              next B;
7670            } else {
7671              !!!cp ('t408');
7672              !!!parse-error (type => 'unmatched end tag',
7673                              text => $token->{tag_name}, token => $token);
7674            ## Ignore the token            ## Ignore the token
7675            !!!next-token;            !!!next-token;
7676            redo B;            next B;
7677          } elsif ($self->{insertion_mode} eq 'after frameset') {          }
7678            if ($token->{type} eq 'character') {        } elsif ({
7679              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                  ## NOTE: End tags for non-phrasing flow content elements
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
7680    
7681                unless (length $token->{data}) {                  ## NOTE: The normal ones
7682                  !!!next-token;                  address => 1, article => 1, aside => 1, blockquote => 1,
7683                  redo B;                  center => 1, datagrid => 1, details => 1, dialog => 1,
7684                }                  dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7685              }                  footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7686                    ol => 1, pre => 1, section => 1, ul => 1,
7687    
7688                    ## NOTE: As normal, but ... optional tags
7689                    dd => 1, dt => 1, li => 1,
7690    
7691                    applet => 1, button => 1, marquee => 1, object => 1,
7692                   }->{$token->{tag_name}}) {
7693            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7694            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7695            ## </dd>" code.
7696    
7697            ## has an element in scope
7698            my $i;
7699            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7700              my $node = $self->{open_elements}->[$_];
7701              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7702                !!!cp ('t410');
7703                $i = $_;
7704                last INSCOPE;
7705              } elsif ($node->[1] & SCOPING_EL) {
7706                !!!cp ('t411');
7707                last INSCOPE;
7708              }
7709            } # INSCOPE
7710    
7711            unless (defined $i) { # has an element in scope
7712              !!!cp ('t413');
7713              !!!parse-error (type => 'unmatched end tag',
7714                              text => $token->{tag_name}, token => $token);
7715              ## NOTE: Ignore the token.
7716            } else {
7717              ## Step 1. generate implied end tags
7718              while ({
7719                      ## END_TAG_OPTIONAL_EL
7720                      dd => ($token->{tag_name} ne 'dd'),
7721                      dt => ($token->{tag_name} ne 'dt'),
7722                      li => ($token->{tag_name} ne 'li'),
7723                      option => 1,
7724                      optgroup => 1,
7725                      p => 1,
7726                      rt => 1,
7727                      rp => 1,
7728                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7729                !!!cp ('t409');
7730                pop @{$self->{open_elements}};
7731              }
7732    
7733              ## Step 2.
7734              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7735                      ne $token->{tag_name}) {
7736                !!!cp ('t412');
7737                !!!parse-error (type => 'not closed',
7738                                text => $self->{open_elements}->[-1]->[0]
7739                                    ->manakai_local_name,
7740                                token => $token);
7741              } else {
7742                !!!cp ('t414');
7743              }
7744    
7745              ## Step 3.
7746              splice @{$self->{open_elements}}, $i;
7747    
7748              ## Step 4.
7749              $clear_up_to_marker->()
7750                  if {
7751                    applet => 1, button => 1, marquee => 1, object => 1,
7752                  }->{$token->{tag_name}};
7753            }
7754            !!!next-token;
7755            next B;
7756          } elsif ($token->{tag_name} eq 'form') {
7757            ## NOTE: As normal, but interacts with the form element pointer
7758    
7759            undef $self->{form_element};
7760    
7761            ## has an element in scope
7762            my $i;
7763            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7764              my $node = $self->{open_elements}->[$_];
7765              if ($node->[1] & FORM_EL) {
7766                !!!cp ('t418');
7767                $i = $_;
7768                last INSCOPE;
7769              } elsif ($node->[1] & SCOPING_EL) {
7770                !!!cp ('t419');
7771                last INSCOPE;
7772              }
7773            } # INSCOPE
7774    
7775            unless (defined $i) { # has an element in scope
7776              !!!cp ('t421');
7777              !!!parse-error (type => 'unmatched end tag',
7778                              text => $token->{tag_name}, token => $token);
7779              ## NOTE: Ignore the token.
7780            } else {
7781              ## Step 1. generate implied end tags
7782              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7783                !!!cp ('t417');
7784                pop @{$self->{open_elements}};
7785              }
7786              
7787              ## Step 2.
7788              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7789                      ne $token->{tag_name}) {
7790                !!!cp ('t417.1');
7791                !!!parse-error (type => 'not closed',
7792                                text => $self->{open_elements}->[-1]->[0]
7793                                    ->manakai_local_name,
7794                                token => $token);
7795              } else {
7796                !!!cp ('t420');
7797              }  
7798              
7799              ## Step 3.
7800              splice @{$self->{open_elements}}, $i;
7801            }
7802    
7803            !!!next-token;
7804            next B;
7805          } elsif ({
7806                    ## NOTE: As normal, except acts as a closer for any ...
7807                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7808                   }->{$token->{tag_name}}) {
7809            ## has an element in scope
7810            my $i;
7811            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7812              my $node = $self->{open_elements}->[$_];
7813              if ($node->[1] & HEADING_EL) {
7814                !!!cp ('t423');
7815                $i = $_;
7816                last INSCOPE;
7817              } elsif ($node->[1] & SCOPING_EL) {
7818                !!!cp ('t424');
7819                last INSCOPE;
7820              }
7821            } # INSCOPE
7822    
7823            unless (defined $i) { # has an element in scope
7824              !!!cp ('t425.1');
7825              !!!parse-error (type => 'unmatched end tag',
7826                              text => $token->{tag_name}, token => $token);
7827              ## NOTE: Ignore the token.
7828            } else {
7829              ## Step 1. generate implied end tags
7830              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7831                !!!cp ('t422');
7832                pop @{$self->{open_elements}};
7833              }
7834              
7835              ## Step 2.
7836              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7837                      ne $token->{tag_name}) {
7838                !!!cp ('t425');
7839                !!!parse-error (type => 'unmatched end tag',
7840                                text => $token->{tag_name}, token => $token);
7841              } else {
7842                !!!cp ('t426');
7843              }
7844    
7845              ## Step 3.
7846              splice @{$self->{open_elements}}, $i;
7847            }
7848            
7849            !!!next-token;
7850            next B;
7851          } elsif ($token->{tag_name} eq 'p') {
7852            ## NOTE: As normal, except </p> implies <p> and ...
7853    
7854            ## has an element in scope
7855            my $non_optional;
7856            my $i;
7857            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7858              my $node = $self->{open_elements}->[$_];
7859              if ($node->[1] & P_EL) {
7860                !!!cp ('t410.1');
7861                $i = $_;
7862                last INSCOPE;
7863              } elsif ($node->[1] & SCOPING_EL) {
7864                !!!cp ('t411.1');
7865                last INSCOPE;
7866              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7867                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7868                !!!cp ('t411.2');
7869              #              #
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               $phase = 'trailing end';  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
7870            } else {            } else {
7871                !!!cp ('t411.3');
7872                $non_optional ||= $node;
7873              #              #
7874            }            }
7875                      } # INSCOPE
7876            if (defined $token->{tag_name}) {  
7877              !!!parse-error (type => 'after frameset:'.$token->{tag_name});          if (defined $i) {
7878              ## 1. Generate implied end tags
7879              #
7880    
7881              ## 2. If current node != "p", parse error
7882              if ($non_optional) {
7883                !!!cp ('t412.1');
7884                !!!parse-error (type => 'not closed',
7885                                text => $non_optional->[0]->manakai_local_name,
7886                                token => $token);
7887            } else {            } else {
7888              !!!parse-error (type => 'after frameset:#'.$token->{type});              !!!cp ('t414.1');
7889            }            }
           ## Ignore the token  
           !!!next-token;  
           redo B;  
7890    
7891            ## ISSUE: An issue in spec there            ## 3. Pop
7892              splice @{$self->{open_elements}}, $i;
7893          } else {          } else {
7894            die "$0: $self->{insertion_mode}: Unknown insertion mode";            !!!cp ('t413.1');
7895              !!!parse-error (type => 'unmatched end tag',
7896                              text => $token->{tag_name}, token => $token);
7897    
7898              !!!cp ('t415.1');
7899              ## As if <p>, then reprocess the current token
7900              my $el;
7901              !!!create-element ($el, $HTML_NS, 'p',, $token);
7902              $insert->($el);
7903              ## NOTE: Not inserted into |$self->{open_elements}|.
7904          }          }
7905        }  
     } elsif ($phase eq 'trailing end') {  
       ## states in the main stage is preserved yet # MUST  
         
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'after html:#DOCTYPE');  
         ## Ignore the token  
7906          !!!next-token;          !!!next-token;
7907          redo B;          next B;
7908        } elsif ($token->{type} eq 'comment') {        } elsif ({
7909          my $comment = $self->{document}->create_comment ($token->{data});                  a => 1,
7910          $self->{document}->append_child ($comment);                  b => 1, big => 1, em => 1, font => 1, i => 1,
7911                    nobr => 1, s => 1, small => 1, strike => 1,
7912                    strong => 1, tt => 1, u => 1,
7913                   }->{$token->{tag_name}}) {
7914            !!!cp ('t427');
7915            $formatting_end_tag->($token);
7916            next B;
7917          } elsif ($token->{tag_name} eq 'br') {
7918            !!!cp ('t428');
7919            !!!parse-error (type => 'unmatched end tag',
7920                            text => 'br', token => $token);
7921    
7922            ## As if <br>
7923            $reconstruct_active_formatting_elements->($insert_to_current);
7924            
7925            my $el;
7926            !!!create-element ($el, $HTML_NS, 'br',, $token);
7927            $insert->($el);
7928            
7929            ## Ignore the token.
7930          !!!next-token;          !!!next-token;
7931          redo B;          next B;
7932        } elsif ($token->{type} eq 'character') {        } else {
7933          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{tag_name} eq 'sarcasm') {
7934            my $data = $1;            sleep 0.001; # take a deep breath
7935            ## As if in the main phase.          }
7936            ## NOTE: The insertion mode in the main phase  
7937            ## just before the phase has been changed to the trailing          ## Step 1
7938            ## end phase is either "after body" or "after frameset".          my $node_i = -1;
7939            $reconstruct_active_formatting_elements->($insert_to_current)          my $node = $self->{open_elements}->[$node_i];
7940              if $phase eq 'main';  
7941            ## Step 2
7942            S2: {
7943              my $node_tag_name = $node->[0]->manakai_local_name;
7944              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7945              if ($node_tag_name eq $token->{tag_name}) {
7946                ## Step 1
7947                ## generate implied end tags
7948                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7949                  !!!cp ('t430');
7950                  ## NOTE: |<ruby><rt></ruby>|.
7951                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7952                  ## which seems wrong.
7953                  pop @{$self->{open_elements}};
7954                  $node_i++;
7955                }
7956            
7957                ## Step 2
7958                my $current_tag_name
7959                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7960                $current_tag_name =~ tr/A-Z/a-z/;
7961                if ($current_tag_name ne $token->{tag_name}) {
7962                  !!!cp ('t431');
7963                  ## NOTE: <x><y></x>
7964                  !!!parse-error (type => 'not closed',
7965                                  text => $self->{open_elements}->[-1]->[0]
7966                                      ->manakai_local_name,
7967                                  token => $token);
7968                } else {
7969                  !!!cp ('t432');
7970                }
7971                
7972                ## Step 3
7973                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7974    
7975                !!!next-token;
7976                last S2;
7977              } else {
7978                ## Step 3
7979                if (not ($node->[1] & FORMATTING_EL) and
7980                    #not $phrasing_category->{$node->[1]} and
7981                    ($node->[1] & SPECIAL_EL or
7982                     $node->[1] & SCOPING_EL)) {
7983                  !!!cp ('t433');
7984                  !!!parse-error (type => 'unmatched end tag',
7985                                  text => $token->{tag_name}, token => $token);
7986                  ## Ignore the token
7987                  !!!next-token;
7988                  last S2;
7989    
7990                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7991                  ## 9.27, "a" is a child of <dd> (conforming).  In
7992                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7993                  ## "a" is a child of both <body> and <dd>.
7994                }
7995                
7996                !!!cp ('t434');
7997              }
7998                        
7999            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 4
8000              $node_i--;
8001              $node = $self->{open_elements}->[$node_i];
8002                        
8003            unless (length $token->{data}) {            ## Step 5;
8004              !!!next-token;            redo S2;
8005              redo B;          } # S2
8006            next B;
8007          }
8008        }
8009        next B;
8010      } continue { # B
8011        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8012          ## NOTE: The code below is executed in cases where it does not have
8013          ## to be, but it it is harmless even in those cases.
8014          ## has an element in scope
8015          INSCOPE: {
8016            for (reverse 0..$#{$self->{open_elements}}) {
8017              my $node = $self->{open_elements}->[$_];
8018              if ($node->[1] & FOREIGN_EL) {
8019                last INSCOPE;
8020              } elsif ($node->[1] & SCOPING_EL) {
8021                last;
8022            }            }
8023          }          }
8024            
8025          !!!parse-error (type => 'after html:#character');          ## NOTE: No foreign element in scope.
8026          $phase = 'main';          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8027          ## reprocess        } # INSCOPE
         redo B;  
       } elsif ($token->{type} eq 'start tag' or  
                $token->{type} eq 'end tag') {  
         !!!parse-error (type => 'after html:'.$token->{tag_name});  
         $phase = 'main';  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Stop parsing  
         last B;  
       } else {  
         die "$0: $token->{type}: Unknown token";  
       }  
8028      }      }
8029    } # B    } # B
8030    
# Line 4907  sub _tree_construction_main ($) { Line 8033  sub _tree_construction_main ($) {
8033    ## TODO: script stuffs    ## TODO: script stuffs
8034  } # _tree_construct_main  } # _tree_construct_main
8035    
8036  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8037    my $class = shift;    my $class = shift;
8038    my $node = shift;    my $node = shift;
8039    my $s = \$_[0];    #my $s = \$_[0];
8040    my $onerror = $_[1];    my $onerror = $_[1];
8041      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8042    
8043      ## ISSUE: Should {confident} be true?
8044    
8045    my $nt = $node->node_type;    my $nt = $node->node_type;
8046    if ($nt == 9) {    if ($nt == 9) {
# Line 4928  sub set_inner_html ($$$) { Line 8057  sub set_inner_html ($$$) {
8057      }      }
8058    
8059      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8060      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8061    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8062      ## TODO: If non-html element      ## TODO: If non-html element
8063    
8064      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8065    
8066    ## TODO: Support for $get_wrapper
8067    
8068      ## Step 1 # MUST      ## Step 1 # MUST
8069      my $doc = $node->owner_document->implementation->create_document;      my $this_doc = $node->owner_document;
8070      ## TODO: Mark as HTML document      my $doc = $this_doc->implementation->create_document;
8071        $doc->manakai_is_html (1);
8072      my $p = $class->new;      my $p = $class->new;
8073      $p->{document} = $doc;      $p->{document} = $doc;
8074    
8075      ## Step 9 # MUST      ## Step 8 # MUST
8076      my $i = 0;      my $i = 0;
8077      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8078      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8079      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8080        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8081        $input = $get_wrapper->($input);
8082        $p->{set_nc} = sub {
8083        my $self = shift;        my $self = shift;
8084        $self->{next_input_character} = -1 and return if $i >= length $$s;  
8085        $self->{next_input_character} = ord substr $$s, $i++, 1;        my $char = '';
8086        $column++;        if (defined $self->{next_nc}) {
8087            $char = $self->{next_nc};
8088        if ($self->{next_input_character} == 0x000A) { # LF          delete $self->{next_nc};
8089          $line++;          $self->{nc} = ord $char;
8090          $column = 0;        } else {
8091        } elsif ($self->{next_input_character} == 0x000D) { # CR          $self->{char_buffer} = '';
8092          if ($i >= length $$s) {          $self->{char_buffer_pos} = 0;
8093            #          
8094            my $count = $input->manakai_read_until
8095                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8096                 $self->{char_buffer_pos});
8097            if ($count) {
8098              $self->{line_prev} = $self->{line};
8099              $self->{column_prev} = $self->{column};
8100              $self->{column}++;
8101              $self->{nc}
8102                  = ord substr ($self->{char_buffer},
8103                                $self->{char_buffer_pos}++, 1);
8104              return;
8105            }
8106            
8107            if ($input->read ($char, 1)) {
8108              $self->{nc} = ord $char;
8109          } else {          } else {
8110            my $next_char = ord substr $$s, $i++, 1;            $self->{nc} = -1;
8111            if ($next_char == 0x000A) { # LF            return;
             #  
           } else {  
             push @{$self->{char}}, $next_char;  
           }  
8112          }          }
8113          $self->{next_input_character} = 0x000A; # LF # MUST        }
8114          $line++;  
8115          $column = 0;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8116        } elsif ($self->{next_input_character} > 0x10FFFF) {        $p->{column}++;
8117          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST  
8118        } elsif ($self->{next_input_character} == 0x0000) { # NULL        if ($self->{nc} == 0x000A) { # LF
8119          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $p->{line}++;
8120            $p->{column} = 0;
8121            !!!cp ('i1');
8122          } elsif ($self->{nc} == 0x000D) { # CR
8123    ## TODO: support for abort/streaming
8124            my $next = '';
8125            if ($input->read ($next, 1) and $next ne "\x0A") {
8126              $self->{next_nc} = $next;
8127            }
8128            $self->{nc} = 0x000A; # LF # MUST
8129            $p->{line}++;
8130            $p->{column} = 0;
8131            !!!cp ('i2');
8132          } elsif ($self->{nc} == 0x0000) { # NULL
8133            !!!cp ('i4');
8134            !!!parse-error (type => 'NULL');
8135            $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8136        }        }
8137      };      };
8138        
8139        $p->{read_until} = sub {
8140          #my ($scalar, $specials_range, $offset) = @_;
8141          return 0 if defined $p->{next_nc};
8142    
8143          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8144          my $offset = $_[2] || 0;
8145          
8146          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8147            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8148            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8149              substr ($_[0], $offset)
8150                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8151              my $count = $+[0] - $-[0];
8152              if ($count) {
8153                $p->{column} += $count;
8154                $p->{char_buffer_pos} += $count;
8155                $p->{line_prev} = $p->{line};
8156                $p->{column_prev} = $p->{column} - 1;
8157                $p->{nc} = -1;
8158              }
8159              return $count;
8160            } else {
8161              return 0;
8162            }
8163          } else {
8164            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8165            if ($count) {
8166              $p->{column} += $count;
8167              $p->{column_prev} += $count;
8168              $p->{nc} = -1;
8169            }
8170            return $count;
8171          }
8172        }; # $p->{read_until}
8173    
8174      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8175        my (%opt) = @_;        my (%opt) = @_;
8176        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8177          my $column = $opt{column};
8178          if (defined $opt{token} and defined $opt{token}->{line}) {
8179            $line = $opt{token}->{line};
8180            $column = $opt{token}->{column};
8181          }
8182          warn "Parse error ($opt{type}) at line $line column $column\n";
8183      };      };
8184      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8185        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8186      };      };
8187            
8188        my $char_onerror = sub {
8189          my (undef, $type, %opt) = @_;
8190          $ponerror->(layer => 'encode',
8191                      line => $p->{line}, column => $p->{column} + 1,
8192                      %opt, type => $type);
8193        }; # $char_onerror
8194        $input->onerror ($char_onerror);
8195    
8196      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8197      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8198    
8199      ## Step 2      ## Step 2
8200      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8201      $p->{content_model_flag} = {      $p->{content_model} = {
8202        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
8203        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
8204        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
8205        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
8206        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
8207        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
8208        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
8209        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
8210        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
8211        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
8212      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
8213         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
8214            unless defined $p->{content_model};
8215            ## ISSUE: What is "the name of the element"? local name?
8216    
8217      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8218          ## TODO: Foreign element OK?
8219    
8220      ## Step 4      ## Step 3
8221      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8222        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8223    
8224      ## Step 5 # MUST      ## Step 4 # MUST
8225      $doc->append_child ($root);      $doc->append_child ($root);
8226    
8227      ## Step 6 # MUST      ## Step 5 # MUST
8228      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8229    
8230      undef $p->{head_element};      undef $p->{head_element};
8231        undef $p->{head_element_inserted};
8232    
8233      ## Step 7 # MUST      ## Step 6 # MUST
8234      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8235    
8236      ## Step 8 # MUST      ## Step 7 # MUST
8237      my $anode = $node;      my $anode = $node;
8238      AN: while (defined $anode) {      AN: while (defined $anode) {
8239        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8240          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8241          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8242            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8243                !!!cp ('i5');
8244              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8245              last AN;              last AN;
8246            }            }
# Line 5033  sub set_inner_html ($$$) { Line 8249  sub set_inner_html ($$$) {
8249        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8250      } # AN      } # AN
8251            
8252      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8253      {      {
8254        my $self = $p;        my $self = $p;
8255        !!!next-token;        !!!next-token;
8256      }      }
8257      $p->_tree_construction_main;      $p->_tree_construction_main;
8258    
8259      ## Step 11 # MUST      ## Step 10 # MUST
8260      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8261      for (@cn) {      for (@cn) {
8262        $node->remove_child ($_);        $node->remove_child ($_);
8263      }      }
8264      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8265    
8266      ## Step 12 # MUST      ## Step 11 # MUST
8267      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8268      for (@cn) {      for (@cn) {
8269          $this_doc->adopt_node ($_);
8270        $node->append_child ($_);        $node->append_child ($_);
8271      }      }
8272      ## ISSUE: adopt_node? mutation events?      ## ISSUE: mutation events?
8273    
8274      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8275    
8276        delete $p->{parse_error}; # delete loop
8277    } else {    } else {
8278      die "$0: |set_inner_html| is not defined for node of type $nt";      die "$0: |set_inner_html| is not defined for node of type $nt";
8279    }    }
# Line 5063  sub set_inner_html ($$$) { Line 8281  sub set_inner_html ($$$) {
8281    
8282  } # tree construction stage  } # tree construction stage
8283    
8284  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8285    my (undef, $node, $on_error) = @_;  push our @ISA, 'Error';
   
   ## Step 1  
   my $s = '';  
   
   my $in_cdata;  
   my $parent = $node;  
   while (defined $parent) {  
     if ($parent->node_type == 1 and  
         $parent->namespace_uri eq 'http://www.w3.org/1999/xhtml' and  
         {  
           style => 1, script => 1, xmp => 1, iframe => 1,  
           noembed => 1, noframes => 1, noscript => 1,  
         }->{$parent->local_name}) { ## TODO: case thingy  
       $in_cdata = 1;  
     }  
     $parent = $parent->parent_node;  
   }  
   
   ## Step 2  
   my @node = @{$node->child_nodes};  
   C: while (@node) {  
     my $child = shift @node;  
     unless (ref $child) {  
       if ($child eq 'cdata-out') {  
         $in_cdata = 0;  
       } else {  
         $s .= $child; # end tag  
       }  
       next C;  
     }  
       
     my $nt = $child->node_type;  
     if ($nt == 1) { # Element  
       my $tag_name = lc $child->tag_name; ## ISSUE: Definition of "lowercase"  
       $s .= '<' . $tag_name;  
   
       ## ISSUE: Non-html elements  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = lc $attr->name; ## ISSUE: Definition of "lowercase"  
         $s .= ' ' . $attr_name . '="';  
         my $attr_value = $attr->value;  
         ## escape  
         $attr_value =~ s/&/&amp;/g;  
         $attr_value =~ s/</&lt;/g;  
         $attr_value =~ s/>/&gt;/g;  
         $attr_value =~ s/"/&quot;/g;  
         $s .= $attr_value . '"';  
       }  
       $s .= '>';  
         
       next C if {  
         area => 1, base => 1, basefont => 1, bgsound => 1,  
         br => 1, col => 1, embed => 1, frame => 1, hr => 1,  
         img => 1, input => 1, link => 1, meta => 1, param => 1,  
         spacer => 1, wbr => 1,  
       }->{$tag_name};  
   
       if (not $in_cdata and {  
         style => 1, script => 1, xmp => 1, iframe => 1,  
         noembed => 1, noframes => 1, noscript => 1,  
       }->{$tag_name}) {  
         unshift @node, 'cdata-out';  
         $in_cdata = 1;  
       }  
   
       unshift @node, @{$child->child_nodes}, '</' . $tag_name . '>';  
     } elsif ($nt == 3 or $nt == 4) {  
       if ($in_cdata) {  
         $s .= $child->data;  
       } else {  
         my $value = $child->data;  
         $value =~ s/&/&amp;/g;  
         $value =~ s/</&lt;/g;  
         $value =~ s/>/&gt;/g;  
         $value =~ s/"/&quot;/g;  
         $s .= $value;  
       }  
     } elsif ($nt == 8) {  
       $s .= '<!--' . $child->data . '-->';  
     } elsif ($nt == 10) {  
       $s .= '<!DOCTYPE ' . $child->name . '>';  
     } elsif ($nt == 5) { # entrefs  
       push @node, @{$child->child_nodes};  
     } else {  
       $on_error->($child) if defined $on_error;  
     }  
     ## ISSUE: This code does not support PIs.  
   } # C  
     
   ## Step 3  
   return \$s;  
 } # get_inner_html  
8286    
8287  1;  1;
8288  # $Date$  # $Date$

Legend:
Removed from v.1.13  
changed lines
  Added in v.1.204

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24