/[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.44 by wakaba, Sat Jul 21 07:34:32 2007 UTC revision 1.193 by wakaba, Sat Oct 4 04:06:33 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    ## 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:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## ISSUE: HTML5 revision 967 says that the encoding layer MUST NOT  require IO::Handle;
23  ## strip BOM and the HTML layer MUST ignore it.  Whether we can do it  
24  ## is not yet clear.  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25  ## "{U+FEFF}..." in UTF-16BE/UTF-16LE is three or four characters?  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  ## "{U+FEFF}..." in GB18030?  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28  my $permitted_slash_tag_name = {  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    base => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    link => 1,  
31    meta => 1,  sub A_EL () { 0b1 }
32    hr => 1,  sub ADDRESS_EL () { 0b10 }
33    br => 1,  sub BODY_EL () { 0b100 }
34    img=> 1,  sub BUTTON_EL () { 0b1000 }
35    embed => 1,  sub CAPTION_EL () { 0b10000 }
36    param => 1,  sub DD_EL () { 0b100000 }
37    area => 1,  sub DIV_EL () { 0b1000000 }
38    col => 1,  sub DT_EL () { 0b10000000 }
39    input => 1,  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 END_TAG_OPTIONAL_EL
70    ## is used in "generate implied end tags" implementation (search for the
71    ## function mae).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      P_EL |
77      RUBY_COMPONENT_EL
78    }
79    
80    ## NOTE: Used in </body> and EOF algorithms.
81    sub ALL_END_TAG_OPTIONAL_EL () {
82      DD_EL |
83      DT_EL |
84      LI_EL |
85      P_EL |
86    
87      BODY_EL |
88      HTML_EL |
89      TABLE_CELL_EL |
90      TABLE_ROW_EL |
91      TABLE_ROW_GROUP_EL
92    }
93    
94    sub SCOPING_EL () {
95      BUTTON_EL |
96      CAPTION_EL |
97      HTML_EL |
98      TABLE_EL |
99      TABLE_CELL_EL |
100      MISC_SCOPING_EL
101    }
102    
103    sub TABLE_SCOPING_EL () {
104      HTML_EL |
105      TABLE_EL
106    }
107    
108    sub TABLE_ROWS_SCOPING_EL () {
109      HTML_EL |
110      TABLE_ROW_GROUP_EL
111    }
112    
113    sub TABLE_ROW_SCOPING_EL () {
114      HTML_EL |
115      TABLE_ROW_EL
116    }
117    
118    sub SPECIAL_EL () {
119      ADDRESS_EL |
120      BODY_EL |
121      DIV_EL |
122    
123      DD_EL |
124      DT_EL |
125      LI_EL |
126      P_EL |
127    
128      FORM_EL |
129      FRAMESET_EL |
130      HEADING_EL |
131      OPTION_EL |
132      OPTGROUP_EL |
133      SELECT_EL |
134      TABLE_ROW_EL |
135      TABLE_ROW_GROUP_EL |
136      MISC_SPECIAL_EL
137    }
138    
139    my $el_category = {
140      a => A_EL | FORMATTING_EL,
141      address => ADDRESS_EL,
142      applet => MISC_SCOPING_EL,
143      area => MISC_SPECIAL_EL,
144      article => MISC_SPECIAL_EL,
145      aside => MISC_SPECIAL_EL,
146      b => FORMATTING_EL,
147      base => MISC_SPECIAL_EL,
148      basefont => MISC_SPECIAL_EL,
149      bgsound => MISC_SPECIAL_EL,
150      big => FORMATTING_EL,
151      blockquote => MISC_SPECIAL_EL,
152      body => BODY_EL,
153      br => MISC_SPECIAL_EL,
154      button => BUTTON_EL,
155      caption => CAPTION_EL,
156      center => MISC_SPECIAL_EL,
157      col => MISC_SPECIAL_EL,
158      colgroup => MISC_SPECIAL_EL,
159      command => MISC_SPECIAL_EL,
160      datagrid => MISC_SPECIAL_EL,
161      dd => DD_EL,
162      details => MISC_SPECIAL_EL,
163      dialog => MISC_SPECIAL_EL,
164      dir => MISC_SPECIAL_EL,
165      div => DIV_EL,
166      dl => MISC_SPECIAL_EL,
167      dt => DT_EL,
168      em => FORMATTING_EL,
169      embed => MISC_SPECIAL_EL,
170      eventsource => MISC_SPECIAL_EL,
171      fieldset => MISC_SPECIAL_EL,
172      figure => MISC_SPECIAL_EL,
173      font => FORMATTING_EL,
174      footer => MISC_SPECIAL_EL,
175      form => FORM_EL,
176      frame => MISC_SPECIAL_EL,
177      frameset => FRAMESET_EL,
178      h1 => HEADING_EL,
179      h2 => HEADING_EL,
180      h3 => HEADING_EL,
181      h4 => HEADING_EL,
182      h5 => HEADING_EL,
183      h6 => HEADING_EL,
184      head => MISC_SPECIAL_EL,
185      header => MISC_SPECIAL_EL,
186      hr => MISC_SPECIAL_EL,
187      html => HTML_EL,
188      i => FORMATTING_EL,
189      iframe => MISC_SPECIAL_EL,
190      img => MISC_SPECIAL_EL,
191      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
192      input => MISC_SPECIAL_EL,
193      isindex => MISC_SPECIAL_EL,
194      li => LI_EL,
195      link => MISC_SPECIAL_EL,
196      listing => MISC_SPECIAL_EL,
197      marquee => MISC_SCOPING_EL,
198      menu => MISC_SPECIAL_EL,
199      meta => MISC_SPECIAL_EL,
200      nav => MISC_SPECIAL_EL,
201      nobr => NOBR_EL | FORMATTING_EL,
202      noembed => MISC_SPECIAL_EL,
203      noframes => MISC_SPECIAL_EL,
204      noscript => MISC_SPECIAL_EL,
205      object => MISC_SCOPING_EL,
206      ol => MISC_SPECIAL_EL,
207      optgroup => OPTGROUP_EL,
208      option => OPTION_EL,
209      p => P_EL,
210      param => MISC_SPECIAL_EL,
211      plaintext => MISC_SPECIAL_EL,
212      pre => MISC_SPECIAL_EL,
213      rp => RUBY_COMPONENT_EL,
214      rt => RUBY_COMPONENT_EL,
215      ruby => RUBY_EL,
216      s => FORMATTING_EL,
217      script => MISC_SPECIAL_EL,
218      select => SELECT_EL,
219      section => MISC_SPECIAL_EL,
220      small => FORMATTING_EL,
221      spacer => MISC_SPECIAL_EL,
222      strike => FORMATTING_EL,
223      strong => FORMATTING_EL,
224      style => MISC_SPECIAL_EL,
225      table => TABLE_EL,
226      tbody => TABLE_ROW_GROUP_EL,
227      td => TABLE_CELL_EL,
228      textarea => MISC_SPECIAL_EL,
229      tfoot => TABLE_ROW_GROUP_EL,
230      th => TABLE_CELL_EL,
231      thead => TABLE_ROW_GROUP_EL,
232      title => MISC_SPECIAL_EL,
233      tr => TABLE_ROW_EL,
234      tt => FORMATTING_EL,
235      u => FORMATTING_EL,
236      ul => MISC_SPECIAL_EL,
237      wbr => MISC_SPECIAL_EL,
238    };
239    
240    my $el_category_f = {
241      $MML_NS => {
242        'annotation-xml' => MML_AXML_EL,
243        mi => FOREIGN_FLOW_CONTENT_EL,
244        mo => FOREIGN_FLOW_CONTENT_EL,
245        mn => FOREIGN_FLOW_CONTENT_EL,
246        ms => FOREIGN_FLOW_CONTENT_EL,
247        mtext => FOREIGN_FLOW_CONTENT_EL,
248      },
249      $SVG_NS => {
250        foreignObject => FOREIGN_FLOW_CONTENT_EL,
251        desc => FOREIGN_FLOW_CONTENT_EL,
252        title => FOREIGN_FLOW_CONTENT_EL,
253      },
254      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
255    };
256    
257    my $svg_attr_name = {
258      attributename => 'attributeName',
259      attributetype => 'attributeType',
260      basefrequency => 'baseFrequency',
261      baseprofile => 'baseProfile',
262      calcmode => 'calcMode',
263      clippathunits => 'clipPathUnits',
264      contentscripttype => 'contentScriptType',
265      contentstyletype => 'contentStyleType',
266      diffuseconstant => 'diffuseConstant',
267      edgemode => 'edgeMode',
268      externalresourcesrequired => 'externalResourcesRequired',
269      filterres => 'filterRes',
270      filterunits => 'filterUnits',
271      glyphref => 'glyphRef',
272      gradienttransform => 'gradientTransform',
273      gradientunits => 'gradientUnits',
274      kernelmatrix => 'kernelMatrix',
275      kernelunitlength => 'kernelUnitLength',
276      keypoints => 'keyPoints',
277      keysplines => 'keySplines',
278      keytimes => 'keyTimes',
279      lengthadjust => 'lengthAdjust',
280      limitingconeangle => 'limitingConeAngle',
281      markerheight => 'markerHeight',
282      markerunits => 'markerUnits',
283      markerwidth => 'markerWidth',
284      maskcontentunits => 'maskContentUnits',
285      maskunits => 'maskUnits',
286      numoctaves => 'numOctaves',
287      pathlength => 'pathLength',
288      patterncontentunits => 'patternContentUnits',
289      patterntransform => 'patternTransform',
290      patternunits => 'patternUnits',
291      pointsatx => 'pointsAtX',
292      pointsaty => 'pointsAtY',
293      pointsatz => 'pointsAtZ',
294      preservealpha => 'preserveAlpha',
295      preserveaspectratio => 'preserveAspectRatio',
296      primitiveunits => 'primitiveUnits',
297      refx => 'refX',
298      refy => 'refY',
299      repeatcount => 'repeatCount',
300      repeatdur => 'repeatDur',
301      requiredextensions => 'requiredExtensions',
302      requiredfeatures => 'requiredFeatures',
303      specularconstant => 'specularConstant',
304      specularexponent => 'specularExponent',
305      spreadmethod => 'spreadMethod',
306      startoffset => 'startOffset',
307      stddeviation => 'stdDeviation',
308      stitchtiles => 'stitchTiles',
309      surfacescale => 'surfaceScale',
310      systemlanguage => 'systemLanguage',
311      tablevalues => 'tableValues',
312      targetx => 'targetX',
313      targety => 'targetY',
314      textlength => 'textLength',
315      viewbox => 'viewBox',
316      viewtarget => 'viewTarget',
317      xchannelselector => 'xChannelSelector',
318      ychannelselector => 'yChannelSelector',
319      zoomandpan => 'zoomAndPan',
320  };  };
321    
322  my $c1_entity_char = {  my $foreign_attr_xname = {
323      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
324      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
325      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
326      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
327      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
328      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
329      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
330      'xml:base' => [$XML_NS, ['xml', 'base']],
331      'xml:lang' => [$XML_NS, ['xml', 'lang']],
332      'xml:space' => [$XML_NS, ['xml', 'space']],
333      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
334      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
335    };
336    
337    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
338    
339    my $charref_map = {
340      0x0D => 0x000A,
341    0x80 => 0x20AC,    0x80 => 0x20AC,
342    0x81 => 0xFFFD,    0x81 => 0xFFFD,
343    0x82 => 0x201A,    0x82 => 0x201A,
# Line 60  my $c1_entity_char = { Line 370  my $c1_entity_char = {
370    0x9D => 0xFFFD,    0x9D => 0xFFFD,
371    0x9E => 0x017E,    0x9E => 0x017E,
372    0x9F => 0x0178,    0x9F => 0x0178,
373  }; # $c1_entity_char  }; # $charref_map
374    $charref_map->{$_} = 0xFFFD
375        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
376            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
377            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
378            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
379            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
380            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
381            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
382    
383  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
384    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
385    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
386    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  sub parse_byte_string ($$$$;$) {
387    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    my $self = shift;
388    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    my $charset_name = shift;
389    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
390    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
391    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  } # parse_byte_string
392    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
393    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  sub parse_byte_stream ($$$$;$$) {
394  };    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
395  my $scoping_category = {    my $self = ref $_[0] ? shift : shift->new;
396    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $charset_name = shift;
397    table => 1, td => 1, th => 1,    my $byte_stream = $_[0];
398  };  
399  my $formatting_category = {    my $onerror = $_[2] || sub {
400    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,      my (%opt) = @_;
401    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,      warn "Parse error ($opt{type})\n";
402  };    };
403  # $phrasing_category: all other elements    $self->{parse_error} = $onerror; # updated later by parse_char_string
404    
405      my $get_wrapper = $_[3] || sub ($) {
406        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
407      };
408    
409      ## HTML5 encoding sniffing algorithm
410      require Message::Charset::Info;
411      my $charset;
412      my $buffer;
413      my ($char_stream, $e_status);
414    
415      SNIFFING: {
416        ## NOTE: By setting |allow_fallback| option true when the
417        ## |get_decode_handle| method is invoked, we ignore what the HTML5
418        ## spec requires, i.e. unsupported encoding should be ignored.
419          ## TODO: We should not do this unless the parser is invoked
420          ## in the conformance checking mode, in which this behavior
421          ## would be useful.
422    
423        ## Step 1
424        if (defined $charset_name) {
425          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
426              ## TODO: Is this ok?  Transfer protocol's parameter should be
427              ## interpreted in its semantics?
428    
429          ($char_stream, $e_status) = $charset->get_decode_handle
430              ($byte_stream, allow_error_reporting => 1,
431               allow_fallback => 1);
432          if ($char_stream) {
433            $self->{confident} = 1;
434            last SNIFFING;
435          } else {
436            !!!parse-error (type => 'charset:not supported',
437                            layer => 'encode',
438                            line => 1, column => 1,
439                            value => $charset_name,
440                            level => $self->{level}->{uncertain});
441          }
442        }
443    
444        ## Step 2
445        my $byte_buffer = '';
446        for (1..1024) {
447          my $char = $byte_stream->getc;
448          last unless defined $char;
449          $byte_buffer .= $char;
450        } ## TODO: timeout
451    
452        ## Step 3
453        if ($byte_buffer =~ /^\xFE\xFF/) {
454          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
455          ($char_stream, $e_status) = $charset->get_decode_handle
456              ($byte_stream, allow_error_reporting => 1,
457               allow_fallback => 1, byte_buffer => \$byte_buffer);
458          $self->{confident} = 1;
459          last SNIFFING;
460        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
461          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
462          ($char_stream, $e_status) = $charset->get_decode_handle
463              ($byte_stream, allow_error_reporting => 1,
464               allow_fallback => 1, byte_buffer => \$byte_buffer);
465          $self->{confident} = 1;
466          last SNIFFING;
467        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
468          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
469          ($char_stream, $e_status) = $charset->get_decode_handle
470              ($byte_stream, allow_error_reporting => 1,
471               allow_fallback => 1, byte_buffer => \$byte_buffer);
472          $self->{confident} = 1;
473          last SNIFFING;
474        }
475    
476        ## Step 4
477        ## TODO: <meta charset>
478    
479        ## Step 5
480        ## TODO: from history
481    
482        ## Step 6
483        require Whatpm::Charset::UniversalCharDet;
484        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
485            ($byte_buffer);
486        if (defined $charset_name) {
487          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
488    
489          ## ISSUE: Unsupported encoding is not ignored according to the spec.
490          require Whatpm::Charset::DecodeHandle;
491          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
492              ($byte_stream);
493          ($char_stream, $e_status) = $charset->get_decode_handle
494              ($buffer, allow_error_reporting => 1,
495               allow_fallback => 1, byte_buffer => \$byte_buffer);
496          if ($char_stream) {
497            $buffer->{buffer} = $byte_buffer;
498            !!!parse-error (type => 'sniffing:chardet',
499                            text => $charset_name,
500                            level => $self->{level}->{info},
501                            layer => 'encode',
502                            line => 1, column => 1);
503            $self->{confident} = 0;
504            last SNIFFING;
505          }
506        }
507    
508        ## Step 7: default
509        ## TODO: Make this configurable.
510        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
511            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
512            ## detectable in the step 6.
513        require Whatpm::Charset::DecodeHandle;
514        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
515            ($byte_stream);
516        ($char_stream, $e_status)
517            = $charset->get_decode_handle ($buffer,
518                                           allow_error_reporting => 1,
519                                           allow_fallback => 1,
520                                           byte_buffer => \$byte_buffer);
521        $buffer->{buffer} = $byte_buffer;
522        !!!parse-error (type => 'sniffing:default',
523                        text => 'windows-1252',
524                        level => $self->{level}->{info},
525                        line => 1, column => 1,
526                        layer => 'encode');
527        $self->{confident} = 0;
528      } # SNIFFING
529    
530      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
531        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
532        !!!parse-error (type => 'chardecode:fallback',
533                        #text => $self->{input_encoding},
534                        level => $self->{level}->{uncertain},
535                        line => 1, column => 1,
536                        layer => 'encode');
537      } elsif (not ($e_status &
538                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
539        $self->{input_encoding} = $charset->get_iana_name;
540        !!!parse-error (type => 'chardecode:no error',
541                        text => $self->{input_encoding},
542                        level => $self->{level}->{uncertain},
543                        line => 1, column => 1,
544                        layer => 'encode');
545      } else {
546        $self->{input_encoding} = $charset->get_iana_name;
547      }
548    
549      $self->{change_encoding} = sub {
550        my $self = shift;
551        $charset_name = shift;
552        my $token = shift;
553    
554  sub parse_string ($$$;$) {      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
555    my $self = shift->new;      ($char_stream, $e_status) = $charset->get_decode_handle
556    my $s = \$_[0];          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
557             byte_buffer => \ $buffer->{buffer});
558        
559        if ($char_stream) { # if supported
560          ## "Change the encoding" algorithm:
561    
562          ## Step 1    
563          if ($charset->{category} &
564              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
565            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
566            ($char_stream, $e_status) = $charset->get_decode_handle
567                ($byte_stream,
568                 byte_buffer => \ $buffer->{buffer});
569          }
570          $charset_name = $charset->get_iana_name;
571          
572          ## Step 2
573          if (defined $self->{input_encoding} and
574              $self->{input_encoding} eq $charset_name) {
575            !!!parse-error (type => 'charset label:matching',
576                            text => $charset_name,
577                            level => $self->{level}->{info});
578            $self->{confident} = 1;
579            return;
580          }
581    
582          !!!parse-error (type => 'charset label detected',
583                          text => $self->{input_encoding},
584                          value => $charset_name,
585                          level => $self->{level}->{warn},
586                          token => $token);
587          
588          ## Step 3
589          # if (can) {
590            ## change the encoding on the fly.
591            #$self->{confident} = 1;
592            #return;
593          # }
594          
595          ## Step 4
596          throw Whatpm::HTML::RestartParser ();
597        }
598      }; # $self->{change_encoding}
599    
600      my $char_onerror = sub {
601        my (undef, $type, %opt) = @_;
602        !!!parse-error (layer => 'encode',
603                        line => $self->{line}, column => $self->{column} + 1,
604                        %opt, type => $type);
605        if ($opt{octets}) {
606          ${$opt{octets}} = "\x{FFFD}"; # relacement character
607        }
608      };
609    
610      my $wrapped_char_stream = $get_wrapper->($char_stream);
611      $wrapped_char_stream->onerror ($char_onerror);
612    
613      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
614      my $return;
615      try {
616        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
617      } catch Whatpm::HTML::RestartParser with {
618        ## NOTE: Invoked after {change_encoding}.
619    
620        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
621          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
622          !!!parse-error (type => 'chardecode:fallback',
623                          level => $self->{level}->{uncertain},
624                          #text => $self->{input_encoding},
625                          line => 1, column => 1,
626                          layer => 'encode');
627        } elsif (not ($e_status &
628                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
629          $self->{input_encoding} = $charset->get_iana_name;
630          !!!parse-error (type => 'chardecode:no error',
631                          text => $self->{input_encoding},
632                          level => $self->{level}->{uncertain},
633                          line => 1, column => 1,
634                          layer => 'encode');
635        } else {
636          $self->{input_encoding} = $charset->get_iana_name;
637        }
638        $self->{confident} = 1;
639    
640        $wrapped_char_stream = $get_wrapper->($char_stream);
641        $wrapped_char_stream->onerror ($char_onerror);
642    
643        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
644      };
645      return $return;
646    } # parse_byte_stream
647    
648    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
649    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
650    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
651    ## because the core part of our HTML parser expects a string of character,
652    ## not a string of bytes or code units or anything which might contain a BOM.
653    ## Therefore, any parser interface that accepts a string of bytes,
654    ## such as |parse_byte_string| in this module, must ensure that it does
655    ## strip the BOM and never strip any ZWNBSP.
656    
657    sub parse_char_string ($$$;$$) {
658      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
659      my $self = shift;
660      my $s = ref $_[0] ? $_[0] : \($_[0]);
661      require Whatpm::Charset::DecodeHandle;
662      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
663      return $self->parse_char_stream ($input, @_[1..$#_]);
664    } # parse_char_string
665    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
666    
667    sub parse_char_stream ($$$;$$) {
668      my $self = ref $_[0] ? shift : shift->new;
669      my $input = $_[0];
670    $self->{document} = $_[1];    $self->{document} = $_[1];
671      @{$self->{document}->child_nodes} = ();
672    
673    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
674    
675    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
676    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
677    my $column = 0;        if defined $self->{input_encoding};
678    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
679    
680      $self->{line_prev} = $self->{line} = 1;
681      $self->{column_prev} = -1;
682      $self->{column} = 0;
683      $self->{set_nc} = sub {
684      my $self = shift;      my $self = shift;
685    
686      pop @{$self->{prev_input_character}};      my $char = '';
687      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
688          $char = $self->{next_nc};
689          delete $self->{next_nc};
690          $self->{nc} = ord $char;
691        } else {
692          $self->{char_buffer} = '';
693          $self->{char_buffer_pos} = 0;
694    
695      $self->{next_input_character} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
696      $self->{next_input_character} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
697      $column++;        if ($count) {
698            $self->{line_prev} = $self->{line};
699            $self->{column_prev} = $self->{column};
700            $self->{column}++;
701            $self->{nc}
702                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
703            return;
704          }
705    
706          if ($input->read ($char, 1)) {
707            $self->{nc} = ord $char;
708          } else {
709            $self->{nc} = -1;
710            return;
711          }
712        }
713    
714        ($self->{line_prev}, $self->{column_prev})
715            = ($self->{line}, $self->{column});
716        $self->{column}++;
717            
718      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
719        $line++;        !!!cp ('j1');
720        $column = 0;        $self->{line}++;
721      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
722        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
723        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
724        $line++;  ## TODO: support for abort/streaming
725        $column = 0;        my $next = '';
726      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
727        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
728      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
729          $self->{nc} = 0x000A; # LF # MUST
730          $self->{line}++;
731          $self->{column} = 0;
732        } elsif ($self->{nc} == 0x0000) { # NULL
733          !!!cp ('j4');
734        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
735        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
736      }      }
737    };    };
738    $self->{prev_input_character} = [-1, -1, -1];  
739    $self->{next_input_character} = -1;    $self->{read_until} = sub {
740        #my ($scalar, $specials_range, $offset) = @_;
741        return 0 if defined $self->{next_nc};
742    
743        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
744        my $offset = $_[2] || 0;
745    
746        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
747          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
748          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
749            substr ($_[0], $offset)
750                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
751            my $count = $+[0] - $-[0];
752            if ($count) {
753              $self->{column} += $count;
754              $self->{char_buffer_pos} += $count;
755              $self->{line_prev} = $self->{line};
756              $self->{column_prev} = $self->{column} - 1;
757              $self->{nc} = -1;
758            }
759            return $count;
760          } else {
761            return 0;
762          }
763        } else {
764          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
765          if ($count) {
766            $self->{column} += $count;
767            $self->{line_prev} = $self->{line};
768            $self->{column_prev} = $self->{column} - 1;
769            $self->{nc} = -1;
770          }
771          return $count;
772        }
773      }; # $self->{read_until}
774    
775    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
776      my (%opt) = @_;      my (%opt) = @_;
777      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
778        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
779        warn "Parse error ($opt{type}) at line $line column $column\n";
780    };    };
781    $self->{parse_error} = sub {    $self->{parse_error} = sub {
782      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
783    };    };
784    
785      my $char_onerror = sub {
786        my (undef, $type, %opt) = @_;
787        !!!parse-error (layer => 'encode',
788                        line => $self->{line}, column => $self->{column} + 1,
789                        %opt, type => $type);
790      }; # $char_onerror
791    
792      if ($_[3]) {
793        $input = $_[3]->($input);
794        $input->onerror ($char_onerror);
795      } else {
796        $input->onerror ($char_onerror) unless defined $input->onerror;
797      }
798    
799    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
800    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
801    $self->_construct_tree;    $self->_construct_tree;
802    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
803    
804      delete $self->{parse_error}; # remove loop
805    
806    return $self->{document};    return $self->{document};
807  } # parse_string  } # parse_char_stream
808    
809  sub new ($) {  sub new ($) {
810    my $class = shift;    my $class = shift;
811    my $self = bless {}, $class;    my $self = bless {
812    $self->{set_next_input_character} = sub {      level => {must => 'm',
813      $self->{next_input_character} = -1;                should => 's',
814                  warn => 'w',
815                  info => 'i',
816                  uncertain => 'u'},
817      }, $class;
818      $self->{set_nc} = sub {
819        $self->{nc} = -1;
820    };    };
821    $self->{parse_error} = sub {    $self->{parse_error} = sub {
822      #      #
823    };    };
824      $self->{change_encoding} = sub {
825        # if ($_[0] is a supported encoding) {
826        #   run "change the encoding" algorithm;
827        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
828        # }
829      };
830      $self->{application_cache_selection} = sub {
831        #
832      };
833    return $self;    return $self;
834  } # new  } # new
835    
# Line 159  sub CDATA_CONTENT_MODEL () { CM_LIMITED_ Line 842  sub CDATA_CONTENT_MODEL () { CM_LIMITED_
842  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
843  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
844    
845    sub DATA_STATE () { 0 }
846    #sub ENTITY_DATA_STATE () { 1 }
847    sub TAG_OPEN_STATE () { 2 }
848    sub CLOSE_TAG_OPEN_STATE () { 3 }
849    sub TAG_NAME_STATE () { 4 }
850    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
851    sub ATTRIBUTE_NAME_STATE () { 6 }
852    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
853    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
854    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
855    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
856    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
857    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
858    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
859    sub COMMENT_START_STATE () { 14 }
860    sub COMMENT_START_DASH_STATE () { 15 }
861    sub COMMENT_STATE () { 16 }
862    sub COMMENT_END_STATE () { 17 }
863    sub COMMENT_END_DASH_STATE () { 18 }
864    sub BOGUS_COMMENT_STATE () { 19 }
865    sub DOCTYPE_STATE () { 20 }
866    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
867    sub DOCTYPE_NAME_STATE () { 22 }
868    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
869    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
870    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
871    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
872    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
873    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
874    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
875    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
876    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
877    sub BOGUS_DOCTYPE_STATE () { 32 }
878    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
879    sub SELF_CLOSING_START_TAG_STATE () { 34 }
880    sub CDATA_SECTION_STATE () { 35 }
881    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
882    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
883    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
884    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
885    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
886    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
887    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
888    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
889    ## NOTE: "Entity data state", "entity in attribute value state", and
890    ## "consume a character reference" algorithm are jointly implemented
891    ## using the following six states:
892    sub ENTITY_STATE () { 44 }
893    sub ENTITY_HASH_STATE () { 45 }
894    sub NCR_NUM_STATE () { 46 }
895    sub HEXREF_X_STATE () { 47 }
896    sub HEXREF_HEX_STATE () { 48 }
897    sub ENTITY_NAME_STATE () { 49 }
898    sub PCDATA_STATE () { 50 } # "data state" in the spec
899    
900    sub DOCTYPE_TOKEN () { 1 }
901    sub COMMENT_TOKEN () { 2 }
902    sub START_TAG_TOKEN () { 3 }
903    sub END_TAG_TOKEN () { 4 }
904    sub END_OF_FILE_TOKEN () { 5 }
905    sub CHARACTER_TOKEN () { 6 }
906    
907    sub AFTER_HTML_IMS () { 0b100 }
908    sub HEAD_IMS ()       { 0b1000 }
909    sub BODY_IMS ()       { 0b10000 }
910    sub BODY_TABLE_IMS () { 0b100000 }
911    sub TABLE_IMS ()      { 0b1000000 }
912    sub ROW_IMS ()        { 0b10000000 }
913    sub BODY_AFTER_IMS () { 0b100000000 }
914    sub FRAME_IMS ()      { 0b1000000000 }
915    sub SELECT_IMS ()     { 0b10000000000 }
916    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
917        ## NOTE: "in foreign content" insertion mode is special; it is combined
918        ## with the secondary insertion mode.  In this parser, they are stored
919        ## together in the bit-or'ed form.
920    
921    ## NOTE: "initial" and "before html" insertion modes have no constants.
922    
923    ## NOTE: "after after body" insertion mode.
924    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
925    
926    ## NOTE: "after after frameset" insertion mode.
927    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
928    
929    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
930    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
931    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
932    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
933    sub IN_BODY_IM () { BODY_IMS }
934    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
935    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
936    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
937    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
938    sub IN_TABLE_IM () { TABLE_IMS }
939    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
940    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
941    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
942    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
943    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
944    sub IN_COLUMN_GROUP_IM () { 0b10 }
945    
946  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
947    
948  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
949    my $self = shift;    my $self = shift;
950    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
951      #$self->{s_kwd}; # state keyword - initialized when used
952      #$self->{entity__value}; # initialized when used
953      #$self->{entity__match}; # initialized when used
954    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
955    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
956    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
957    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
958    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
959    $self->{char} = [];    delete $self->{self_closing};
960    # $self->{next_input_character}    $self->{char_buffer} = '';
961      $self->{char_buffer_pos} = 0;
962      $self->{nc} = -1; # next input character
963      #$self->{next_nc}
964    !!!next-input-character;    !!!next-input-character;
965    $self->{token} = [];    $self->{token} = [];
966    # $self->{escape}    # $self->{escape}
967  } # _initialize_tokenizer  } # _initialize_tokenizer
968    
969  ## A token has:  ## A token has:
970  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
971  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
972  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
973  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
974  ##   ->{system_identifier} (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
975  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{sysid} (DOCTYPE_TOKEN)
976  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
977  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
978    ##        ->{name}
979    ##        ->{value}
980    ##        ->{has_reference} == 1 or 0
981    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
982    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
983    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
984    ##     while the token is pushed back to the stack.
985    
986  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
987    
# Line 194  sub _initialize_tokenizer ($) { Line 991  sub _initialize_tokenizer ($) {
991  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
992  ## and removed from the list.  ## and removed from the list.
993    
994    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
995    ## (This requirement was dropped from HTML5 spec, unfortunately.)
996    
997    my $is_space = {
998      0x0009 => 1, # CHARACTER TABULATION (HT)
999      0x000A => 1, # LINE FEED (LF)
1000      #0x000B => 0, # LINE TABULATION (VT)
1001      0x000C => 1, # FORM FEED (FF)
1002      #0x000D => 1, # CARRIAGE RETURN (CR)
1003      0x0020 => 1, # SPACE (SP)
1004    };
1005    
1006  sub _get_next_token ($) {  sub _get_next_token ($) {
1007    my $self = shift;    my $self = shift;
1008    
1009      if ($self->{self_closing}) {
1010        !!!parse-error (type => 'nestc', token => $self->{ct});
1011        ## NOTE: The |self_closing| flag is only set by start tag token.
1012        ## In addition, when a start tag token is emitted, it is always set to
1013        ## |ct|.
1014        delete $self->{self_closing};
1015      }
1016    
1017    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1018        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1019      return shift @{$self->{token}};      return shift @{$self->{token}};
1020    }    }
1021    
1022    A: {    A: {
1023      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1024        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1025          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA  
1026            $self->{state} = 'entity data';        if ($self->{nc} == 0x0026) { # &
1027            !!!cp (0.1);
1028            ## NOTE: In the spec, the tokenizer is switched to the
1029            ## "entity data state".  In this implementation, the tokenizer
1030            ## is switched to the |ENTITY_STATE|, which is an implementation
1031            ## of the "consume a character reference" algorithm.
1032            $self->{entity_add} = -1;
1033            $self->{prev_state} = DATA_STATE;
1034            $self->{state} = ENTITY_STATE;
1035            !!!next-input-character;
1036            redo A;
1037          } elsif ($self->{nc} == 0x003C) { # <
1038            !!!cp (0.2);
1039            $self->{state} = TAG_OPEN_STATE;
1040            !!!next-input-character;
1041            redo A;
1042          } elsif ($self->{nc} == -1) {
1043            !!!cp (0.3);
1044            !!!emit ({type => END_OF_FILE_TOKEN,
1045                      line => $self->{line}, column => $self->{column}});
1046            last A; ## TODO: ok?
1047          } else {
1048            !!!cp (0.4);
1049            #
1050          }
1051    
1052          # Anything else
1053          my $token = {type => CHARACTER_TOKEN,
1054                       data => chr $self->{nc},
1055                       line => $self->{line}, column => $self->{column},
1056                      };
1057          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1058    
1059          ## Stay in the state.
1060          !!!next-input-character;
1061          !!!emit ($token);
1062          redo A;
1063        } elsif ($self->{state} == DATA_STATE) {
1064          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1065          if ($self->{nc} == 0x0026) { # &
1066            $self->{s_kwd} = '';
1067            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1068                not $self->{escape}) {
1069              !!!cp (1);
1070              ## NOTE: In the spec, the tokenizer is switched to the
1071              ## "entity data state".  In this implementation, the tokenizer
1072              ## is switched to the |ENTITY_STATE|, which is an implementation
1073              ## of the "consume a character reference" algorithm.
1074              $self->{entity_add} = -1;
1075              $self->{prev_state} = DATA_STATE;
1076              $self->{state} = ENTITY_STATE;
1077            !!!next-input-character;            !!!next-input-character;
1078            redo A;            redo A;
1079          } else {          } else {
1080              !!!cp (2);
1081            #            #
1082          }          }
1083        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1084          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1085            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1086              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1087                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1088                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1089                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1090              }              $self->{s_kwd} = '--';
1091                #
1092              } elsif ($self->{s_kwd} eq '---') {
1093                !!!cp (4);
1094                $self->{s_kwd} = '--';
1095                #
1096              } else {
1097                !!!cp (5);
1098                #
1099            }            }
1100          }          }
1101                    
1102          #          #
1103        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1104            if (length $self->{s_kwd}) {
1105              !!!cp (5.1);
1106              $self->{s_kwd} .= '!';
1107              #
1108            } else {
1109              !!!cp (5.2);
1110              #$self->{s_kwd} = '';
1111              #
1112            }
1113            #
1114          } elsif ($self->{nc} == 0x003C) { # <
1115          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1116              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1117               not $self->{escape})) {               not $self->{escape})) {
1118            $self->{state} = 'tag open';            !!!cp (6);
1119              $self->{state} = TAG_OPEN_STATE;
1120            !!!next-input-character;            !!!next-input-character;
1121            redo A;            redo A;
1122          } else {          } else {
1123              !!!cp (7);
1124              $self->{s_kwd} = '';
1125            #            #
1126          }          }
1127        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1128          if ($self->{escape} and          if ($self->{escape} and
1129              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1130            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1131                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
1132              delete $self->{escape};              delete $self->{escape};
1133              } else {
1134                !!!cp (9);
1135            }            }
1136            } else {
1137              !!!cp (10);
1138          }          }
1139                    
1140            $self->{s_kwd} = '';
1141          #          #
1142        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1143          !!!emit ({type => 'end-of-file'});          !!!cp (11);
1144            $self->{s_kwd} = '';
1145            !!!emit ({type => END_OF_FILE_TOKEN,
1146                      line => $self->{line}, column => $self->{column}});
1147          last A; ## TODO: ok?          last A; ## TODO: ok?
1148          } else {
1149            !!!cp (12);
1150            $self->{s_kwd} = '';
1151            #
1152        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
   
       !!!emit ($token);  
1153    
1154        redo A;        # Anything else
1155      } elsif ($self->{state} eq 'entity data') {        my $token = {type => CHARACTER_TOKEN,
1156        ## (cannot happen in CDATA state)                     data => chr $self->{nc},
1157                             line => $self->{line}, column => $self->{column},
1158        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);                    };
1159          if ($self->{read_until}->($token->{data}, q[-!<>&],
1160        $self->{state} = 'data';                                  length $token->{data})) {
1161        # next-input-character is already done          $self->{s_kwd} = '';
1162          }
1163        unless (defined $token) {  
1164          !!!emit ({type => 'character', data => '&'});        ## Stay in the data state.
1165          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1166            !!!cp (13);
1167            $self->{state} = PCDATA_STATE;
1168        } else {        } else {
1169          !!!emit ($token);          !!!cp (14);
1170            ## Stay in the state.
1171        }        }
1172          !!!next-input-character;
1173          !!!emit ($token);
1174        redo A;        redo A;
1175      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1176        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1177          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1178              !!!cp (15);
1179            !!!next-input-character;            !!!next-input-character;
1180            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1181            redo A;            redo A;
1182            } elsif ($self->{nc} == 0x0021) { # !
1183              !!!cp (15.1);
1184              $self->{s_kwd} = '<' unless $self->{escape};
1185              #
1186          } else {          } else {
1187            ## reconsume            !!!cp (16);
1188            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1189          }          }
1190    
1191            ## reconsume
1192            $self->{state} = DATA_STATE;
1193            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1194                      line => $self->{line_prev},
1195                      column => $self->{column_prev},
1196                     });
1197            redo A;
1198        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1199          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1200            $self->{state} = 'markup declaration open';            !!!cp (17);
1201              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1202            !!!next-input-character;            !!!next-input-character;
1203            redo A;            redo A;
1204          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1205            $self->{state} = 'close tag open';            !!!cp (18);
1206              $self->{state} = CLOSE_TAG_OPEN_STATE;
1207            !!!next-input-character;            !!!next-input-character;
1208            redo A;            redo A;
1209          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1210                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1211            $self->{current_token}            !!!cp (19);
1212              = {type => 'start tag',            $self->{ct}
1213                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1214            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1215                   line => $self->{line_prev},
1216                   column => $self->{column_prev}};
1217              $self->{state} = TAG_NAME_STATE;
1218            !!!next-input-character;            !!!next-input-character;
1219            redo A;            redo A;
1220          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1221                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1222            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1223                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1224            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1225                                        line => $self->{line_prev},
1226                                        column => $self->{column_prev}};
1227              $self->{state} = TAG_NAME_STATE;
1228            !!!next-input-character;            !!!next-input-character;
1229            redo A;            redo A;
1230          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1231            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1232            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1233                              line => $self->{line_prev},
1234                              column => $self->{column_prev});
1235              $self->{state} = DATA_STATE;
1236            !!!next-input-character;            !!!next-input-character;
1237    
1238            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1239                        line => $self->{line_prev},
1240                        column => $self->{column_prev},
1241                       });
1242    
1243            redo A;            redo A;
1244          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1245            !!!parse-error (type => 'pio');            !!!cp (22);
1246            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1247            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1248                              column => $self->{column_prev});
1249              $self->{state} = BOGUS_COMMENT_STATE;
1250              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1251                                        line => $self->{line_prev},
1252                                        column => $self->{column_prev},
1253                                       };
1254              ## $self->{nc} is intentionally left as is
1255            redo A;            redo A;
1256          } else {          } else {
1257            !!!parse-error (type => 'bare stago');            !!!cp (23);
1258            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1259                              line => $self->{line_prev},
1260                              column => $self->{column_prev});
1261              $self->{state} = DATA_STATE;
1262            ## reconsume            ## reconsume
1263    
1264            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1265                        line => $self->{line_prev},
1266                        column => $self->{column_prev},
1267                       });
1268    
1269            redo A;            redo A;
1270          }          }
1271        } else {        } else {
1272          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1273        }        }
1274      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1275        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1276          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_input_character};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = 'data';  
1277    
1278                !!!emit ({type => 'character', data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1279            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1280                redo A;          if (defined $self->{last_stag_name}) {
1281              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1282            }            $self->{s_kwd} = '';
1283            push @next_char, $self->{next_input_character};            ## Reconsume.
1284                    redo A;
           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} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = 'data';  
             !!!emit ({type => 'character', data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1285          } else {          } else {
1286            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1287            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1288            $self->{state} = 'data';            !!!cp (28);
1289            !!!emit ({type => 'character', data => '</'});            $self->{state} = DATA_STATE;
1290              ## Reconsume.
1291              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1292                        line => $l, column => $c,
1293                       });
1294            redo A;            redo A;
1295          }          }
1296        }        }
1297          
1298        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1299            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1300          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1301                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1302          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1303          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1304          redo A;                 line => $l, column => $c};
1305        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1306                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1307          $self->{current_token} = {type => 'end tag',          redo A;
1308                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1309          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1310          !!!next-input-character;          !!!cp (30);
1311          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1312        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1313          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1314          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1315            !!!next-input-character;
1316            redo A;
1317          } elsif ($self->{nc} == 0x003E) { # >
1318            !!!cp (31);
1319            !!!parse-error (type => 'empty end tag',
1320                            line => $self->{line_prev}, ## "<" in "</>"
1321                            column => $self->{column_prev} - 1);
1322            $self->{state} = DATA_STATE;
1323          !!!next-input-character;          !!!next-input-character;
1324          redo A;          redo A;
1325        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1326            !!!cp (32);
1327          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1328          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1329          # reconsume          # reconsume
1330    
1331          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1332                      line => $l, column => $c,
1333                     });
1334    
1335          redo A;          redo A;
1336        } else {        } else {
1337            !!!cp (33);
1338          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1339          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1340          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1341          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1342                                      column => $self->{column_prev} - 1,
1343                                     };
1344            ## NOTE: $self->{nc} is intentionally left as is.
1345            ## Although the "anything else" case of the spec not explicitly
1346            ## states that the next input character is to be reconsumed,
1347            ## it will be included to the |data| of the comment token
1348            ## generated from the bogus end tag, as defined in the
1349            ## "bogus comment state" entry.
1350            redo A;
1351          }
1352        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1353          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1354          if (length $ch) {
1355            my $CH = $ch;
1356            $ch =~ tr/a-z/A-Z/;
1357            my $nch = chr $self->{nc};
1358            if ($nch eq $ch or $nch eq $CH) {
1359              !!!cp (24);
1360              ## Stay in the state.
1361              $self->{s_kwd} .= $nch;
1362              !!!next-input-character;
1363              redo A;
1364            } else {
1365              !!!cp (25);
1366              $self->{state} = DATA_STATE;
1367              ## Reconsume.
1368              !!!emit ({type => CHARACTER_TOKEN,
1369                        data => '</' . $self->{s_kwd},
1370                        line => $self->{line_prev},
1371                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1372                       });
1373              redo A;
1374            }
1375          } else { # after "<{tag-name}"
1376            unless ($is_space->{$self->{nc}} or
1377                    {
1378                     0x003E => 1, # >
1379                     0x002F => 1, # /
1380                     -1 => 1, # EOF
1381                    }->{$self->{nc}}) {
1382              !!!cp (26);
1383              ## Reconsume.
1384              $self->{state} = DATA_STATE;
1385              !!!emit ({type => CHARACTER_TOKEN,
1386                        data => '</' . $self->{s_kwd},
1387                        line => $self->{line_prev},
1388                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1389                       });
1390              redo A;
1391            } else {
1392              !!!cp (27);
1393              $self->{ct}
1394                  = {type => END_TAG_TOKEN,
1395                     tag_name => $self->{last_stag_name},
1396                     line => $self->{line_prev},
1397                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1398              $self->{state} = TAG_NAME_STATE;
1399              ## Reconsume.
1400              redo A;
1401            }
1402        }        }
1403      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1404        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1405            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
1406            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1407            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1408            $self->{next_input_character} == 0x0020) { # SP          redo A;
1409          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
1410          !!!next-input-character;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1411          redo A;            !!!cp (35);
1412        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{last_stag_name} = $self->{ct}->{tag_name};
1413          if ($self->{current_token}->{type} eq 'start tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1414            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1415            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1416              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1417            }            #  !!! cp (36);
1418              #  !!! parse-error (type => 'end tag attribute');
1419              #} else {
1420                !!!cp (37);
1421              #}
1422          } else {          } else {
1423            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1424          }          }
1425          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1426          !!!next-input-character;          !!!next-input-character;
1427    
1428          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1429    
1430          redo A;          redo A;
1431        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1432                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1433          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1434            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1435            # start tag or end tag            # start tag or end tag
1436          ## Stay in this state          ## Stay in this state
1437          !!!next-input-character;          !!!next-input-character;
1438          redo A;          redo A;
1439        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1440          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1441          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1442            $self->{current_token}->{first_start_tag}            !!!cp (39);
1443                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1444            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1445            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1446            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1447              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1448            }            #  !!! cp (40);
1449              #  !!! parse-error (type => 'end tag attribute');
1450              #} else {
1451                !!!cp (41);
1452              #}
1453          } else {          } else {
1454            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1455          }          }
1456          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1457          # reconsume          # reconsume
1458    
1459          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1460    
1461          redo A;          redo A;
1462        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1463            !!!cp (42);
1464            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1465          !!!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  
1466          redo A;          redo A;
1467        } else {        } else {
1468          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1469            $self->{ct}->{tag_name} .= chr $self->{nc};
1470            # start tag or end tag            # start tag or end tag
1471          ## Stay in the state          ## Stay in the state
1472          !!!next-input-character;          !!!next-input-character;
1473          redo A;          redo A;
1474        }        }
1475      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1476        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1477            $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  
1478          ## Stay in the state          ## Stay in the state
1479          !!!next-input-character;          !!!next-input-character;
1480          redo A;          redo A;
1481        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1482          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1483            $self->{current_token}->{first_start_tag}            !!!cp (46);
1484                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1485            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1486            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1487            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1488                !!!cp (47);
1489              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1490              } else {
1491                !!!cp (48);
1492            }            }
1493          } else {          } else {
1494            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1495          }          }
1496          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1497          !!!next-input-character;          !!!next-input-character;
1498    
1499          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1500    
1501          redo A;          redo A;
1502        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1503                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1504          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1505                                value => ''};          $self->{ca}
1506          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1507                   value => '',
1508                   line => $self->{line}, column => $self->{column}};
1509            $self->{state} = ATTRIBUTE_NAME_STATE;
1510          !!!next-input-character;          !!!next-input-character;
1511          redo A;          redo A;
1512        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1513            !!!cp (50);
1514            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1515          !!!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  
1516          redo A;          redo A;
1517        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1518          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1519          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1520            $self->{current_token}->{first_start_tag}            !!!cp (52);
1521                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1522            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1523            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1524            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1525                !!!cp (53);
1526              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1527              } else {
1528                !!!cp (54);
1529            }            }
1530          } else {          } else {
1531            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1532          }          }
1533          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1534          # reconsume          # reconsume
1535    
1536          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1537    
1538          redo A;          redo A;
1539        } else {        } else {
1540          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1541                                value => ''};               0x0022 => 1, # "
1542          $self->{state} = 'attribute name';               0x0027 => 1, # '
1543                 0x003D => 1, # =
1544                }->{$self->{nc}}) {
1545              !!!cp (55);
1546              !!!parse-error (type => 'bad attribute name');
1547            } else {
1548              !!!cp (56);
1549            }
1550            $self->{ca}
1551                = {name => chr ($self->{nc}),
1552                   value => '',
1553                   line => $self->{line}, column => $self->{column}};
1554            $self->{state} = ATTRIBUTE_NAME_STATE;
1555          !!!next-input-character;          !!!next-input-character;
1556          redo A;          redo A;
1557        }        }
1558      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1559        my $before_leave = sub {        my $before_leave = sub {
1560          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1561              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1562            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1563            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1564          } else {            ## Discard $self->{ca} # MUST
1565            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1566              = $self->{current_attribute};            !!!cp (58);
1567              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1568                = $self->{ca};
1569          }          }
1570        }; # $before_leave        }; # $before_leave
1571    
1572        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1573            $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  
1574          $before_leave->();          $before_leave->();
1575          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1576          !!!next-input-character;          !!!next-input-character;
1577          redo A;          redo A;
1578        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1579            !!!cp (60);
1580          $before_leave->();          $before_leave->();
1581          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1582          !!!next-input-character;          !!!next-input-character;
1583          redo A;          redo A;
1584        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1585          $before_leave->();          $before_leave->();
1586          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1587            $self->{current_token}->{first_start_tag}            !!!cp (61);
1588                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1589            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1590          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (62);
1591            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1592            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1593              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1594            }            }
1595          } else {          } else {
1596            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1597          }          }
1598          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1599          !!!next-input-character;          !!!next-input-character;
1600    
1601          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1602    
1603          redo A;          redo A;
1604        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1605                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1606          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1607            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1608          ## Stay in the state          ## Stay in the state
1609          !!!next-input-character;          !!!next-input-character;
1610          redo A;          redo A;
1611        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1612            !!!cp (64);
1613          $before_leave->();          $before_leave->();
1614            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1615          !!!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  
1616          redo A;          redo A;
1617        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1618          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1619          $before_leave->();          $before_leave->();
1620          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1621            $self->{current_token}->{first_start_tag}            !!!cp (66);
1622                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1623            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1624            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1625            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1626                !!!cp (67);
1627              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1628              } else {
1629                ## NOTE: This state should never be reached.
1630                !!!cp (68);
1631            }            }
1632          } else {          } else {
1633            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1634          }          }
1635          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1636          # reconsume          # reconsume
1637    
1638          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1639    
1640          redo A;          redo A;
1641        } else {        } else {
1642          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1643                $self->{nc} == 0x0027) { # '
1644              !!!cp (69);
1645              !!!parse-error (type => 'bad attribute name');
1646            } else {
1647              !!!cp (70);
1648            }
1649            $self->{ca}->{name} .= chr ($self->{nc});
1650          ## Stay in the state          ## Stay in the state
1651          !!!next-input-character;          !!!next-input-character;
1652          redo A;          redo A;
1653        }        }
1654      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1655        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1656            $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  
1657          ## Stay in the state          ## Stay in the state
1658          !!!next-input-character;          !!!next-input-character;
1659          redo A;          redo A;
1660        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1661          $self->{state} = 'before attribute value';          !!!cp (72);
1662            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1663          !!!next-input-character;          !!!next-input-character;
1664          redo A;          redo A;
1665        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1666          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1667            $self->{current_token}->{first_start_tag}            !!!cp (73);
1668                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1669            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1670            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1671            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1672                !!!cp (74);
1673              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1674              } else {
1675                ## NOTE: This state should never be reached.
1676                !!!cp (75);
1677            }            }
1678          } else {          } else {
1679            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1680          }          }
1681          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1682          !!!next-input-character;          !!!next-input-character;
1683    
1684          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1685    
1686          redo A;          redo A;
1687        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1688                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1689          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1690                                value => ''};          $self->{ca}
1691          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1692                   value => '',
1693                   line => $self->{line}, column => $self->{column}};
1694            $self->{state} = ATTRIBUTE_NAME_STATE;
1695          !!!next-input-character;          !!!next-input-character;
1696          redo A;          redo A;
1697        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1698            !!!cp (77);
1699            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1700          !!!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');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1701          redo A;          redo A;
1702        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1703          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1704          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1705            $self->{current_token}->{first_start_tag}            !!!cp (79);
1706                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1707            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1708            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1709            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1710                !!!cp (80);
1711              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1712              } else {
1713                ## NOTE: This state should never be reached.
1714                !!!cp (81);
1715            }            }
1716          } else {          } else {
1717            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1718          }          }
1719          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1720          # reconsume          # reconsume
1721    
1722          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1723    
1724          redo A;          redo A;
1725        } else {        } else {
1726          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1727                                value => ''};              $self->{nc} == 0x0027) { # '
1728          $self->{state} = 'attribute name';            !!!cp (78);
1729              !!!parse-error (type => 'bad attribute name');
1730            } else {
1731              !!!cp (82);
1732            }
1733            $self->{ca}
1734                = {name => chr ($self->{nc}),
1735                   value => '',
1736                   line => $self->{line}, column => $self->{column}};
1737            $self->{state} = ATTRIBUTE_NAME_STATE;
1738          !!!next-input-character;          !!!next-input-character;
1739          redo A;                  redo A;        
1740        }        }
1741      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1742        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1743            $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        
1744          ## Stay in the state          ## Stay in the state
1745          !!!next-input-character;          !!!next-input-character;
1746          redo A;          redo A;
1747        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1748          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1749            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1750          !!!next-input-character;          !!!next-input-character;
1751          redo A;          redo A;
1752        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1753          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1754            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1755          ## reconsume          ## reconsume
1756          redo A;          redo A;
1757        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1758          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1759            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1760          !!!next-input-character;          !!!next-input-character;
1761          redo A;          redo A;
1762        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1763          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1764            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1765                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1766            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1767          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1768            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1769            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1770                !!!cp (88);
1771              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1772              } else {
1773                ## NOTE: This state should never be reached.
1774                !!!cp (89);
1775            }            }
1776          } else {          } else {
1777            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1778          }          }
1779          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1780          !!!next-input-character;          !!!next-input-character;
1781    
1782          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1783    
1784          redo A;          redo A;
1785        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1786          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1787          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1788            $self->{current_token}->{first_start_tag}            !!!cp (90);
1789                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1790            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1791            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1792            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1793                !!!cp (91);
1794              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1795              } else {
1796                ## NOTE: This state should never be reached.
1797                !!!cp (92);
1798            }            }
1799          } else {          } else {
1800            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1801          }          }
1802          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1803          ## reconsume          ## reconsume
1804    
1805          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1806    
1807          redo A;          redo A;
1808        } else {        } else {
1809          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1810          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1811              !!!parse-error (type => 'bad attribute value');
1812            } else {
1813              !!!cp (94);
1814            }
1815            $self->{ca}->{value} .= chr ($self->{nc});
1816            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1817          !!!next-input-character;          !!!next-input-character;
1818          redo A;          redo A;
1819        }        }
1820      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1821        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1822          $self->{state} = 'before attribute name';          !!!cp (95);
1823            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1824          !!!next-input-character;          !!!next-input-character;
1825          redo A;          redo A;
1826        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1827          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1828          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1829            ## "entity in attribute value state".  In this implementation, the
1830            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1831            ## implementation of the "consume a character reference" algorithm.
1832            $self->{prev_state} = $self->{state};
1833            $self->{entity_add} = 0x0022; # "
1834            $self->{state} = ENTITY_STATE;
1835          !!!next-input-character;          !!!next-input-character;
1836          redo A;          redo A;
1837        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1838          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1839          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1840            $self->{current_token}->{first_start_tag}            !!!cp (97);
1841                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1842            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1843            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1844            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1845                !!!cp (98);
1846              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1847              } else {
1848                ## NOTE: This state should never be reached.
1849                !!!cp (99);
1850            }            }
1851          } else {          } else {
1852            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1853          }          }
1854          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1855          ## reconsume          ## reconsume
1856    
1857          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1858    
1859          redo A;          redo A;
1860        } else {        } else {
1861          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1862            $self->{ca}->{value} .= chr ($self->{nc});
1863            $self->{read_until}->($self->{ca}->{value},
1864                                  q["&],
1865                                  length $self->{ca}->{value});
1866    
1867          ## Stay in the state          ## Stay in the state
1868          !!!next-input-character;          !!!next-input-character;
1869          redo A;          redo A;
1870        }        }
1871      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1872        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1873          $self->{state} = 'before attribute name';          !!!cp (101);
1874            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1875            !!!next-input-character;
1876            redo A;
1877          } elsif ($self->{nc} == 0x0026) { # &
1878            !!!cp (102);
1879            ## NOTE: In the spec, the tokenizer is switched to the
1880            ## "entity in attribute value state".  In this implementation, the
1881            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1882            ## implementation of the "consume a character reference" algorithm.
1883            $self->{entity_add} = 0x0027; # '
1884            $self->{prev_state} = $self->{state};
1885            $self->{state} = ENTITY_STATE;
1886          !!!next-input-character;          !!!next-input-character;
1887          redo A;          redo A;
1888        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = 'attribute value (single-quoted)';  
         $self->{state} = 'entity in attribute value';  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1889          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1890          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1891            $self->{current_token}->{first_start_tag}            !!!cp (103);
1892                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1893            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1894            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1895            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1896                !!!cp (104);
1897              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1898              } else {
1899                ## NOTE: This state should never be reached.
1900                !!!cp (105);
1901            }            }
1902          } else {          } else {
1903            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1904          }          }
1905          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1906          ## reconsume          ## reconsume
1907    
1908          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1909    
1910          redo A;          redo A;
1911        } else {        } else {
1912          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1913            $self->{ca}->{value} .= chr ($self->{nc});
1914            $self->{read_until}->($self->{ca}->{value},
1915                                  q['&],
1916                                  length $self->{ca}->{value});
1917    
1918          ## Stay in the state          ## Stay in the state
1919          !!!next-input-character;          !!!next-input-character;
1920          redo A;          redo A;
1921        }        }
1922      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1923        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1924            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1925            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1926            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1927            $self->{next_input_character} == 0x0020) { # SP          redo A;
1928          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1929          !!!next-input-character;          !!!cp (108);
1930          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1931        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1932          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1933          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1934          !!!next-input-character;          $self->{entity_add} = -1;
1935          redo A;          $self->{prev_state} = $self->{state};
1936        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1937          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1938            $self->{current_token}->{first_start_tag}          redo A;
1939                = not defined $self->{last_emitted_start_tag_name};        } elsif ($self->{nc} == 0x003E) { # >
1940            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1941          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (109);
1942              $self->{last_stag_name} = $self->{ct}->{tag_name};
1943            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1944            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1945            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1946                !!!cp (110);
1947              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1948              } else {
1949                ## NOTE: This state should never be reached.
1950                !!!cp (111);
1951            }            }
1952          } else {          } else {
1953            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1954          }          }
1955          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1956          !!!next-input-character;          !!!next-input-character;
1957    
1958          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1959    
1960          redo A;          redo A;
1961        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1962          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1963          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1964            $self->{current_token}->{first_start_tag}            !!!cp (112);
1965                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1966            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1967            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1968            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1969                !!!cp (113);
1970              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1971              } else {
1972                ## NOTE: This state should never be reached.
1973                !!!cp (114);
1974            }            }
1975          } else {          } else {
1976            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1977          }          }
1978          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1979          ## reconsume          ## reconsume
1980    
1981          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1982    
1983          redo A;          redo A;
1984        } else {        } else {
1985          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1986                 0x0022 => 1, # "
1987                 0x0027 => 1, # '
1988                 0x003D => 1, # =
1989                }->{$self->{nc}}) {
1990              !!!cp (115);
1991              !!!parse-error (type => 'bad attribute value');
1992            } else {
1993              !!!cp (116);
1994            }
1995            $self->{ca}->{value} .= chr ($self->{nc});
1996            $self->{read_until}->($self->{ca}->{value},
1997                                  q["'=& >],
1998                                  length $self->{ca}->{value});
1999    
2000          ## Stay in the state          ## Stay in the state
2001          !!!next-input-character;          !!!next-input-character;
2002          redo A;          redo A;
2003        }        }
2004      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2005        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        if ($is_space->{$self->{nc}}) {
2006            !!!cp (118);
2007            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2008            !!!next-input-character;
2009            redo A;
2010          } elsif ($self->{nc} == 0x003E) { # >
2011            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2012              !!!cp (119);
2013              $self->{last_stag_name} = $self->{ct}->{tag_name};
2014            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2015              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2016              if ($self->{ct}->{attributes}) {
2017                !!!cp (120);
2018                !!!parse-error (type => 'end tag attribute');
2019              } else {
2020                ## NOTE: This state should never be reached.
2021                !!!cp (121);
2022              }
2023            } else {
2024              die "$0: $self->{ct}->{type}: Unknown token type";
2025            }
2026            $self->{state} = DATA_STATE;
2027            !!!next-input-character;
2028    
2029        unless (defined $token) {          !!!emit ($self->{ct}); # start tag or end tag
2030          $self->{current_attribute}->{value} .= '&';  
2031            redo A;
2032          } elsif ($self->{nc} == 0x002F) { # /
2033            !!!cp (122);
2034            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2035            !!!next-input-character;
2036            redo A;
2037          } elsif ($self->{nc} == -1) {
2038            !!!parse-error (type => 'unclosed tag');
2039            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2040              !!!cp (122.3);
2041              $self->{last_stag_name} = $self->{ct}->{tag_name};
2042            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2043              if ($self->{ct}->{attributes}) {
2044                !!!cp (122.1);
2045                !!!parse-error (type => 'end tag attribute');
2046              } else {
2047                ## NOTE: This state should never be reached.
2048                !!!cp (122.2);
2049              }
2050            } else {
2051              die "$0: $self->{ct}->{type}: Unknown token type";
2052            }
2053            $self->{state} = DATA_STATE;
2054            ## Reconsume.
2055            !!!emit ($self->{ct}); # start tag or end tag
2056            redo A;
2057        } else {        } else {
2058          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2059          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2060            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2061            ## reconsume
2062            redo A;
2063        }        }
2064        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2065          if ($self->{nc} == 0x003E) { # >
2066            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2067              !!!cp ('124.2');
2068              !!!parse-error (type => 'nestc', token => $self->{ct});
2069              ## TODO: Different type than slash in start tag
2070              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2071              if ($self->{ct}->{attributes}) {
2072                !!!cp ('124.4');
2073                !!!parse-error (type => 'end tag attribute');
2074              } else {
2075                !!!cp ('124.5');
2076              }
2077              ## TODO: Test |<title></title/>|
2078            } else {
2079              !!!cp ('124.3');
2080              $self->{self_closing} = 1;
2081            }
2082    
2083        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2084        # 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  
2085    
2086            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2087    
2088            redo A;          redo A;
2089          } elsif ($self->{nc} == -1) {
2090            !!!parse-error (type => 'unclosed tag');
2091            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2092              !!!cp (124.7);
2093              $self->{last_stag_name} = $self->{ct}->{tag_name};
2094            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2095              if ($self->{ct}->{attributes}) {
2096                !!!cp (124.5);
2097                !!!parse-error (type => 'end tag attribute');
2098              } else {
2099                ## NOTE: This state should never be reached.
2100                !!!cp (124.6);
2101              }
2102          } else {          } else {
2103            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2104          }          }
2105        } # BC          $self->{state} = DATA_STATE;
2106      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2107            !!!emit ($self->{ct}); # start tag or end tag
2108            redo A;
2109          } else {
2110            !!!cp ('124.4');
2111            !!!parse-error (type => 'nestc');
2112            ## TODO: This error type is wrong.
2113            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2114            ## Reconsume.
2115            redo A;
2116          }
2117        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2118        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2119    
2120        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2121        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2122                
2123        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2124            !!!cp (124);
2125            $self->{state} = DATA_STATE;
2126          !!!next-input-character;          !!!next-input-character;
2127          push @next_char, $self->{next_input_character};  
2128          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2129            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2130            $self->{state} = 'comment start';        } elsif ($self->{nc} == -1) {
2131            !!!next-input-character;          !!!cp (125);
2132            redo A;          $self->{state} = DATA_STATE;
2133          }          ## reconsume
2134        } elsif ($self->{next_input_character} == 0x0044 or # D  
2135                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2136            redo A;
2137          } else {
2138            !!!cp (126);
2139            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2140            $self->{read_until}->($self->{ct}->{data},
2141                                  q[>],
2142                                  length $self->{ct}->{data});
2143    
2144            ## Stay in the state.
2145          !!!next-input-character;          !!!next-input-character;
2146          push @next_char, $self->{next_input_character};          redo A;
2147          if ($self->{next_input_character} == 0x004F or # O        }
2148              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2149            !!!next-input-character;        ## (only happen if PCDATA state)
2150            push @next_char, $self->{next_input_character};        
2151            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2152                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2153              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2154              push @next_char, $self->{next_input_character};          !!!next-input-character;
2155              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2156                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2157                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2158                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2159                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2160                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2161                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2162                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2163                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2164                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2165                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2166                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2167                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2168                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2169                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2170                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2171                      !!!next-input-character;          redo A;
2172                      redo A;        } else {
2173                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2174        }        }
2175    
2176        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2177        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2178        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2179        $self->{state} = 'bogus comment';        ## Reconsume.
2180          $self->{state} = BOGUS_COMMENT_STATE;
2181          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2182                                    line => $self->{line_prev},
2183                                    column => $self->{column_prev} - 1,
2184                                   };
2185        redo A;        redo A;
2186              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2187        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2188        ## 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);
2189      } elsif ($self->{state} eq 'comment start') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2190        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2191          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2192                                     };
2193            $self->{state} = COMMENT_START_STATE;
2194            !!!next-input-character;
2195            redo A;
2196          } else {
2197            !!!cp (128);
2198            !!!parse-error (type => 'bogus comment',
2199                            line => $self->{line_prev},
2200                            column => $self->{column_prev} - 2);
2201            $self->{state} = BOGUS_COMMENT_STATE;
2202            ## Reconsume.
2203            $self->{ct} = {type => COMMENT_TOKEN,
2204                                      data => '-',
2205                                      line => $self->{line_prev},
2206                                      column => $self->{column_prev} - 2,
2207                                     };
2208            redo A;
2209          }
2210        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2211          ## ASCII case-insensitive.
2212          if ($self->{nc} == [
2213                undef,
2214                0x004F, # O
2215                0x0043, # C
2216                0x0054, # T
2217                0x0059, # Y
2218                0x0050, # P
2219              ]->[length $self->{s_kwd}] or
2220              $self->{nc} == [
2221                undef,
2222                0x006F, # o
2223                0x0063, # c
2224                0x0074, # t
2225                0x0079, # y
2226                0x0070, # p
2227              ]->[length $self->{s_kwd}]) {
2228            !!!cp (131);
2229            ## Stay in the state.
2230            $self->{s_kwd} .= chr $self->{nc};
2231            !!!next-input-character;
2232            redo A;
2233          } elsif ((length $self->{s_kwd}) == 6 and
2234                   ($self->{nc} == 0x0045 or # E
2235                    $self->{nc} == 0x0065)) { # e
2236            !!!cp (129);
2237            $self->{state} = DOCTYPE_STATE;
2238            $self->{ct} = {type => DOCTYPE_TOKEN,
2239                                      quirks => 1,
2240                                      line => $self->{line_prev},
2241                                      column => $self->{column_prev} - 7,
2242                                     };
2243            !!!next-input-character;
2244            redo A;
2245          } else {
2246            !!!cp (132);        
2247            !!!parse-error (type => 'bogus comment',
2248                            line => $self->{line_prev},
2249                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2250            $self->{state} = BOGUS_COMMENT_STATE;
2251            ## Reconsume.
2252            $self->{ct} = {type => COMMENT_TOKEN,
2253                                      data => $self->{s_kwd},
2254                                      line => $self->{line_prev},
2255                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2256                                     };
2257            redo A;
2258          }
2259        } elsif ($self->{state} == MD_CDATA_STATE) {
2260          if ($self->{nc} == {
2261                '[' => 0x0043, # C
2262                '[C' => 0x0044, # D
2263                '[CD' => 0x0041, # A
2264                '[CDA' => 0x0054, # T
2265                '[CDAT' => 0x0041, # A
2266              }->{$self->{s_kwd}}) {
2267            !!!cp (135.1);
2268            ## Stay in the state.
2269            $self->{s_kwd} .= chr $self->{nc};
2270            !!!next-input-character;
2271            redo A;
2272          } elsif ($self->{s_kwd} eq '[CDATA' and
2273                   $self->{nc} == 0x005B) { # [
2274            !!!cp (135.2);
2275            $self->{ct} = {type => CHARACTER_TOKEN,
2276                                      data => '',
2277                                      line => $self->{line_prev},
2278                                      column => $self->{column_prev} - 7};
2279            $self->{state} = CDATA_SECTION_STATE;
2280            !!!next-input-character;
2281            redo A;
2282          } else {
2283            !!!cp (135.3);
2284            !!!parse-error (type => 'bogus comment',
2285                            line => $self->{line_prev},
2286                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2287            $self->{state} = BOGUS_COMMENT_STATE;
2288            ## Reconsume.
2289            $self->{ct} = {type => COMMENT_TOKEN,
2290                                      data => $self->{s_kwd},
2291                                      line => $self->{line_prev},
2292                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2293                                     };
2294            redo A;
2295          }
2296        } elsif ($self->{state} == COMMENT_START_STATE) {
2297          if ($self->{nc} == 0x002D) { # -
2298            !!!cp (137);
2299            $self->{state} = COMMENT_START_DASH_STATE;
2300          !!!next-input-character;          !!!next-input-character;
2301          redo A;          redo A;
2302        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2303            !!!cp (138);
2304          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2305          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2306          !!!next-input-character;          !!!next-input-character;
2307    
2308          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2309    
2310          redo A;          redo A;
2311        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2312            !!!cp (139);
2313          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2314          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2315          ## reconsume          ## reconsume
2316    
2317          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2318    
2319          redo A;          redo A;
2320        } else {        } else {
2321          $self->{current_token}->{data} # comment          !!!cp (140);
2322              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2323          $self->{state} = 'comment';              .= chr ($self->{nc});
2324            $self->{state} = COMMENT_STATE;
2325          !!!next-input-character;          !!!next-input-character;
2326          redo A;          redo A;
2327        }        }
2328      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2329        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2330          $self->{state} = 'comment end';          !!!cp (141);
2331            $self->{state} = COMMENT_END_STATE;
2332          !!!next-input-character;          !!!next-input-character;
2333          redo A;          redo A;
2334        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2335            !!!cp (142);
2336          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2337          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2338          !!!next-input-character;          !!!next-input-character;
2339    
2340          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2341    
2342          redo A;          redo A;
2343        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2344            !!!cp (143);
2345          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2346          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2347          ## reconsume          ## reconsume
2348    
2349          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2350    
2351          redo A;          redo A;
2352        } else {        } else {
2353          $self->{current_token}->{data} # comment          !!!cp (144);
2354              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2355          $self->{state} = 'comment';              .= '-' . chr ($self->{nc});
2356            $self->{state} = COMMENT_STATE;
2357          !!!next-input-character;          !!!next-input-character;
2358          redo A;          redo A;
2359        }        }
2360      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2361        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2362          $self->{state} = 'comment end dash';          !!!cp (145);
2363            $self->{state} = COMMENT_END_DASH_STATE;
2364          !!!next-input-character;          !!!next-input-character;
2365          redo A;          redo A;
2366        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2367            !!!cp (146);
2368          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2369          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2370          ## reconsume          ## reconsume
2371    
2372          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2373    
2374          redo A;          redo A;
2375        } else {        } else {
2376          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2377            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2378            $self->{read_until}->($self->{ct}->{data},
2379                                  q[-],
2380                                  length $self->{ct}->{data});
2381    
2382          ## Stay in the state          ## Stay in the state
2383          !!!next-input-character;          !!!next-input-character;
2384          redo A;          redo A;
2385        }        }
2386      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2387        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2388          $self->{state} = 'comment end';          !!!cp (148);
2389            $self->{state} = COMMENT_END_STATE;
2390          !!!next-input-character;          !!!next-input-character;
2391          redo A;          redo A;
2392        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2393            !!!cp (149);
2394          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2395          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2396          ## reconsume          ## reconsume
2397    
2398          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2399    
2400          redo A;          redo A;
2401        } else {        } else {
2402          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2403          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2404            $self->{state} = COMMENT_STATE;
2405          !!!next-input-character;          !!!next-input-character;
2406          redo A;          redo A;
2407        }        }
2408      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2409        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2410          $self->{state} = 'data';          !!!cp (151);
2411            $self->{state} = DATA_STATE;
2412          !!!next-input-character;          !!!next-input-character;
2413    
2414          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2415    
2416          redo A;          redo A;
2417        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2418          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2419          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2420                            line => $self->{line_prev},
2421                            column => $self->{column_prev});
2422            $self->{ct}->{data} .= '-'; # comment
2423          ## Stay in the state          ## Stay in the state
2424          !!!next-input-character;          !!!next-input-character;
2425          redo A;          redo A;
2426        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2427            !!!cp (153);
2428          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2429          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2430          ## reconsume          ## reconsume
2431    
2432          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2433    
2434          redo A;          redo A;
2435        } else {        } else {
2436          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2437          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2438          $self->{state} = 'comment';                          line => $self->{line_prev},
2439                            column => $self->{column_prev});
2440            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2441            $self->{state} = COMMENT_STATE;
2442          !!!next-input-character;          !!!next-input-character;
2443          redo A;          redo A;
2444        }        }
2445      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2446        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2447            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2448            $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';  
2449          !!!next-input-character;          !!!next-input-character;
2450          redo A;          redo A;
2451        } else {        } else {
2452            !!!cp (156);
2453          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2454          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2455          ## reconsume          ## reconsume
2456          redo A;          redo A;
2457        }        }
2458      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2459        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2460            $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  
2461          ## Stay in the state          ## Stay in the state
2462          !!!next-input-character;          !!!next-input-character;
2463          redo A;          redo A;
2464        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2465            !!!cp (158);
2466          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2467          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2468          !!!next-input-character;          !!!next-input-character;
2469    
2470          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2471    
2472          redo A;          redo A;
2473        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2474            !!!cp (159);
2475          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2476          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2477          ## reconsume          ## reconsume
2478    
2479          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2480    
2481          redo A;          redo A;
2482        } else {        } else {
2483          $self->{current_token}          !!!cp (160);
2484              = {type => 'DOCTYPE',          $self->{ct}->{name} = chr $self->{nc};
2485                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
                correct => 1};  
2486  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2487          $self->{state} = 'DOCTYPE name';          $self->{state} = DOCTYPE_NAME_STATE;
2488          !!!next-input-character;          !!!next-input-character;
2489          redo A;          redo A;
2490        }        }
2491      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2494            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
2495            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'after DOCTYPE name';  
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499          $self->{state} = 'data';          !!!cp (162);
2500            $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2507            !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          $self->{current_token}->{name}          !!!cp (164);
2518            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2519              .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} eq 'after 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);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2527          ## Stay in the state          ## Stay in the state
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->{state} = 'data';          !!!cp (166);
2532            $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2539            !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2554              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $self->{next_input_character} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x004C or # L  
                 $self->{next_input_character} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0049 or # I  
                   $self->{next_input_character} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x0043 or # C  
                     $self->{next_input_character} == 0x0063) { # c  
                   $self->{state} = 'before DOCTYPE public identifier';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2558          !!!next-input-character;          !!!next-input-character;
2559          if ($self->{next_input_character} == 0x0059 or # Y          redo A;
             $self->{next_input_character} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_input_character} == 0x0053 or # S  
               $self->{next_input_character} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0045 or # E  
                   $self->{next_input_character} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x004D or # M  
                     $self->{next_input_character} == 0x006D) { # m  
                   $self->{state} = 'before DOCTYPE system identifier';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2560        } else {        } 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;          !!!next-input-character;
2567          #          redo A;
2568        }        }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570          ## ASCII case-insensitive
2571          if ($self->{nc} == [
2572                undef,
2573                0x0055, # U
2574                0x0042, # B
2575                0x004C, # L
2576                0x0049, # I
2577              ]->[length $self->{s_kwd}] or
2578              $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2605        $self->{state} = 'bogus DOCTYPE';          ## Reconsume.
2606        # next-input-character is already done          redo A;
2607        redo A;        }
2608      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == SYSTEM_STATE) {
2609        if ({        ## ASCII case-insensitive
2610              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,        if ($self->{nc} == [
2611              #0x000D => 1, # HT, LF, VT, FF, SP, CR              undef,
2612            }->{$self->{next_input_character}}) {              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} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2655          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2656            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2661          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2662            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666            !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2677            !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688            !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2691    
2692            $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698          $self->{state} = 'after DOCTYPE public identifier';          !!!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;          !!!next-input-character;
2708    
2709            $self->{ct}->{quirks} = 1;
2710            !!!emit ($self->{ct}); # DOCTYPE
2711    
2712          redo A;          redo A;
2713        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2714            !!!cp (189);
2715          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2726              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2727                .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2738            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2742            !!!cp (192);
2743          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2746            !!!next-input-character;
2747    
2748            $self->{ct}->{quirks} = 1;
2749            !!!emit ($self->{ct}); # DOCTYPE
2750    
2751            redo A;
2752          } elsif ($self->{nc} == -1) {
2753            !!!cp (193);
2754            !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756            $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2765              .= chr $self->{next_input_character};          $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        }        }
2774      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ({        if ($is_space->{$self->{nc}}) {
2776              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2782          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2783            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2788          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2789            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793          $self->{state} = 'data';          !!!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
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    
2804          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812            !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!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 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ({        if ($is_space->{$self->{nc}}) {
2822              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2828          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2829            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2834          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2835            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839            !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!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    
2852          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860            !!!cp (206);
2861          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2862          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2863    
2864            $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2871            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872          !!!next-input-character;          !!!next-input-character;
2873          redo A;          redo A;
2874        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2875            !!!cp (208);
2876          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2879            !!!next-input-character;
2880    
2881            $self->{ct}->{quirks} = 1;
2882            !!!emit ($self->{ct}); # DOCTYPE
2883    
2884            redo A;
2885          } elsif ($self->{nc} == -1) {
2886            !!!cp (209);
2887            !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889            $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2898              .= chr $self->{next_input_character};          $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      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2910            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2914            !!!cp (212);
2915          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2918            !!!next-input-character;
2919    
2920            $self->{ct}->{quirks} = 1;
2921            !!!emit ($self->{ct}); # DOCTYPE
2922    
2923            redo A;
2924          } elsif ($self->{nc} == -1) {
2925            !!!cp (213);
2926            !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928            $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2937              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2938                .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
2948              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          $self->{state} = 'data';          !!!cp (216);
2954            $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2961            !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2963            $self->{state} = DATA_STATE;
         $self->{state} = 'data';  
2964          ## reconsume          ## reconsume
2965    
2966          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971            !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2974    
2975            $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981          $self->{state} = 'data';          !!!cp (219);
2982            $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2989            !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998            !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3012  } # _get_next_token          !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034  sub _tokenize_attempt_to_consume_an_entity ($$) {          ## Stay in the state.
3035    my ($self, $in_attr) = @_;          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039    if ({        ## ISSUE: "text tokens" in spec.
3040         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3042        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3043      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3044      ## No error          !!!next-input-character;
3045      return undef;          redo A;
3046    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3047      !!!next-input-character;          !!!cp (221.6);
3048      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3049          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3050        my $code;          ## Reconsume.
3051        X: {          redo A;
3052          my $x_char = $self->{next_input_character};        }
3053          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3055              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3056            $code ||= 0;          !!!next-input-character;
3057            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3058            $code += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3059            redo X;            !!!emit ($self->{ct}); # character
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3060          } else {          } else {
3061            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063          }          }
3064            redo A;
3065          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3067            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
3069            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!next-input-character;
3070            $code = 0xFFFD;          redo A;
3071          } elsif ($code == 0x000D) {        } else {
3072            !!!parse-error (type => 'CR character reference');          !!!cp (221.11);
3073            $code = 0x000A;          $self->{ct}->{data} .= ']]'; # character
3074          } elsif (0x80 <= $code and $code <= 0x9F) {          $self->{state} = CDATA_SECTION_STATE;
3075            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          ## Reconsume.
3076            $code = $c1_entity_char->{$code};          redo A;
3077          }        }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          return {type => 'character', data => chr $code};        if ($is_space->{$self->{nc}} or
3080        } # X            {
3081      } elsif (0x0030 <= $self->{next_input_character} and              0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082               $self->{next_input_character} <= 0x0039) { # 0..9              $self->{entity_add} => 1,
3083        my $code = $self->{next_input_character} - 0x0030;            }->{$self->{nc}}) {
3084        !!!next-input-character;          !!!cp (1001);
3085                  ## Don't consume
3086        while (0x0030 <= $self->{next_input_character} and          ## No error
3087                  $self->{next_input_character} <= 0x0039) { # 0..9          ## Return nothing.
3088          $code *= 10;          #
3089          $code += $self->{next_input_character} - 0x0030;        } elsif ($self->{nc} == 0x0023) { # #
3090                    !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105          !!!next-input-character;          !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112        }        }
3113    
3114        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116          ## that does not introduce a character reference, which would be
3117          ## appended to the parent element or the attribute value in later
3118          ## process of the tokenizer.
3119    
3120          if ($self->{prev_state} == DATA_STATE) {
3121            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149          !!!next-input-character;          !!!next-input-character;
3150            redo A;
3151        } else {        } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177          }
3178        } elsif ($self->{state} == NCR_NUM_STATE) {
3179          if (0x0030 <= $self->{nc} and
3180              $self->{nc} <= 0x0039) { # 0..9
3181            !!!cp (1012);
3182            $self->{s_kwd} *= 10;
3183            $self->{s_kwd} += $self->{nc} - 0x0030;
3184            
3185            ## Stay in the state.
3186            !!!next-input-character;
3187            redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
3189            !!!cp (1013);
3190            !!!next-input-character;
3191            #
3192          } else {
3193            !!!cp (1014);
3194          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203            !!!cp (1015);
3204            !!!parse-error (type => 'invalid character reference',
3205                            text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208          } elsif ($code > 0x10FFFF) {
3209            !!!cp (1016);
3210            !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $code = 0xFFFD;          $code = 0xFFFD;
3214          }
3215    
3216          if ($self->{prev_state} == DATA_STATE) {
3217            !!!cp (992);
3218            $self->{state} = $self->{prev_state};
3219            ## Reconsume.
3220            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221                      line => $l, column => $c,
3222                     });
3223            redo A;
3224          } else {
3225            !!!cp (991);
3226            $self->{ca}->{value} .= chr $code;
3227            $self->{ca}->{has_reference} = 1;
3228            $self->{state} = $self->{prev_state};
3229            ## Reconsume.
3230            redo A;
3231          }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237            !!!cp (990);
3238            $self->{state} = HEXREF_HEX_STATE;
3239            $self->{s_kwd} = 0;
3240            ## Reconsume.
3241            redo A;
3242          } else {
3243            !!!parse-error (type => 'bare hcro',
3244                            line => $self->{line_prev},
3245                            column => $self->{column_prev} - 2);
3246    
3247            ## NOTE: According to the spec algorithm, nothing is returned,
3248            ## and then "&#" followed by "X" or "x" is appended to the parent
3249            ## element or the attribute value in the later processing.
3250    
3251            if ($self->{prev_state} == DATA_STATE) {
3252              !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261            } else {
3262              !!!cp (989);
3263              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264              $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3317          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3322        }        }
3323          
3324        return {type => 'character', data => chr $code};        if ($self->{prev_state} == DATA_STATE) {
3325      } else {          !!!cp (988);
3326        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3327        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3328        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329        return undef;                    line => $l, column => $c,
3330      }                   });
3331    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3332              $self->{next_input_character} <= 0x005A) or        } else {
3333             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3334              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3335      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3336      !!!next-input-character;          $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338      my $value = $entity_name;          redo A;
3339      my $match = 0;        }
3340      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3344             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3345             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3346               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3347              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3348               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3349              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3350               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3351              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3352        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3353        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3354          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3355            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356            $match = 1;              $self->{entity__match} = 1;
3357            !!!next-input-character;              !!!next-input-character;
3358            last;              #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367          } else {          } else {
3368            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3369            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373              redo A;
3374          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3375        }        }
3376      }  
3377              my $data;
3378      if ($match > 0) {        my $has_ref;
3379        return {type => 'character', data => $value};        if ($self->{entity__match} > 0) {
3380      } elsif ($match < 0) {          !!!cp (1023);
3381        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3382        if ($in_attr and $match < -1) {          $has_ref = 1;
3383          return {type => 'character', data => '&'.$entity_name};          #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391            } else {
3392              !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396            }
3397        } else {        } else {
3398          return {type => 'character', data => $value};          !!!cp (1026);
3399            !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
3434      } else {      } else {
3435        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       return {type => 'character', data => '&'.$value};  
3436      }      }
3437    } else {    } # A  
3438      ## no characters are consumed  
3439      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3440      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 1780  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1806  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474        
3475    !!!next-token;    !!!next-token;
3476    
   $self->{insertion_mode} = 'before head';  
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
3482      ## NOTE: The "initial" insertion mode.
3483    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3484    
3485      ## NOTE: The "before html" insertion mode.
3486    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3487      $self->{insertion_mode} = BEFORE_HEAD_IM;
3488    
3489      ## NOTE: The "before head" insertion mode and so on.
3490    $self->_tree_construction_main;    $self->_tree_construction_main;
3491  } # _construct_tree  } # _construct_tree
3492    
3493  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3494    my $self = shift;    my $self = shift;
3495    
3496      ## NOTE: "initial" insertion mode
3497    
3498    INITIAL: {    INITIAL: {
3499      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
3500        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3501        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
3508            defined $token->{system_identifier}) {          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3512          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3513          } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521          } else {
3522            !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3530        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3535                
3536        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537            !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3604            "-//W3C//DTD W3 HTML//EN" => 1,            }
3605            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3606            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3607            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3610            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615                !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618                !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623              !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625            } else {
3626              !!!cp ('t9');
3627          }          }
3628          } else {
3629            !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638              !!!cp ('t11');
3639            } else {
3640              !!!cp ('t12');
3641          }          }
3642          } else {
3643            !!!cp ('t13');
3644        }        }
3645                
3646        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3647        !!!next-token;        !!!next-token;
3648        return;        return;
3649      } elsif ({      } elsif ({
3650                'start tag' => 1,                START_TAG_TOKEN, 1,
3651                'end tag' => 1,                END_TAG_TOKEN, 1,
3652                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
3653               }->{$token->{type}}) {               }->{$token->{type}}) {
3654        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3655          !!!parse-error (type => 'no DOCTYPE', token => $token);
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3658        ## reprocess        ## reprocess
3659          !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
3666            ## Stay in the phase            !!!cp ('t15');
3667              ## Stay in the insertion mode.
3668            !!!next-token;            !!!next-token;
3669            redo INITIAL;            redo INITIAL;
3670            } else {
3671              !!!cp ('t16');
3672          }          }
3673          } else {
3674            !!!cp ('t17');
3675        }        }
3676    
3677        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3678        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3679        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3680        ## reprocess        ## reprocess
3681        return;        return;
3682      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
3683          !!!cp ('t18');
3684        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3685        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3686                
3687        ## Stay in the phase.        ## Stay in the insertion mode.
3688        !!!next-token;        !!!next-token;
3689        redo INITIAL;        redo INITIAL;
3690      } else {      } else {
3691        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
3692      }      }
3693    } # INITIAL    } # INITIAL
3694    
3695      die "$0: _tree_construction_initial: This should be never reached";
3696  } # _tree_construction_initial  } # _tree_construction_initial
3697    
3698  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3699    my $self = shift;    my $self = shift;
3700    
3701      ## NOTE: "before html" insertion mode.
3702        
3703    B: {    B: {
3704        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3706            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the phase          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
3710          redo B;          redo B;
3711        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3712            !!!cp ('t20');
3713          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3714          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3715          ## Stay in the phase          ## Stay in the insertion mode.
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
3723              ## Stay in the phase              !!!cp ('t21');
3724                ## Stay in the insertion mode.
3725              !!!next-token;              !!!next-token;
3726              redo B;              redo B;
3727              } else {
3728                !!!cp ('t22');
3729            }            }
3730            } else {
3731              !!!cp ('t23');
3732          }          }
3733    
3734            $self->{application_cache_selection}->(undef);
3735    
3736          #          #
3737          } elsif ($token->{type} == START_TAG_TOKEN) {
3738            if ($token->{tag_name} eq 'html') {
3739              my $root_element;
3740              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741              $self->{document}->append_child ($root_element);
3742              push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745              if ($token->{attributes}->{manifest}) {
3746                !!!cp ('t24');
3747                $self->{application_cache_selection}
3748                    ->($token->{attributes}->{manifest}->{value});
3749                ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753              } else {
3754                !!!cp ('t25');
3755                $self->{application_cache_selection}->(undef);
3756              }
3757    
3758              !!!nack ('t25c');
3759    
3760              !!!next-token;
3761              return; ## Go to the "before head" insertion mode.
3762            } else {
3763              !!!cp ('t25.1');
3764              #
3765            }
3766        } elsif ({        } elsif ({
3767                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3768                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3769                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3770          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3771          #          #
3772        } else {        } else {
3773          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775        my $root_element; !!!create-element ($root_element, 'html');  
3776        $self->{document}->append_child ($root_element);      my $root_element;
3777        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778        ## reprocess      $self->{document}->append_child ($root_element);
3779        #redo B;      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780        return; ## Go to the main phase.  
3781        $self->{application_cache_selection}->(undef);
3782    
3783        ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785        return; ## Go to the "before head" insertion mode.
3786    
3787        ## ISSUE: There is an issue in the spec
3788    } # B    } # B
3789    
3790      die "$0: _tree_construction_root_element: This should never be reached";
3791  } # _tree_construction_root_element  } # _tree_construction_root_element
3792    
3793  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2043  sub _reset_insertion_mode ($) { Line 3802  sub _reset_insertion_mode ($) {
3802            
3803      ## Step 3      ## Step 3
3804      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
3805        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3806          $last = 1;          $last = 1;
3807          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3808            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3809                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3810              #          } else {
3811            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3812          }          }
3813        }        }
3814              
3815        ## Step 4..13        ## Step 4..14
3816        my $new_mode = {        my $new_mode;
3817                        select => 'in select',        if ($node->[1] & FOREIGN_EL) {
3818                        td => 'in cell',          !!!cp ('t28.1');
3819                        th => 'in cell',          ## NOTE: Strictly spaking, the line below only applies to MathML and
3820                        tr => 'in row',          ## SVG elements.  Currently the HTML syntax supports only MathML and
3821                        tbody => 'in table body',          ## SVG elements as foreigners.
3822                        thead => 'in table head',          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3823                        tfoot => 'in table foot',        } elsif ($node->[1] & TABLE_CELL_EL) {
3824                        caption => 'in caption',          if ($last) {
3825                        colgroup => 'in column group',            !!!cp ('t28.2');
3826                        table => 'in table',            #
3827                        head => 'in body', # not in head!          } else {
3828                        body => 'in body',            !!!cp ('t28.3');
3829                        frameset => 'in frameset',            $new_mode = IN_CELL_IM;
3830                       }->{$node->[1]};          }
3831          } else {
3832            !!!cp ('t28.4');
3833            $new_mode = {
3834                          select => IN_SELECT_IM,
3835                          ## NOTE: |option| and |optgroup| do not set
3836                          ## insertion mode to "in select" by themselves.
3837                          tr => IN_ROW_IM,
3838                          tbody => IN_TABLE_BODY_IM,
3839                          thead => IN_TABLE_BODY_IM,
3840                          tfoot => IN_TABLE_BODY_IM,
3841                          caption => IN_CAPTION_IM,
3842                          colgroup => IN_COLUMN_GROUP_IM,
3843                          table => IN_TABLE_IM,
3844                          head => IN_BODY_IM, # not in head!
3845                          body => IN_BODY_IM,
3846                          frameset => IN_FRAMESET_IM,
3847                         }->{$node->[0]->manakai_local_name};
3848          }
3849        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3850                
3851        ## Step 14        ## Step 15
3852        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3853          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3854            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3855              $self->{insertion_mode} = BEFORE_HEAD_IM;
3856          } else {          } else {
3857            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3858              !!!cp ('t30');
3859              $self->{insertion_mode} = AFTER_HEAD_IM;
3860          }          }
3861          return;          return;
3862          } else {
3863            !!!cp ('t31');
3864        }        }
3865                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3866        ## Step 16        ## Step 16
3867          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3868          
3869          ## Step 17
3870        $i--;        $i--;
3871        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3872                
3873        ## Step 17        ## Step 18
3874        redo S3;        redo S3;
3875      } # S3      } # S3
3876    
3877      die "$0: _reset_insertion_mode: This line should never be reached";
3878  } # _reset_insertion_mode  } # _reset_insertion_mode
3879    
3880  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3881    my $self = shift;    my $self = shift;
3882    
   my $previous_insertion_mode;  
   
3883    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3884    
3885    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2121  sub _tree_construction_main ($) { Line 3896  sub _tree_construction_main ($) {
3896      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3897      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3898        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3899            !!!cp ('t32');
3900          return;          return;
3901        }        }
3902      }      }
# Line 2135  sub _tree_construction_main ($) { Line 3911  sub _tree_construction_main ($) {
3911    
3912        ## Step 6        ## Step 6
3913        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3914            !!!cp ('t33_1');
3915          #          #
3916        } else {        } else {
3917          my $in_open_elements;          my $in_open_elements;
3918          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3919            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3920                !!!cp ('t33');
3921              $in_open_elements = 1;              $in_open_elements = 1;
3922              last OE;              last OE;
3923            }            }
3924          }          }
3925          if ($in_open_elements) {          if ($in_open_elements) {
3926              !!!cp ('t34');
3927            #            #
3928          } else {          } else {
3929              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3930              !!!cp ('t35');
3931            redo S4;            redo S4;
3932          }          }
3933        }        }
# Line 2169  sub _tree_construction_main ($) { Line 3950  sub _tree_construction_main ($) {
3950    
3951        ## Step 11        ## Step 11
3952        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3953            !!!cp ('t36');
3954          ## Step 7'          ## Step 7'
3955          $i++;          $i++;
3956          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3957                    
3958          redo S7;          redo S7;
3959        }        }
3960    
3961          !!!cp ('t37');
3962      } # S7      } # S7
3963    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3964    
3965    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3966      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3967        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3968            !!!cp ('t38');
3969          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3970          return;          return;
3971        }        }
3972      }      }
3973    
3974        !!!cp ('t39');
3975    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3976    
3977    my $parse_rcdata = sub ($$) {    my $insert;
3978      my ($content_model_flag, $insert) = @_;  
3979      my $parse_rcdata = sub ($) {
3980        my ($content_model_flag) = @_;
3981    
3982      ## Step 1      ## Step 1
3983      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3984      my $el;      my $el;
3985      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3986    
3987      ## Step 2      ## Step 2
3988      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3989    
3990      ## Step 3      ## Step 3
3991      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2204  sub _tree_construction_main ($) { Line 3993  sub _tree_construction_main ($) {
3993    
3994      ## Step 4      ## Step 4
3995      my $text = '';      my $text = '';
3996        !!!nack ('t40.1');
3997      !!!next-token;      !!!next-token;
3998      while ($token->{type} eq 'character') { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3999          !!!cp ('t40');
4000        $text .= $token->{data};        $text .= $token->{data};
4001        !!!next-token;        !!!next-token;
4002      }      }
4003    
4004      ## Step 5      ## Step 5
4005      if (length $text) {      if (length $text) {
4006          !!!cp ('t41');
4007        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
4008        $el->append_child ($text);        $el->append_child ($text);
4009      }      }
# Line 2220  sub _tree_construction_main ($) { Line 4012  sub _tree_construction_main ($) {
4012      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
4013    
4014      ## Step 7      ## Step 7
4015      if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
4016            $token->{tag_name} eq $start_tag_name) {
4017          !!!cp ('t42');
4018        ## Ignore the token        ## Ignore the token
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4019      } else {      } else {
4020        die "$0: $content_model_flag in parse_rcdata";        ## NOTE: An end-of-file token.
4021          if ($content_model_flag == CDATA_CONTENT_MODEL) {
4022            !!!cp ('t43');
4023            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4024          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4025            !!!cp ('t44');
4026            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4027          } else {
4028            die "$0: $content_model_flag in parse_rcdata";
4029          }
4030      }      }
4031      !!!next-token;      !!!next-token;
4032    }; # $parse_rcdata    }; # $parse_rcdata
4033    
4034    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
4035      my $script_el;      my $script_el;
4036      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4037      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4038    
4039      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4040      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4041            
4042      my $text = '';      my $text = '';
4043        !!!nack ('t45.1');
4044      !!!next-token;      !!!next-token;
4045      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
4046          !!!cp ('t45');
4047        $text .= $token->{data};        $text .= $token->{data};
4048        !!!next-token;        !!!next-token;
4049      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4050      if (length $text) {      if (length $text) {
4051          !!!cp ('t46');
4052        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4053      }      }
4054                                
4055      $self->{content_model} = PCDATA_CONTENT_MODEL;      $self->{content_model} = PCDATA_CONTENT_MODEL;
4056    
4057      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
4058          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4059          !!!cp ('t47');
4060        ## Ignore the token        ## Ignore the token
4061      } else {      } else {
4062        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4063          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4064        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4065        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4066      }      }
4067            
4068      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4069          !!!cp ('t49');
4070        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4071      } else {      } else {
4072          !!!cp ('t50');
4073        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4074        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4075    
# Line 2278  sub _tree_construction_main ($) { Line 4083  sub _tree_construction_main ($) {
4083      !!!next-token;      !!!next-token;
4084    }; # $script_start_tag    }; # $script_start_tag
4085    
4086      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4087      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4088      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4089    
4090    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4091      my $tag_name = shift;      my $end_tag_token = shift;
4092        my $tag_name = $end_tag_token->{tag_name};
4093    
4094        ## NOTE: The adoption agency algorithm (AAA).
4095    
4096      FET: {      FET: {
4097        ## Step 1        ## Step 1
4098        my $formatting_element;        my $formatting_element;
4099        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4100        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4101          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4102              !!!cp ('t52');
4103              last AFE;
4104            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4105                         eq $tag_name) {
4106              !!!cp ('t51');
4107            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4108            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4109            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4110          }          }
4111        } # AFE        } # AFE
4112        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4113          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4114            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4115          ## Ignore the token          ## Ignore the token
4116          !!!next-token;          !!!next-token;
4117          return;          return;
# Line 2307  sub _tree_construction_main ($) { Line 4123  sub _tree_construction_main ($) {
4123          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4124          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4125            if ($in_scope) {            if ($in_scope) {
4126                !!!cp ('t54');
4127              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4128              last INSCOPE;              last INSCOPE;
4129            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4130              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4131                !!!parse-error (type => 'unmatched end tag',
4132                                text => $token->{tag_name},
4133                                token => $end_tag_token);
4134              ## Ignore the token              ## Ignore the token
4135              !!!next-token;              !!!next-token;
4136              return;              return;
4137            }            }
4138          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4139                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4140            $in_scope = 0;            $in_scope = 0;
4141          }          }
4142        } # INSCOPE        } # INSCOPE
4143        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4144          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4145            !!!parse-error (type => 'unmatched end tag',
4146                            text => $token->{tag_name},
4147                            token => $end_tag_token);
4148          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4149          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4150          return;          return;
4151        }        }
4152        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4153          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4154            !!!parse-error (type => 'not closed',
4155                            text => $self->{open_elements}->[-1]->[0]
4156                                ->manakai_local_name,
4157                            token => $end_tag_token);
4158        }        }
4159                
4160        ## Step 2        ## Step 2
# Line 2337  sub _tree_construction_main ($) { Line 4162  sub _tree_construction_main ($) {
4162        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4163        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4164          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4165          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4166              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4167              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4168               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4169              !!!cp ('t59');
4170            $furthest_block = $node;            $furthest_block = $node;
4171            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4172          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4173              !!!cp ('t60');
4174            last OE;            last OE;
4175          }          }
4176        } # OE        } # OE
4177                
4178        ## Step 3        ## Step 3
4179        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4180            !!!cp ('t61');
4181          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4182          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4183          !!!next-token;          !!!next-token;
# Line 2362  sub _tree_construction_main ($) { Line 4190  sub _tree_construction_main ($) {
4190        ## Step 5        ## Step 5
4191        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4192        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4193            !!!cp ('t62');
4194          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4195        }        }
4196                
# Line 2384  sub _tree_construction_main ($) { Line 4213  sub _tree_construction_main ($) {
4213          S7S2: {          S7S2: {
4214            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4215              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4216                  !!!cp ('t63');
4217                $node_i_in_active = $_;                $node_i_in_active = $_;
4218                last S7S2;                last S7S2;
4219              }              }
# Line 2397  sub _tree_construction_main ($) { Line 4227  sub _tree_construction_main ($) {
4227                    
4228          ## Step 4          ## Step 4
4229          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4230              !!!cp ('t64');
4231            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4232          }          }
4233                    
4234          ## Step 5          ## Step 5
4235          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4236              !!!cp ('t65');
4237            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4238            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4239            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2419  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        } # S7          } # S7  
4252                
4253        ## Step 8        ## Step 8
4254        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4255            my $foster_parent_element;
4256            my $next_sibling;
4257            OE: for (reverse 0..$#{$self->{open_elements}}) {
4258              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4259                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4260                                 if (defined $parent and $parent->node_type == 1) {
4261                                   !!!cp ('t65.1');
4262                                   $foster_parent_element = $parent;
4263                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4264                                 } else {
4265                                   !!!cp ('t65.2');
4266                                   $foster_parent_element
4267                                     = $self->{open_elements}->[$_ - 1]->[0];
4268                                 }
4269                                 last OE;
4270                               }
4271                             } # OE
4272                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4273                               unless defined $foster_parent_element;
4274            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4275            $open_tables->[-1]->[1] = 1; # tainted
4276          } else {
4277            !!!cp ('t65.3');
4278            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4279          }
4280                
4281        ## Step 9        ## Step 9
4282        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2436  sub _tree_construction_main ($) { Line 4293  sub _tree_construction_main ($) {
4293        my $i;        my $i;
4294        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4295          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4296              !!!cp ('t66');
4297            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4298            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4299          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4300              !!!cp ('t67');
4301            $i = $_;            $i = $_;
4302          }          }
4303        } # AFE        } # AFE
# Line 2448  sub _tree_construction_main ($) { Line 4307  sub _tree_construction_main ($) {
4307        undef $i;        undef $i;
4308        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4309          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4310              !!!cp ('t68');
4311            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4312            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4313          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4314              !!!cp ('t69');
4315            $i = $_;            $i = $_;
4316          }          }
4317        } # OE        } # OE
# Line 2461  sub _tree_construction_main ($) { Line 4322  sub _tree_construction_main ($) {
4322      } # FET      } # FET
4323    }; # $formatting_end_tag    }; # $formatting_end_tag
4324    
4325    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4326      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4327    }; # $insert_to_current    }; # $insert_to_current
4328    
4329    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4330                         my $child = shift;      my $child = shift;
4331                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4332                              table => 1, tbody => 1, tfoot => 1,        # MUST
4333                              thead => 1, tr => 1,        my $foster_parent_element;
4334                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4335                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4336                           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') {  
4337                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4338                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4339                                   !!!cp ('t70');
4340                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4341                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4342                               } else {                               } else {
4343                                   !!!cp ('t71');
4344                                 $foster_parent_element                                 $foster_parent_element
4345                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4346                               }                               }
# Line 2491  sub _tree_construction_main ($) { Line 4351  sub _tree_construction_main ($) {
4351                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4352                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4353                             ($child, $next_sibling);                             ($child, $next_sibling);
4354                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4355                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4356                         }        !!!cp ('t72');
4357          $self->{open_elements}->[-1]->[0]->append_child ($child);
4358        }
4359    }; # $insert_to_foster    }; # $insert_to_foster
4360    
4361    my $in_body = sub {    B: while (1) {
4362      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
4363      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
4364        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4365          ## NOTE: This is an "as if in head" code clone        ## Ignore the token
4366          $script_start_tag->($insert);        ## Stay in the phase
4367          return;        !!!next-token;
4368        } elsif ($token->{tag_name} eq 'style') {        next B;
4369          ## NOTE: This is an "as if in head" code clone      } elsif ($token->{type} == START_TAG_TOKEN and
4370          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);               $token->{tag_name} eq 'html') {
4371          return;        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4372        } elsif ({          !!!cp ('t79');
4373                  base => 1, link => 1,          !!!parse-error (type => 'after html', text => 'html', token => $token);
4374                 }->{$token->{tag_name}}) {          $self->{insertion_mode} = AFTER_BODY_IM;
4375          ## NOTE: This is an "as if in head" code clone, only "-t" differs        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4376          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!cp ('t80');
4377          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          !!!parse-error (type => 'after html', text => 'html', token => $token);
4378          !!!next-token;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4379          return;        } else {
4380        } elsif ($token->{tag_name} eq 'meta') {          !!!cp ('t81');
4381          ## NOTE: This is an "as if in head" code clone, only "-t" differs        }
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
   
         unless ($self->{confident}) {  
           my $charset;  
           if ($token->{attributes}->{charset}) { ## TODO: And if supported  
             $charset = $token->{attributes}->{charset}->{value};  
           }  
           if ($token->{attributes}->{'http-equiv'}) {  
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
             if ($token->{attributes}->{'http-equiv'}->{value}  
                 =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=  
                     [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|  
                     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {  
               $charset = defined $1 ? $1 : defined $2 ? $2 : $3;  
             } ## TODO: And if supported  
           }  
           ## TODO: Change the encoding  
         }  
4382    
4383          !!!next-token;        !!!cp ('t82');
4384          return;        !!!parse-error (type => 'not first start tag', token => $token);
4385        } elsif ($token->{tag_name} eq 'title') {        my $top_el = $self->{open_elements}->[0]->[0];
4386          !!!parse-error (type => 'in body:title');        for my $attr_name (keys %{$token->{attributes}}) {
4387          ## NOTE: This is an "as if in head" code clone          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4388          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {            !!!cp ('t84');
4389            if (defined $self->{head_element}) {            $top_el->set_attribute_ns
4390              $self->{head_element}->append_child ($_[0]);              (undef, [undef, $attr_name],
4391            } else {               $token->{attributes}->{$attr_name}->{value});
             $insert->($_[0]);  
           }  
         });  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error (type => 'in body:body');  
                 
         if (@{$self->{open_elements}} == 1 or  
             $self->{open_elements}->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $self->{open_elements}->[1]->[0];  
           for my $attr_name (keys %{$token->{attributes}}) {  
             unless ($body_el->has_attribute_ns (undef, $attr_name)) {  
               $body_el->set_attribute_ns  
                 (undef, [undef, $attr_name],  
                  $token->{attributes}->{$attr_name}->{value});  
             }  
           }  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, p => 1, ul => 1,  
                 pre => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         if ($token->{tag_name} eq 'pre') {  
           !!!next-token;  
           if ($token->{type} eq 'character') {  
             $token->{data} =~ s/^\x0A//;  
             unless (length $token->{data}) {  
               !!!next-token;  
             }  
           }  
         } else {  
           !!!next-token;  
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => 'end tag', tag_name => 'p'};  
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
           !!!next-token;  
           return;  
4392          }          }
4393        } elsif ($token->{tag_name} eq 'li') {        }
4394          ## has a p element in scope        !!!nack ('t84.1');
4395          INSCOPE: for (reverse @{$self->{open_elements}}) {        !!!next-token;
4396            if ($_->[1] eq 'p') {        next B;
4397              !!!back-token;      } elsif ($token->{type} == COMMENT_TOKEN) {
4398              $token = {type => 'end tag', tag_name => 'p'};        my $comment = $self->{document}->create_comment ($token->{data});
4399              return;        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4400            } elsif ({          !!!cp ('t85');
4401                      table => 1, caption => 1, td => 1, th => 1,          $self->{document}->append_child ($comment);
4402                      button => 1, marquee => 1, object => 1, html => 1,        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4403                     }->{$_->[1]}) {          !!!cp ('t86');
4404              last INSCOPE;          $self->{open_elements}->[0]->[0]->append_child ($comment);
4405            }        } else {
4406          } # INSCOPE          !!!cp ('t87');
4407                      $self->{open_elements}->[-1]->[0]->append_child ($comment);
4408          ## Step 1        }
4409          my $i = -1;        !!!next-token;
4410          my $node = $self->{open_elements}->[$i];        next B;
4411          LI: {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4412            ## Step 2        if ($token->{type} == CHARACTER_TOKEN) {
4413            if ($node->[1] eq 'li') {          !!!cp ('t87.1');
4414              if ($i != -1) {          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model} = PLAINTEXT_CONTENT_MODEL;  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
4415          !!!next-token;          !!!next-token;
4416          return;          next B;
4417        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4418          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4419            my $node = $active_formatting_elements->[$i];               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4420            if ($node->[1] eq 'a') {              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4421              !!!parse-error (type => 'in a:a');              ($token->{tag_name} eq 'svg' and
4422                             $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4423              !!!back-token;            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4424              $token = {type => 'end tag', tag_name => 'a'};            !!!cp ('t87.2');
4425              $formatting_end_tag->($token->{tag_name});            #
4426                        } elsif ({
4427              AFE2: for (reverse 0..$#$active_formatting_elements) {                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4428                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4429                  splice @$active_formatting_elements, $_, 1;                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4430                  last AFE2;                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4431                }                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4432              } # AFE2                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4433              OE: for (reverse 0..$#{$self->{open_elements}}) {                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4434                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4435                  splice @{$self->{open_elements}}, $_, 1;                   }->{$token->{tag_name}}) {
4436                  last OE;            !!!cp ('t87.2');
4437                }            !!!parse-error (type => 'not closed',
4438              } # OE                            text => $self->{open_elements}->[-1]->[0]
4439              last AFE;                                ->manakai_local_name,
4440            } elsif ($node->[0] eq '#marker') {                            token => $token);
4441              last AFE;  
4442              pop @{$self->{open_elements}}
4443                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4444    
4445              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4446              ## Reprocess.
4447              next B;
4448            } else {
4449              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4450              my $tag_name = $token->{tag_name};
4451              if ($nsuri eq $SVG_NS) {
4452                $tag_name = {
4453                   altglyph => 'altGlyph',
4454                   altglyphdef => 'altGlyphDef',
4455                   altglyphitem => 'altGlyphItem',
4456                   animatecolor => 'animateColor',
4457                   animatemotion => 'animateMotion',
4458                   animatetransform => 'animateTransform',
4459                   clippath => 'clipPath',
4460                   feblend => 'feBlend',
4461                   fecolormatrix => 'feColorMatrix',
4462                   fecomponenttransfer => 'feComponentTransfer',
4463                   fecomposite => 'feComposite',
4464                   feconvolvematrix => 'feConvolveMatrix',
4465                   fediffuselighting => 'feDiffuseLighting',
4466                   fedisplacementmap => 'feDisplacementMap',
4467                   fedistantlight => 'feDistantLight',
4468                   feflood => 'feFlood',
4469                   fefunca => 'feFuncA',
4470                   fefuncb => 'feFuncB',
4471                   fefuncg => 'feFuncG',
4472                   fefuncr => 'feFuncR',
4473                   fegaussianblur => 'feGaussianBlur',
4474                   feimage => 'feImage',
4475                   femerge => 'feMerge',
4476                   femergenode => 'feMergeNode',
4477                   femorphology => 'feMorphology',
4478                   feoffset => 'feOffset',
4479                   fepointlight => 'fePointLight',
4480                   fespecularlighting => 'feSpecularLighting',
4481                   fespotlight => 'feSpotLight',
4482                   fetile => 'feTile',
4483                   feturbulence => 'feTurbulence',
4484                   foreignobject => 'foreignObject',
4485                   glyphref => 'glyphRef',
4486                   lineargradient => 'linearGradient',
4487                   radialgradient => 'radialGradient',
4488                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4489                   textpath => 'textPath',  
4490                }->{$tag_name} || $tag_name;
4491            }            }
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4492    
4493          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            ## "adjust SVG attributes" (SVG only) - done in insert-element-f
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
4494    
4495          !!!next-token;            ## "adjust foreign attributes" - done in insert-element-f
         return;  
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 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 'nobr') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
4496    
4497          ## has a |nobr| element in scope            !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'nobr') {  
             !!!parse-error (type => 'not closed:nobr');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'nobr'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         !!!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', ''];  
   
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!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);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         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';  
         }  
4498    
4499          ## NOTE: There is an "as if <br>" code clone.            if ($self->{self_closing}) {
4500          $reconstruct_active_formatting_elements->($insert_to_current);              pop @{$self->{open_elements}};
4501                        !!!ack ('t87.3');
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           my $form_attrs;  
           $form_attrs->{action} = $at->{action} if $at->{action};  
           my $prompt_attr = $at->{prompt};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           delete $at->{action};  
           delete $at->{prompt};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form',  
                          attributes => $form_attrs},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                        );  
           if ($prompt_attr) {  
             push @tokens, {type => 'character', data => $prompt_attr->{value}};  
4502            } else {            } else {
4503              push @tokens, {type => 'character',              !!!cp ('t87.4');
                            data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD  
             ## TODO: make this configurable  
4504            }            }
4505            push @tokens,  
                         {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 ($token->{tag_name} eq 'textarea') {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         ## TODO: $self->{form_element} if defined  
         $self->{content_model} = RCDATA_CONTENT_MODEL;  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         if ($token->{type} eq 'character') {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!next-token;  
           }  
         }  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
4506            !!!next-token;            !!!next-token;
4507              next B;
4508          }          }
4509          if (length $text) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4510            $el->manakai_append_text ($text);          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4511          }          !!!cp ('t87.5');
4512                    #
4513          $self->{content_model} = PCDATA_CONTENT_MODEL;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4514                    !!!cp ('t87.6');
4515          if ($token->{type} eq 'end tag' and          !!!parse-error (type => 'not closed',
4516              $token->{tag_name} eq $tag_name) {                          text => $self->{open_elements}->[-1]->[0]
4517            ## Ignore the token                              ->manakai_local_name,
4518          } else {                          token => $token);
4519            !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4520          }          pop @{$self->{open_elements}}
4521          !!!next-token;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4522          return;  
4523        } elsif ({          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4524                  iframe => 1,          ## Reprocess.
4525                  noembed => 1,          next B;
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         ## NOTE: There are two "as if in body" code clones.  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = 'in select';  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'in body:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
4526        } else {        } else {
4527          $reconstruct_active_formatting_elements->($insert_to_current);          die "$0: $token->{type}: Unknown token type";        
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
4528        }        }
4529      } 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') {  
           for (@{$self->{open_elements}}) {  
             unless ({  
                        dd => 1, dt => 1, li => 1, p => 1, td => 1,  
                        th => 1, tr => 1, body => 1, html => 1,  
                      tbody => 1, tfoot => 1, thead => 1,  
                     }->{$_->[1]}) {  
               !!!parse-error (type => 'not closed:'.$_->[1]);  
             }  
           }  
4530    
4531            $self->{insertion_mode} = 'after body';      if ($self->{insertion_mode} & HEAD_IMS) {
4532            !!!next-token;        if ($token->{type} == CHARACTER_TOKEN) {
4533            return;          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4534          } else {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4535            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.2');
4536            ## Ignore the token              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4537            !!!next-token;              #
           return;  
         }  
       } elsif ($token->{tag_name} eq 'html') {  
         if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
           }  
           $self->{insertion_mode} = 'after body';  
           ## reprocess  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 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}) {  
           if (defined $i) {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4538            } else {            } else {
4539              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.1');
4540            }              ## Ignore the token.
4541          }              #
           
         if (defined $i) {  
           splice @{$self->{open_elements}}, $i;  
         } elsif ($token->{tag_name} eq 'p') {  
           ## As if <p>, then reprocess the current token  
           my $el;  
           !!!create-element ($el, 'p');  
           $insert->($el);  
         }  
         $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,  
                  tbody => 1, tfoot=> 1, thead => 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;  
4542            }            }
4543          } # INSCOPE            unless (length $token->{data}) {
4544                        !!!cp ('t88');
4545          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {              !!!next-token;
4546            pop @{$self->{open_elements}};              next B;
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         undef $self->{form_element};  
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 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;  
4547            }            }
4548          } # INSCOPE  ## TODO: set $token->{column} appropriately
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4549          }          }
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ($token->{tag_name} eq 'br') {  
         !!!parse-error (type => 'unmatched end tag:br');  
4550    
4551          ## As if <br>          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552          $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t89');
4553                      ## As if <head>
4554          my $el;            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4555          !!!create-element ($el, 'br');            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4556          $insert->($el);            push @{$self->{open_elements}},
4557                          [$self->{head_element}, $el_category->{head}];
         ## Ignore the token.  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!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];  
4558    
4559          ## Step 2            ## Reprocess in the "in head" insertion mode...
4560          S2: {            pop @{$self->{open_elements}};
           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,  
                  tbody => 1, tfoot=> 1, thead => 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;  
4561    
4562              !!!next-token;            ## Reprocess in the "after head" insertion mode...
4563              last S2;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4564            } else {            !!!cp ('t90');
4565              ## Step 3            ## As if </noscript>
4566              if (not $formatting_category->{$node->[1]} and            pop @{$self->{open_elements}};
4567                  #not $phrasing_category->{$node->[1]} and            !!!parse-error (type => 'in noscript:#text', token => $token);
                 ($special_category->{$node->[1]} or  
                  $scoping_category->{$node->[1]})) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
           }  
             
           ## Step 4  
           $node_i--;  
           $node = $self->{open_elements}->[$node_i];  
4568                        
4569            ## Step 5;            ## Reprocess in the "in head" insertion mode...
4570            redo S2;            ## As if </head>
4571          } # S2            pop @{$self->{open_elements}};
         return;  
       }  
     }  
   }; # $in_body  
   
   B: {  
     if ($token->{type} eq 'DOCTYPE') {  
       !!!parse-error (type => 'DOCTYPE in the middle');  
       ## Ignore the token  
       ## Stay in the phase  
       !!!next-token;  
       redo B;  
     } elsif ($token->{type} eq 'end-of-file') {  
       if ($token->{insertion_mode} ne 'trailing end') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
              tbody => 1, tfoot=> 1, thead => 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]);  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
4572    
4573        ## Stop parsing            ## Reprocess in the "after head" insertion mode...
4574        last B;          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4575      } elsif ($token->{type} eq 'start tag' and            !!!cp ('t91');
4576               $token->{tag_name} eq 'html') {            pop @{$self->{open_elements}};
       if ($self->{insertion_mode} eq 'trailing end') {  
         ## Turn into the main phase  
         !!!parse-error (type => 'after html:html');  
         $self->{insertion_mode} = $previous_insertion_mode;  
       }  
4577    
4578  ## ISSUE: "aa<html>" is not a parse error.            ## Reprocess in the "after head" insertion mode...
4579  ## ISSUE: "<html>" in fragment is not a parse error.          } else {
4580        unless ($token->{first_start_tag}) {            !!!cp ('t92');
         !!!parse-error (type => 'not first start tag');  
       }  
       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});  
4581          }          }
4582        }  
4583        !!!next-token;          ## "after head" insertion mode
4584        redo B;          ## As if <body>
4585      } elsif ($token->{type} eq 'comment') {          !!!insert-element ('body',, $token);
4586        my $comment = $self->{document}->create_comment ($token->{data});          $self->{insertion_mode} = IN_BODY_IM;
4587        if ($self->{insertion_mode} eq 'trailing end') {          ## reprocess
4588          $self->{document}->append_child ($comment);          next B;
4589        } elsif ($self->{insertion_mode} eq 'after body') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4590          $self->{open_elements}->[0]->[0]->append_child ($comment);          if ($token->{tag_name} eq 'head') {
4591        } else {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4592          $self->{open_elements}->[-1]->[0]->append_child ($comment);              !!!cp ('t93');
4593        }              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4594        !!!next-token;              $self->{open_elements}->[-1]->[0]->append_child
4595        redo B;                  ($self->{head_element});
4596      } elsif ($self->{insertion_mode} eq 'before head') {              push @{$self->{open_elements}},
4597            if ($token->{type} eq 'character') {                  [$self->{head_element}, $el_category->{head}];
4598              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $self->{insertion_mode} = IN_HEAD_IM;
4599                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              !!!nack ('t93.1');
4600                unless (length $token->{data}) {              !!!next-token;
4601                  !!!next-token;              next B;
4602                  redo B;            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4603                }              !!!cp ('t93.2');
4604              }              !!!parse-error (type => 'after head', text => 'head',
4605              ## As if <head>                              token => $token);
4606              !!!create-element ($self->{head_element}, 'head');              ## Ignore the token
4607              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!nack ('t93.3');
4608              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!next-token;
4609              $self->{insertion_mode} = 'in head';              next B;
             ## reprocess  
             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 ({  
                  head => 1, body => 1, html => 1,  
                  p => 1, br => 1,  
                 }->{$token->{tag_name}}) {  
               ## 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 ## ISSUE: An issue in the spec.  
               !!!next-token;  
               redo B;  
             }  
4610            } else {            } else {
4611              die "$0: $token->{type}: Unknown type";              !!!cp ('t95');
4612                !!!parse-error (type => 'in head:head',
4613                                token => $token); # or in head noscript
4614                ## Ignore the token
4615                !!!nack ('t95.1');
4616                !!!next-token;
4617                next B;
4618            }            }
4619          } elsif ($self->{insertion_mode} eq 'in head' or          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620                   $self->{insertion_mode} eq 'in head noscript' or            !!!cp ('t96');
4621                   $self->{insertion_mode} eq 'after head') {            ## As if <head>
4622            if ($token->{type} eq 'character') {            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4623              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4624                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            push @{$self->{open_elements}},
4625                unless (length $token->{data}) {                [$self->{head_element}, $el_category->{head}];
4626                  !!!next-token;  
4627                  redo B;            $self->{insertion_mode} = IN_HEAD_IM;
4628              ## Reprocess in the "in head" insertion mode...
4629            } else {
4630              !!!cp ('t97');
4631            }
4632    
4633                if ($token->{tag_name} eq 'base') {
4634                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4635                    !!!cp ('t98');
4636                    ## As if </noscript>
4637                    pop @{$self->{open_elements}};
4638                    !!!parse-error (type => 'in noscript', text => 'base',
4639                                    token => $token);
4640                  
4641                    $self->{insertion_mode} = IN_HEAD_IM;
4642                    ## Reprocess in the "in head" insertion mode...
4643                  } else {
4644                    !!!cp ('t99');
4645                }                }
4646              }  
               
             #  
           } elsif ($token->{type} eq 'start tag') {  
             if ({base => ($self->{insertion_mode} eq 'in head' or  
                           $self->{insertion_mode} eq 'after head'),  
                  link => 1}->{$token->{tag_name}}) {  
4647                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4648                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4649                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t100');
4650                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4651                                    text => $token->{tag_name}, token => $token);
4652                    push @{$self->{open_elements}},
4653                        [$self->{head_element}, $el_category->{head}];
4654                  } else {
4655                    !!!cp ('t101');
4656                }                }
4657                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4658                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4659                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4660                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4661                  !!!nack ('t101.1');
4662                !!!next-token;                !!!next-token;
4663                redo B;                next B;
4664              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'link') {
4665                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4666                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4667                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t102');
4668                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4669                                    text => $token->{tag_name}, token => $token);
4670                    push @{$self->{open_elements}},
4671                        [$self->{head_element}, $el_category->{head}];
4672                  } else {
4673                    !!!cp ('t103');
4674                }                }
4675                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4676                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4677                  pop @{$self->{open_elements}} # <head>
4678                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4679                  !!!ack ('t103.1');
4680                  !!!next-token;
4681                  next B;
4682                } elsif ($token->{tag_name} eq 'meta') {
4683                  ## NOTE: There is a "as if in head" code clone.
4684                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4685                    !!!cp ('t104');
4686                    !!!parse-error (type => 'after head',
4687                                    text => $token->{tag_name}, token => $token);
4688                    push @{$self->{open_elements}},
4689                        [$self->{head_element}, $el_category->{head}];
4690                  } else {
4691                    !!!cp ('t105');
4692                  }
4693                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4694                  my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4695    
4696                unless ($self->{confident}) {                unless ($self->{confident}) {
4697                  my $charset;                  if ($token->{attributes}->{charset}) {
4698                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                    !!!cp ('t106');
4699                    $charset = $token->{attributes}->{charset}->{value};                    ## NOTE: Whether the encoding is supported or not is handled
4700                      ## in the {change_encoding} callback.
4701                      $self->{change_encoding}
4702                          ->($self, $token->{attributes}->{charset}->{value},
4703                             $token);
4704                      
4705                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4706                          ->set_user_data (manakai_has_reference =>
4707                                               $token->{attributes}->{charset}
4708                                                   ->{has_reference});
4709                    } elsif ($token->{attributes}->{content}) {
4710                      if ($token->{attributes}->{content}->{value}
4711                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4712                              [\x09\x0A\x0C\x0D\x20]*=
4713                              [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4714                              ([^"'\x09\x0A\x0C\x0D\x20]
4715                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4716                        !!!cp ('t107');
4717                        ## NOTE: Whether the encoding is supported or not is handled
4718                        ## in the {change_encoding} callback.
4719                        $self->{change_encoding}
4720                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4721                               $token);
4722                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4723                            ->set_user_data (manakai_has_reference =>
4724                                                 $token->{attributes}->{content}
4725                                                       ->{has_reference});
4726                      } else {
4727                        !!!cp ('t108');
4728                      }
4729                  }                  }
4730                  if ($token->{attributes}->{'http-equiv'}) {                } else {
4731                    ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.                  if ($token->{attributes}->{charset}) {
4732                    if ($token->{attributes}->{'http-equiv'}->{value}                    !!!cp ('t109');
4733                        =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4734                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                        ->set_user_data (manakai_has_reference =>
4735                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                                             $token->{attributes}->{charset}
4736                      $charset = defined $1 ? $1 : defined $2 ? $2 : $3;                                                 ->{has_reference});
4737                    } ## TODO: And if supported                  }
4738                    if ($token->{attributes}->{content}) {
4739                      !!!cp ('t110');
4740                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4741                          ->set_user_data (manakai_has_reference =>
4742                                               $token->{attributes}->{content}
4743                                                   ->{has_reference});
4744                  }                  }
                 ## TODO: Change the encoding  
4745                }                }
4746    
4747                ## TODO: Extracting |charset| from |meta|.                pop @{$self->{open_elements}} # <head>
4748                pop @{$self->{open_elements}}                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4749                    if $self->{insertion_mode} eq 'after head';                !!!ack ('t110.1');
4750                !!!next-token;                !!!next-token;
4751                redo B;                next B;
4752              } elsif ($token->{tag_name} eq 'title' and              } elsif ($token->{tag_name} eq 'title') {
4753                       $self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4754                ## NOTE: There is a "as if in head" code clone.                  !!!cp ('t111');
4755                if ($self->{insertion_mode} eq 'after head') {                  ## As if </noscript>
4756                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  pop @{$self->{open_elements}};
4757                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'in noscript', text => 'title',
4758                                    token => $token);
4759                  
4760                    $self->{insertion_mode} = IN_HEAD_IM;
4761                    ## Reprocess in the "in head" insertion mode...
4762                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4763                    !!!cp ('t112');
4764                    !!!parse-error (type => 'after head',
4765                                    text => $token->{tag_name}, token => $token);
4766                    push @{$self->{open_elements}},
4767                        [$self->{head_element}, $el_category->{head}];
4768                  } else {
4769                    !!!cp ('t113');
4770                }                }
4771    
4772                  ## NOTE: There is a "as if in head" code clone.
4773                my $parent = defined $self->{head_element} ? $self->{head_element}                my $parent = defined $self->{head_element} ? $self->{head_element}
4774                    : $self->{open_elements}->[-1]->[0];                    : $self->{open_elements}->[-1]->[0];
4775                $parse_rcdata->(RCDATA_CONTENT_MODEL,                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4776                                sub { $parent->append_child ($_[0]) });                pop @{$self->{open_elements}} # <head>
4777                pop @{$self->{open_elements}}                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4778                    if $self->{insertion_mode} eq 'after head';                next B;
4779                redo B;              } elsif ($token->{tag_name} eq 'style' or
4780              } elsif ($token->{tag_name} eq 'style') {                       $token->{tag_name} eq 'noframes') {
4781                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4782                ## insertion mode 'in head')                ## insertion mode IN_HEAD_IM)
4783                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4784                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4785                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t114');
4786                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4787                                    text => $token->{tag_name}, token => $token);
4788                    push @{$self->{open_elements}},
4789                        [$self->{head_element}, $el_category->{head}];
4790                  } else {
4791                    !!!cp ('t115');
4792                }                }
4793                $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4794                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4795                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4796                redo B;                next B;
4797              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4798                if ($self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4799                    !!!cp ('t116');
4800                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4801                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4802                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4803                    !!!nack ('t116.1');
4804                  !!!next-token;                  !!!next-token;
4805                  redo B;                  next B;
4806                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4807                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4808                    !!!parse-error (type => 'in noscript', text => 'noscript',
4809                                    token => $token);
4810                  ## Ignore the token                  ## Ignore the token
4811                    !!!nack ('t117.1');
4812                  !!!next-token;                  !!!next-token;
4813                  redo B;                  next B;
4814                } else {                } else {
4815                    !!!cp ('t118');
4816                  #                  #
4817                }                }
4818              } elsif ($token->{tag_name} eq 'head' and              } elsif ($token->{tag_name} eq 'script') {
4819                       $self->{insertion_mode} ne 'after head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4820                !!!parse-error (type => 'in head:head'); # or in head noscript                  !!!cp ('t119');
4821                ## Ignore the token                  ## As if </noscript>
4822                !!!next-token;                  pop @{$self->{open_elements}};
4823                redo B;                  !!!parse-error (type => 'in noscript', text => 'script',
4824              } elsif ($self->{insertion_mode} ne 'in head noscript' and                                  token => $token);
4825                       $token->{tag_name} eq 'script') {                
4826                if ($self->{insertion_mode} eq 'after head') {                  $self->{insertion_mode} = IN_HEAD_IM;
4827                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  ## Reprocess in the "in head" insertion mode...
4828                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4829                    !!!cp ('t120');
4830                    !!!parse-error (type => 'after head',
4831                                    text => $token->{tag_name}, token => $token);
4832                    push @{$self->{open_elements}},
4833                        [$self->{head_element}, $el_category->{head}];
4834                  } else {
4835                    !!!cp ('t121');
4836                }                }
4837    
4838                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4839                $script_start_tag->($insert_to_current);                $script_start_tag->();
4840                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4841                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4842                redo B;                next B;
4843              } elsif ($self->{insertion_mode} eq 'after head' and              } elsif ($token->{tag_name} eq 'body' or
                      $token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $self->{insertion_mode} = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'after head' and  
4844                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4845                !!!insert-element ('frameset', $token->{attributes});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4846                $self->{insertion_mode} = 'in frameset';                  !!!cp ('t122');
4847                    ## As if </noscript>
4848                    pop @{$self->{open_elements}};
4849                    !!!parse-error (type => 'in noscript',
4850                                    text => $token->{tag_name}, token => $token);
4851                    
4852                    ## Reprocess in the "in head" insertion mode...
4853                    ## As if </head>
4854                    pop @{$self->{open_elements}};
4855                    
4856                    ## Reprocess in the "after head" insertion mode...
4857                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4858                    !!!cp ('t124');
4859                    pop @{$self->{open_elements}};
4860                    
4861                    ## Reprocess in the "after head" insertion mode...
4862                  } else {
4863                    !!!cp ('t125');
4864                  }
4865    
4866                  ## "after head" insertion mode
4867                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4868                  if ($token->{tag_name} eq 'body') {
4869                    !!!cp ('t126');
4870                    $self->{insertion_mode} = IN_BODY_IM;
4871                  } elsif ($token->{tag_name} eq 'frameset') {
4872                    !!!cp ('t127');
4873                    $self->{insertion_mode} = IN_FRAMESET_IM;
4874                  } else {
4875                    die "$0: tag name: $self->{tag_name}";
4876                  }
4877                  !!!nack ('t127.1');
4878                !!!next-token;                !!!next-token;
4879                redo B;                next B;
4880              } else {              } else {
4881                  !!!cp ('t128');
4882                #                #
4883              }              }
4884            } elsif ($token->{type} eq 'end tag') {  
4885              if ($self->{insertion_mode} eq 'in head' and              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4886                  $token->{tag_name} eq 'head') {                !!!cp ('t129');
4887                  ## As if </noscript>
4888                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4889                $self->{insertion_mode} = 'after head';                !!!parse-error (type => 'in noscript:/',
4890                !!!next-token;                                text => $token->{tag_name}, token => $token);
4891                redo B;                
4892              } elsif ($self->{insertion_mode} eq 'in head noscript' and                ## Reprocess in the "in head" insertion mode...
4893                  $token->{tag_name} eq 'noscript') {                ## As if </head>
4894                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4895                $self->{insertion_mode} = 'in head';  
4896                !!!next-token;                ## Reprocess in the "after head" insertion mode...
4897                redo B;              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4898              } elsif ($self->{insertion_mode} eq 'in head' and                !!!cp ('t130');
4899                       {                ## As if </head>
4900                  pop @{$self->{open_elements}};
4901    
4902                  ## Reprocess in the "after head" insertion mode...
4903                } else {
4904                  !!!cp ('t131');
4905                }
4906    
4907                ## "after head" insertion mode
4908                ## As if <body>
4909                !!!insert-element ('body',, $token);
4910                $self->{insertion_mode} = IN_BODY_IM;
4911                ## reprocess
4912                !!!ack-later;
4913                next B;
4914              } elsif ($token->{type} == END_TAG_TOKEN) {
4915                if ($token->{tag_name} eq 'head') {
4916                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4917                    !!!cp ('t132');
4918                    ## As if <head>
4919                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4920                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4921                    push @{$self->{open_elements}},
4922                        [$self->{head_element}, $el_category->{head}];
4923    
4924                    ## Reprocess in the "in head" insertion mode...
4925                    pop @{$self->{open_elements}};
4926                    $self->{insertion_mode} = AFTER_HEAD_IM;
4927                    !!!next-token;
4928                    next B;
4929                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4930                    !!!cp ('t133');
4931                    ## As if </noscript>
4932                    pop @{$self->{open_elements}};
4933                    !!!parse-error (type => 'in noscript:/',
4934                                    text => 'head', token => $token);
4935                    
4936                    ## Reprocess in the "in head" insertion mode...
4937                    pop @{$self->{open_elements}};
4938                    $self->{insertion_mode} = AFTER_HEAD_IM;
4939                    !!!next-token;
4940                    next B;
4941                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4942                    !!!cp ('t134');
4943                    pop @{$self->{open_elements}};
4944                    $self->{insertion_mode} = AFTER_HEAD_IM;
4945                    !!!next-token;
4946                    next B;
4947                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4948                    !!!cp ('t134.1');
4949                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4950                                    token => $token);
4951                    ## Ignore the token
4952                    !!!next-token;
4953                    next B;
4954                  } else {
4955                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4956                  }
4957                } elsif ($token->{tag_name} eq 'noscript') {
4958                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4959                    !!!cp ('t136');
4960                    pop @{$self->{open_elements}};
4961                    $self->{insertion_mode} = IN_HEAD_IM;
4962                    !!!next-token;
4963                    next B;
4964                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4965                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4966                    !!!cp ('t137');
4967                    !!!parse-error (type => 'unmatched end tag',
4968                                    text => 'noscript', token => $token);
4969                    ## Ignore the token ## ISSUE: An issue in the spec.
4970                    !!!next-token;
4971                    next B;
4972                  } else {
4973                    !!!cp ('t138');
4974                    #
4975                  }
4976                } elsif ({
4977                        body => 1, html => 1,                        body => 1, html => 1,
                       p => 1, br => 1,  
4978                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4979                #                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
4980              } elsif ($self->{insertion_mode} eq 'in head noscript' and                    $self->{insertion_mode} == IN_HEAD_IM or
4981                       {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4982                        p => 1, br => 1,                  !!!cp ('t140');
4983                       }->{$token->{tag_name}}) {                  !!!parse-error (type => 'unmatched end tag',
4984                #                                  text => $token->{tag_name}, token => $token);
4985              } elsif ($self->{insertion_mode} ne 'after head') {                  ## Ignore the token
4986                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!next-token;
4987                    next B;
4988                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4989                    !!!cp ('t140.1');
4990                    !!!parse-error (type => 'unmatched end tag',
4991                                    text => $token->{tag_name}, token => $token);
4992                    ## Ignore the token
4993                    !!!next-token;
4994                    next B;
4995                  } else {
4996                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4997                  }
4998                } elsif ($token->{tag_name} eq 'p') {
4999                  !!!cp ('t142');
5000                  !!!parse-error (type => 'unmatched end tag',
5001                                  text => $token->{tag_name}, token => $token);
5002                ## Ignore the token                ## Ignore the token
5003                !!!next-token;                !!!next-token;
5004                redo B;                next B;
5005                } elsif ($token->{tag_name} eq 'br') {
5006                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5007                    !!!cp ('t142.2');
5008                    ## (before head) as if <head>, (in head) as if </head>
5009                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5010                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5011                    $self->{insertion_mode} = AFTER_HEAD_IM;
5012      
5013                    ## Reprocess in the "after head" insertion mode...
5014                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5015                    !!!cp ('t143.2');
5016                    ## As if </head>
5017                    pop @{$self->{open_elements}};
5018                    $self->{insertion_mode} = AFTER_HEAD_IM;
5019      
5020                    ## Reprocess in the "after head" insertion mode...
5021                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5022                    !!!cp ('t143.3');
5023                    ## ISSUE: Two parse errors for <head><noscript></br>
5024                    !!!parse-error (type => 'unmatched end tag',
5025                                    text => 'br', token => $token);
5026                    ## As if </noscript>
5027                    pop @{$self->{open_elements}};
5028                    $self->{insertion_mode} = IN_HEAD_IM;
5029    
5030                    ## Reprocess in the "in head" insertion mode...
5031                    ## As if </head>
5032                    pop @{$self->{open_elements}};
5033                    $self->{insertion_mode} = AFTER_HEAD_IM;
5034    
5035                    ## Reprocess in the "after head" insertion mode...
5036                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5037                    !!!cp ('t143.4');
5038                    #
5039                  } else {
5040                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5041                  }
5042    
5043                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5044                  !!!parse-error (type => 'unmatched end tag',
5045                                  text => 'br', token => $token);
5046                  ## Ignore the token
5047                  !!!next-token;
5048                  next B;
5049              } else {              } else {
5050                #                !!!cp ('t145');
5051                  !!!parse-error (type => 'unmatched end tag',
5052                                  text => $token->{tag_name}, token => $token);
5053                  ## Ignore the token
5054                  !!!next-token;
5055                  next B;
5056              }              }
           } else {  
             #  
           }  
5057    
5058            ## As if </head> or </noscript> or <body>              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5059            if ($self->{insertion_mode} eq 'in head') {                !!!cp ('t146');
5060              pop @{$self->{open_elements}};                ## As if </noscript>
5061              $self->{insertion_mode} = 'after head';                pop @{$self->{open_elements}};
5062            } elsif ($self->{insertion_mode} eq 'in head noscript') {                !!!parse-error (type => 'in noscript:/',
5063              pop @{$self->{open_elements}};                                text => $token->{tag_name}, token => $token);
5064              !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));                
5065              $self->{insertion_mode} = 'in head';                ## Reprocess in the "in head" insertion mode...
5066            } else { # 'after head'                ## As if </head>
5067              !!!insert-element ('body');                pop @{$self->{open_elements}};
5068              $self->{insertion_mode} = 'in body';  
5069            }                ## Reprocess in the "after head" insertion mode...
5070            ## reprocess              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5071            redo B;                !!!cp ('t147');
5072                  ## As if </head>
5073                  pop @{$self->{open_elements}};
5074    
5075                  ## Reprocess in the "after head" insertion mode...
5076                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5077    ## ISSUE: This case cannot be reached?
5078                  !!!cp ('t148');
5079                  !!!parse-error (type => 'unmatched end tag',
5080                                  text => $token->{tag_name}, token => $token);
5081                  ## Ignore the token ## ISSUE: An issue in the spec.
5082                  !!!next-token;
5083                  next B;
5084                } else {
5085                  !!!cp ('t149');
5086                }
5087    
5088                ## "after head" insertion mode
5089                ## As if <body>
5090                !!!insert-element ('body',, $token);
5091                $self->{insertion_mode} = IN_BODY_IM;
5092                ## reprocess
5093                next B;
5094          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5095            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5096              !!!cp ('t149.1');
5097    
5098              ## NOTE: As if <head>
5099              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5100              $self->{open_elements}->[-1]->[0]->append_child
5101                  ($self->{head_element});
5102              #push @{$self->{open_elements}},
5103              #    [$self->{head_element}, $el_category->{head}];
5104              #$self->{insertion_mode} = IN_HEAD_IM;
5105              ## NOTE: Reprocess.
5106    
5107              ## NOTE: As if </head>
5108              #pop @{$self->{open_elements}};
5109              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5110              ## NOTE: Reprocess.
5111              
5112              #
5113            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5114              !!!cp ('t149.2');
5115    
5116              ## NOTE: As if </head>
5117              pop @{$self->{open_elements}};
5118              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5119              ## NOTE: Reprocess.
5120    
5121              #
5122            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5123              !!!cp ('t149.3');
5124    
5125              !!!parse-error (type => 'in noscript:#eof', token => $token);
5126    
5127              ## As if </noscript>
5128              pop @{$self->{open_elements}};
5129              #$self->{insertion_mode} = IN_HEAD_IM;
5130              ## NOTE: Reprocess.
5131    
5132              ## NOTE: As if </head>
5133              pop @{$self->{open_elements}};
5134              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5135              ## NOTE: Reprocess.
5136    
5137              #
5138            } else {
5139              !!!cp ('t149.4');
5140              #
5141            }
5142    
5143            ## NOTE: As if <body>
5144            !!!insert-element ('body',, $token);
5145            $self->{insertion_mode} = IN_BODY_IM;
5146            ## NOTE: Reprocess.
5147            next B;
5148          } else {
5149            die "$0: $token->{type}: Unknown token type";
5150          }
5151    
5152            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
5153          } elsif ($self->{insertion_mode} eq 'in body' or      } elsif ($self->{insertion_mode} & BODY_IMS) {
5154                   $self->{insertion_mode} eq 'in cell' or            if ($token->{type} == CHARACTER_TOKEN) {
5155                   $self->{insertion_mode} eq 'in caption') {              !!!cp ('t150');
           if ($token->{type} eq 'character') {  
5156              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5157              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5158                            
5159              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5160    
5161              !!!next-token;              !!!next-token;
5162              redo B;              next B;
5163            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} == START_TAG_TOKEN) {
5164              if ({              if ({
5165                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5166                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5167                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5168                if ($self->{insertion_mode} eq 'in cell') {                if ($self->{insertion_mode} == IN_CELL_IM) {
5169                  ## have an element in table scope                  ## have an element in table scope
5170                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5171                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5172                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5173                      $tn = $node->[1];                      !!!cp ('t151');
5174                      last INSCOPE;  
5175                    } elsif ({                      ## Close the cell
5176                              table => 1, html => 1,                      !!!back-token; # <x>
5177                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5178                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5179                    }                                line => $token->{line},
5180                  } # INSCOPE                                column => $token->{column}};
5181                    unless (defined $tn) {                      next B;
5182                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5183                      ## Ignore the token                      !!!cp ('t152');
5184                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5185                      redo B;                      last;
5186                    }                    }
5187                    }
5188    
5189                    !!!cp ('t153');
5190                    !!!parse-error (type => 'start tag not allowed',
5191                        text => $token->{tag_name}, token => $token);
5192                    ## Ignore the token
5193                    !!!nack ('t153.1');
5194                    !!!next-token;
5195                    next B;
5196                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5197                    !!!parse-error (type => 'not closed', text => 'caption',
5198                                    token => $token);
5199                                    
5200                  ## Close the cell                  ## NOTE: As if </caption>.
                 !!!back-token; # <?>  
                 $token = {type => 'end tag', tag_name => $tn};  
                 redo B;  
               } elsif ($self->{insertion_mode} eq 'in caption') {  
                 !!!parse-error (type => 'not closed:caption');  
                   
                 ## As if </caption>  
5201                  ## have a table element in table scope                  ## have a table element in table scope
5202                  my $i;                  my $i;
5203                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5204                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5205                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5206                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5207                      last INSCOPE;                        !!!cp ('t155');
5208                    } elsif ({                        $i = $_;
5209                              table => 1, html => 1,                        last INSCOPE;
5210                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5211                      last INSCOPE;                        !!!cp ('t156');
5212                          last;
5213                        }
5214                    }                    }
5215    
5216                      !!!cp ('t157');
5217                      !!!parse-error (type => 'start tag not allowed',
5218                                      text => $token->{tag_name}, token => $token);
5219                      ## Ignore the token
5220                      !!!nack ('t157.1');
5221                      !!!next-token;
5222                      next B;
5223                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5224                                    
5225                  ## generate implied end tags                  ## generate implied end tags
5226                  if ({                  while ($self->{open_elements}->[-1]->[1]
5227                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5228                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5229                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token; # <?>  
                   $token = {type => 'end tag', tag_name => 'caption'};  
                   !!!back-token;  
                   $token = {type => 'end tag',  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5230                  }                  }
5231    
5232                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5233                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t159');
5234                      !!!parse-error (type => 'not closed',
5235                                      text => $self->{open_elements}->[-1]->[0]
5236                                          ->manakai_local_name,
5237                                      token => $token);
5238                    } else {
5239                      !!!cp ('t160');
5240                  }                  }
5241                                    
5242                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
5243                                    
5244                  $clear_up_to_marker->();                  $clear_up_to_marker->();
5245                                    
5246                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
5247                                    
5248                  ## reprocess                  ## reprocess
5249                  redo B;                  !!!ack-later;
5250                    next B;
5251                } else {                } else {
5252                    !!!cp ('t161');
5253                  #                  #
5254                }                }
5255              } else {              } else {
5256                  !!!cp ('t162');
5257                #                #
5258              }              }
5259            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5260              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5261                if ($self->{insertion_mode} eq 'in cell') {                if ($self->{insertion_mode} == IN_CELL_IM) {
5262                  ## have an element in table scope                  ## have an element in table scope
5263                  my $i;                  my $i;
5264                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5265                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5266                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5267                        !!!cp ('t163');
5268                      $i = $_;                      $i = $_;
5269                      last INSCOPE;                      last INSCOPE;
5270                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5271                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5272                      last INSCOPE;                      last INSCOPE;
5273                    }                    }
5274                  } # INSCOPE                  } # INSCOPE
5275                    unless (defined $i) {                    unless (defined $i) {
5276                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5277                        !!!parse-error (type => 'unmatched end tag',
5278                                        text => $token->{tag_name},
5279                                        token => $token);
5280                      ## Ignore the token                      ## Ignore the token
5281                      !!!next-token;                      !!!next-token;
5282                      redo B;                      next B;
5283                    }                    }
5284                                    
5285                  ## generate implied end tags                  ## generate implied end tags
5286                  if ({                  while ($self->{open_elements}->[-1]->[1]
5287                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5288                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5289                       th => ($token->{tag_name} eq 'td'),                    pop @{$self->{open_elements}};
                      tr => 1,  
                      tbody => 1, tfoot=> 1, thead => 1,  
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => 'end tag',  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5290                  }                  }
5291                    
5292                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5293                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          ne $token->{tag_name}) {
5294                      !!!cp ('t167');
5295                      !!!parse-error (type => 'not closed',
5296                                      text => $self->{open_elements}->[-1]->[0]
5297                                          ->manakai_local_name,
5298                                      token => $token);
5299                    } else {
5300                      !!!cp ('t168');
5301                  }                  }
5302                                    
5303                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
5304                                    
5305                  $clear_up_to_marker->();                  $clear_up_to_marker->();
5306                                    
5307                  $self->{insertion_mode} = 'in row';                  $self->{insertion_mode} = IN_ROW_IM;
5308                                    
5309                  !!!next-token;                  !!!next-token;
5310                  redo B;                  next B;
5311                } elsif ($self->{insertion_mode} eq 'in caption') {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5312                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t169');
5313                    !!!parse-error (type => 'unmatched end tag',
5314                                    text => $token->{tag_name}, token => $token);
5315                  ## Ignore the token                  ## Ignore the token
5316                  !!!next-token;                  !!!next-token;
5317                  redo B;                  next B;
5318                } else {                } else {
5319                    !!!cp ('t170');
5320                  #                  #
5321                }                }
5322              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5323                if ($self->{insertion_mode} eq 'in caption') {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5324                  ## have a table element in table scope                  ## have a table element in table scope
5325                  my $i;                  my $i;
5326                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5327                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5328                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5329                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5330                      last INSCOPE;                        !!!cp ('t171');
5331                    } elsif ({                        $i = $_;
5332                              table => 1, html => 1,                        last INSCOPE;
5333                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5334                      last INSCOPE;                        !!!cp ('t172');
5335                          last;
5336                        }
5337                    }                    }
5338    
5339                      !!!cp ('t173');
5340                      !!!parse-error (type => 'unmatched end tag',
5341                                      text => $token->{tag_name}, token => $token);
5342                      ## Ignore the token
5343                      !!!next-token;
5344                      next B;
5345                  } # INSCOPE                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5346                                    
5347                  ## generate implied end tags                  ## generate implied end tags
5348                  if ({                  while ($self->{open_elements}->[-1]->[1]
5349                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5350                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5351                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => 'end tag',  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5352                  }                  }
5353                                    
5354                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5355                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!cp ('t175');
5356                      !!!parse-error (type => 'not closed',
5357                                      text => $self->{open_elements}->[-1]->[0]
5358                                          ->manakai_local_name,
5359                                      token => $token);
5360                    } else {
5361                      !!!cp ('t176');
5362                  }                  }
5363                                    
5364                  splice @{$self->{open_elements}}, $i;                  splice @{$self->{open_elements}}, $i;
5365                                    
5366                  $clear_up_to_marker->();                  $clear_up_to_marker->();
5367                                    
5368                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
5369                                    
5370                  !!!next-token;                  !!!next-token;
5371                  redo B;                  next B;
5372                } elsif ($self->{insertion_mode} eq 'in cell') {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5373                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5374                    !!!parse-error (type => 'unmatched end tag',
5375                                    text => $token->{tag_name}, token => $token);
5376                  ## Ignore the token                  ## Ignore the token
5377                  !!!next-token;                  !!!next-token;
5378                  redo B;                  next B;
5379                } else {                } else {
5380                    !!!cp ('t178');
5381                  #                  #
5382                }                }
5383              } elsif ({              } elsif ({
5384                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
5385                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5386                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5387                       $self->{insertion_mode} eq 'in cell') {                       $self->{insertion_mode} == IN_CELL_IM) {
5388                ## have an element in table scope                ## have an element in table scope
5389                my $i;                my $i;
5390                my $tn;                my $tn;
5391                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5392                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5393                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5394                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5395                    last INSCOPE;                      !!!cp ('t179');
5396                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5397                    $tn = $node->[1];  
5398                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5399                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5400                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5401                            table => 1, html => 1,                                line => $token->{line},
5402                           }->{$node->[1]}) {                                column => $token->{column}};
5403                    last INSCOPE;                      next B;
5404                      } elsif ($node->[1] & TABLE_CELL_EL) {
5405                        !!!cp ('t180');
5406                        $tn = $node->[0]->manakai_local_name;
5407                        ## NOTE: There is exactly one |td| or |th| element
5408                        ## in scope in the stack of open elements by definition.
5409                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5410                        ## ISSUE: Can this be reached?
5411                        !!!cp ('t181');
5412                        last;
5413                      }
5414                  }                  }
5415                } # INSCOPE  
5416                unless (defined $i) {                  !!!cp ('t182');
5417                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5418                        text => $token->{tag_name}, token => $token);
5419                  ## Ignore the token                  ## Ignore the token
5420                  !!!next-token;                  !!!next-token;
5421                  redo B;                  next B;
5422                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
5423              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5424                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5425                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5426                                  token => $token);
5427    
5428                ## As if </caption>                ## As if </caption>
5429                ## have a table element in table scope                ## have a table element in table scope
5430                my $i;                my $i;
5431                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5432                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5433                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5434                      !!!cp ('t184');
5435                    $i = $_;                    $i = $_;
5436                    last INSCOPE;                    last INSCOPE;
5437                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5438                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5439                    last INSCOPE;                    last INSCOPE;
5440                  }                  }
5441                } # INSCOPE                } # INSCOPE
5442                unless (defined $i) {                unless (defined $i) {
5443                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5444                    !!!parse-error (type => 'unmatched end tag',
5445                                    text => 'caption', token => $token);
5446                  ## Ignore the token                  ## Ignore the token
5447                  !!!next-token;                  !!!next-token;
5448                  redo B;                  next B;
5449                }                }
5450                                
5451                ## generate implied end tags                ## generate implied end tags
5452                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5453                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5454                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$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;  
5455                }                }
5456    
5457                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5458                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5459                    !!!parse-error (type => 'not closed',
5460                                    text => $self->{open_elements}->[-1]->[0]
5461                                        ->manakai_local_name,
5462                                    token => $token);
5463                  } else {
5464                    !!!cp ('t189');
5465                }                }
5466    
5467                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5468    
5469                $clear_up_to_marker->();                $clear_up_to_marker->();
5470    
5471                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5472    
5473                ## reprocess                ## reprocess
5474                redo B;                next B;
5475              } elsif ({              } elsif ({
5476                        body => 1, col => 1, colgroup => 1, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5477                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5478                if ($self->{insertion_mode} eq 'in cell' or                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5479                    $self->{insertion_mode} eq 'in caption') {                  !!!cp ('t190');
5480                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5481                                    text => $token->{tag_name}, token => $token);
5482                  ## Ignore the token                  ## Ignore the token
5483                  !!!next-token;                  !!!next-token;
5484                  redo B;                  next B;
5485                } else {                } else {
5486                    !!!cp ('t191');
5487                  #                  #
5488                }                }
5489              } elsif ({              } elsif ({
5490                        tbody => 1, tfoot => 1,                        tbody => 1, tfoot => 1,
5491                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5492                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5493                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5494                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5495                  !!!parse-error (type => 'unmatched end tag',
5496                                  text => $token->{tag_name}, token => $token);
5497                ## Ignore the token                ## Ignore the token
5498                !!!next-token;                !!!next-token;
5499                redo B;                next B;
5500              } else {              } else {
5501                  !!!cp ('t193');
5502                #                #
5503              }              }
5504            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5505              #          for my $entry (@{$self->{open_elements}}) {
5506              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5507                !!!cp ('t75');
5508                !!!parse-error (type => 'in body:#eof', token => $token);
5509                last;
5510            }            }
5511                      }
5512            $in_body->($insert_to_current);  
5513            redo B;          ## Stop parsing.
5514          } elsif ($self->{insertion_mode} eq 'in table') {          last B;
5515            if ($token->{type} eq 'character') {        } else {
5516              ## NOTE: There are "character in table" code clones.          die "$0: $token->{type}: Unknown token type";
5517              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {        }
5518                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
5519          $insert = $insert_to_current;
5520          #
5521        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5522          if ($token->{type} == CHARACTER_TOKEN) {
5523            if (not $open_tables->[-1]->[1] and # tainted
5524                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5525              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5526                                
5527                unless (length $token->{data}) {            unless (length $token->{data}) {
5528                  !!!next-token;              !!!cp ('t194');
5529                  redo B;              !!!next-token;
5530                }              next B;
5531              }            } else {
5532                !!!cp ('t195');
5533              }
5534            }
5535    
5536              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5537    
5538              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5539              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
# Line 3990  sub _tree_construction_main ($) { Line 5541  sub _tree_construction_main ($) {
5541              ## result in a new Text node.              ## result in a new Text node.
5542              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5543                            
5544              if ({              if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
5545                # MUST                # MUST
5546                my $foster_parent_element;                my $foster_parent_element;
5547                my $next_sibling;                my $next_sibling;
5548                my $prev_sibling;                my $prev_sibling;
5549                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5550                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5551                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5552                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5553                        !!!cp ('t196');
5554                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5555                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5556                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5557                    } else {                    } else {
5558                        !!!cp ('t197');
5559                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5560                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5561                    }                    }
# Line 4017  sub _tree_construction_main ($) { Line 5567  sub _tree_construction_main ($) {
5567                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5568                if (defined $prev_sibling and                if (defined $prev_sibling and
5569                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5570                    !!!cp ('t198');
5571                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5572                } else {                } else {
5573                    !!!cp ('t199');
5574                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5575                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5576                     $next_sibling);                     $next_sibling);
5577                }                }
5578              } else {            $open_tables->[-1]->[1] = 1; # tainted
5579                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5580              }            !!!cp ('t200');
5581              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5582            }
5583                            
5584              !!!next-token;          !!!next-token;
5585              redo B;          next B;
5586            } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{type} == START_TAG_TOKEN) {
5587              if ({          if ({
5588                   caption => 1,               tr => ($self->{insertion_mode} != IN_ROW_IM),
5589                   colgroup => 1,               th => 1, td => 1,
5590                   tbody => 1, tfoot => 1, thead => 1,              }->{$token->{tag_name}}) {
5591                  }->{$token->{tag_name}}) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5592                ## Clear back to table context              ## Clear back to table context
5593                while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5594                       $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5595                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5596                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               }  
   
               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;  
             } elsif ({  
                       col => 1,  
                       td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
   
               !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');  
               $self->{insertion_mode} = $token->{tag_name} eq 'col'  
                 ? 'in column group' : 'in table body';  
               ## reprocess  
               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]);  
   
               ## As if </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 'table') {  
                   $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,  
                    tbody => 1, tfoot=> 1, thead => 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;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } 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,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               !!!next-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 {  
               #  
5597              }              }
5598            } else {              
5599              #              !!!insert-element ('tbody',, $token);
5600                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5601                ## reprocess in the "in table body" insertion mode...
5602            }            }
5603              
5604            !!!parse-error (type => 'in table:'.$token->{tag_name});            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5605            $in_body->($insert_to_foster);              unless ($token->{tag_name} eq 'tr') {
5606            redo B;                !!!cp ('t202');
5607          } elsif ($self->{insertion_mode} eq 'in column group') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
           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;  
               }  
5608              }              }
5609                                
5610              #              ## Clear back to table body context
5611            } elsif ($token->{type} eq 'start tag') {              while (not ($self->{open_elements}->[-1]->[1]
5612              if ($token->{tag_name} eq 'col') {                              & TABLE_ROWS_SCOPING_EL)) {
5613                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t203');
5614                  ## ISSUE: Can this case be reached?
5615                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               !!!next-token;  
               redo B;  
             } else {  
               #  
5616              }              }
5617            } elsif ($token->{type} eq 'end tag') {                  
5618              if ($token->{tag_name} eq 'colgroup') {                  $self->{insertion_mode} = IN_ROW_IM;
5619                if ($self->{open_elements}->[-1]->[1] eq 'html') {                  if ($token->{tag_name} eq 'tr') {
5620                  !!!parse-error (type => 'unmatched end tag:colgroup');                    !!!cp ('t204');
5621                  ## Ignore the token                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5622                  !!!next-token;                    !!!nack ('t204');
5623                  redo B;                    !!!next-token;
5624                } else {                    next B;
5625                  pop @{$self->{open_elements}}; # colgroup                  } else {
5626                  $self->{insertion_mode} = 'in table';                    !!!cp ('t205');
5627                  !!!next-token;                    !!!insert-element ('tr',, $token);
5628                  redo B;                                ## reprocess in the "in row" insertion mode
               }  
             } elsif ($token->{tag_name} eq 'col') {  
               !!!parse-error (type => 'unmatched end tag:col');  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           ## As if </colgroup>  
           if ($self->{open_elements}->[-1]->[1] eq 'html') {  
             !!!parse-error (type => 'unmatched end tag:colgroup');  
             ## Ignore the token  
             !!!next-token;  
             redo B;  
           } else {  
             pop @{$self->{open_elements}}; # colgroup  
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
           }  
         } 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');  
   
             ## As if in body, but insert into foster parent element  
             ## 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);  
   
             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;  
5629                  }                  }
               } # 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});  
5630                } else {                } else {
5631                  $foster_parent_element->insert_before                  !!!cp ('t206');
                   ($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 'start tag') {  
             if ({  
                  tr => 1,  
                  th => 1, td => 1,  
                 }->{$token->{tag_name}}) {  
               unless ($token->{tag_name} eq 'tr') {  
                 !!!parse-error (type => 'missing start tag:tr');  
5632                }                }
5633    
5634                ## Clear back to table body context                ## Clear back to table row context
5635                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5636                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5637                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5638                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5639                }                }
5640                                
5641                $self->{insertion_mode} = 'in row';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5642                if ($token->{tag_name} eq 'tr') {                $self->{insertion_mode} = IN_CELL_IM;
5643                  !!!insert-element ($token->{tag_name}, $token->{attributes});  
5644                  !!!next-token;                push @$active_formatting_elements, ['#marker', ''];
5645                } else {                
5646                  !!!insert-element ('tr');                !!!nack ('t207.1');
5647                  ## reprocess                !!!next-token;
5648                }                next B;
               redo B;  
5649              } elsif ({              } elsif ({
5650                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5651                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5652                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5653                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5654                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
5655                my $i;                  ## As if </tr>
5656                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
5657                  my $node = $self->{open_elements}->[$_];                  my $i;
5658                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5659                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
5660                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
5661                    $i = $_;                      !!!cp ('t208');
5662                    last INSCOPE;                      $i = $_;
5663                  } elsif ({                      last INSCOPE;
5664                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5665                           }->{$node->[1]}) {                      !!!cp ('t209');
5666                    last INSCOPE;                      last INSCOPE;
5667                      }
5668                    } # INSCOPE
5669                    unless (defined $i) {
5670                      !!!cp ('t210');
5671    ## TODO: This type is wrong.
5672                      !!!parse-error (type => 'unmacthed end tag',
5673                                      text => $token->{tag_name}, token => $token);
5674                      ## Ignore the token
5675                      !!!nack ('t210.1');
5676                      !!!next-token;
5677                      next B;
5678                    }
5679                    
5680                    ## Clear back to table row context
5681                    while (not ($self->{open_elements}->[-1]->[1]
5682                                    & TABLE_ROW_SCOPING_EL)) {
5683                      !!!cp ('t211');
5684                      ## ISSUE: Can this case be reached?
5685                      pop @{$self->{open_elements}};
5686                    }
5687                    
5688                    pop @{$self->{open_elements}}; # tr
5689                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5690                    if ($token->{tag_name} eq 'tr') {
5691                      !!!cp ('t212');
5692                      ## reprocess
5693                      !!!ack-later;
5694                      next B;
5695                    } else {
5696                      !!!cp ('t213');
5697                      ## reprocess in the "in table body" insertion mode...
5698                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5699                }                }
5700    
5701                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5702                while (not {                  ## have an element in table scope
5703                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5704                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5705                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5706                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5707                        !!!cp ('t214');
5708                        $i = $_;
5709                        last INSCOPE;
5710                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5711                        !!!cp ('t215');
5712                        last INSCOPE;
5713                      }
5714                    } # INSCOPE
5715                    unless (defined $i) {
5716                      !!!cp ('t216');
5717    ## TODO: This erorr type is wrong.
5718                      !!!parse-error (type => 'unmatched end tag',
5719                                      text => $token->{tag_name}, token => $token);
5720                      ## Ignore the token
5721                      !!!nack ('t216.1');
5722                      !!!next-token;
5723                      next B;
5724                    }
5725    
5726                    ## Clear back to table body context
5727                    while (not ($self->{open_elements}->[-1]->[1]
5728                                    & TABLE_ROWS_SCOPING_EL)) {
5729                      !!!cp ('t217');
5730                      ## ISSUE: Can this state be reached?
5731                      pop @{$self->{open_elements}};
5732                    }
5733                    
5734                    ## As if <{current node}>
5735                    ## have an element in table scope
5736                    ## true by definition
5737                    
5738                    ## Clear back to table body context
5739                    ## nop by definition
5740                    
5741                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5742                    $self->{insertion_mode} = IN_TABLE_IM;
5743                    ## reprocess in "in table" insertion mode...
5744                  } else {
5745                    !!!cp ('t218');
5746                }                }
5747    
5748                ## As if <{current node}>                if ($token->{tag_name} eq 'col') {
5749                ## have an element in table scope                  ## Clear back to table context
5750                ## true by definition                  while (not ($self->{open_elements}->[-1]->[1]
5751                                    & TABLE_SCOPING_EL)) {
5752                ## Clear back to table body context                    !!!cp ('t219');
5753                ## nop by definition                    ## ISSUE: Can this state be reached?
5754                      pop @{$self->{open_elements}};
5755                pop @{$self->{open_elements}};                  }
5756                $self->{insertion_mode} = 'in table';                  
5757                ## reprocess                  !!!insert-element ('colgroup',, $token);
5758                redo B;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5759                    ## reprocess
5760                    !!!ack-later;
5761                    next B;
5762                  } elsif ({
5763                            caption => 1,
5764                            colgroup => 1,
5765                            tbody => 1, tfoot => 1, thead => 1,
5766                           }->{$token->{tag_name}}) {
5767                    ## Clear back to table context
5768                    while (not ($self->{open_elements}->[-1]->[1]
5769                                    & TABLE_SCOPING_EL)) {
5770                      !!!cp ('t220');
5771                      ## ISSUE: Can this state be reached?
5772                      pop @{$self->{open_elements}};
5773                    }
5774                    
5775                    push @$active_formatting_elements, ['#marker', '']
5776                        if $token->{tag_name} eq 'caption';
5777                    
5778                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5779                    $self->{insertion_mode} = {
5780                                               caption => IN_CAPTION_IM,
5781                                               colgroup => IN_COLUMN_GROUP_IM,
5782                                               tbody => IN_TABLE_BODY_IM,
5783                                               tfoot => IN_TABLE_BODY_IM,
5784                                               thead => IN_TABLE_BODY_IM,
5785                                              }->{$token->{tag_name}};
5786                    !!!next-token;
5787                    !!!nack ('t220.1');
5788                    next B;
5789                  } else {
5790                    die "$0: in table: <>: $token->{tag_name}";
5791                  }
5792              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5793                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5794                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5795                                      ->manakai_local_name,
5796                                  token => $token);
5797    
5798                ## As if </table>                ## As if </table>
5799                ## have a table element in table scope                ## have a table element in table scope
5800                my $i;                my $i;
5801                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5802                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5803                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5804                      !!!cp ('t221');
5805                    $i = $_;                    $i = $_;
5806                    last INSCOPE;                    last INSCOPE;
5807                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5808                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5809                    last INSCOPE;                    last INSCOPE;
5810                  }                  }
5811                } # INSCOPE                } # INSCOPE
5812                unless (defined $i) {                unless (defined $i) {
5813                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5814    ## TODO: The following is wrong, maybe.
5815                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5816                                    token => $token);
5817                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5818                    !!!nack ('t223.1');
5819                  !!!next-token;                  !!!next-token;
5820                  redo B;                  next B;
5821                }                }
5822                                
5823    ## TODO: Followings are removed from the latest spec.
5824                ## generate implied end tags                ## generate implied end tags
5825                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5826                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5827                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 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;  
5828                }                }
5829    
5830                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5831                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5832                    ## NOTE: |<table><tr><table>|
5833                    !!!parse-error (type => 'not closed',
5834                                    text => $self->{open_elements}->[-1]->[0]
5835                                        ->manakai_local_name,
5836                                    token => $token);
5837                  } else {
5838                    !!!cp ('t226');
5839                }                }
5840    
5841                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5842                  pop @{$open_tables};
5843    
5844                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5845    
5846                ## reprocess            ## reprocess
5847                redo B;            !!!ack-later;
5848              } else {            next B;
5849                #          } elsif ($token->{tag_name} eq 'style') {
5850              }            if (not $open_tables->[-1]->[1]) { # tainted
5851            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5852              if ({              ## NOTE: This is a "as if in head" code clone.
5853                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5854                  }->{$token->{tag_name}}) {              next B;
5855                ## have an element in table scope            } else {
5856                my $i;              !!!cp ('t227.7');
5857                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              #
5858                  my $node = $self->{open_elements}->[$_];            }
5859                  if ($node->[1] eq $token->{tag_name}) {          } elsif ($token->{tag_name} eq 'script') {
5860                    $i = $_;            if (not $open_tables->[-1]->[1]) { # tainted
5861                    last INSCOPE;              !!!cp ('t227.6');
5862                  } elsif ({              ## NOTE: This is a "as if in head" code clone.
5863                            table => 1, html => 1,              $script_start_tag->();
5864                           }->{$node->[1]}) {              next B;
5865                    last INSCOPE;            } else {
5866                  }              !!!cp ('t227.5');
5867                } # INSCOPE              #
5868                unless (defined $i) {            }
5869                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'input') {
5870                  ## Ignore the token            if (not $open_tables->[-1]->[1]) { # tainted
5871                  !!!next-token;              if ($token->{attributes}->{type}) { ## TODO: case
5872                  redo B;                my $type = lc $token->{attributes}->{type}->{value};
5873                }                if ($type eq 'hidden') {
5874                    !!!cp ('t227.3');
5875                    !!!parse-error (type => 'in table',
5876                                    text => $token->{tag_name}, token => $token);
5877    
5878                ## Clear back to table body context                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
               while (not {  
                 tbody => 1, tfoot => 1, thead => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
5879    
5880                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;  
               }  
5881    
               ## 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]);  
5882                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
5883    
5884                ## Clear back to table body context                  !!!next-token;
5885                ## nop by definition                  !!!ack ('t227.2.1');
5886                    next B;
5887                pop @{$self->{open_elements}};                } else {
5888                $self->{insertion_mode} = 'in table';                  !!!cp ('t227.2');
5889                ## reprocess                  #
5890                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;  
5891              } else {              } else {
5892                  !!!cp ('t227.1');
5893                #                #
5894              }              }
5895            } else {            } else {
5896                !!!cp ('t227.4');
5897              #              #
5898            }            }
5899                      } else {
5900            ## As if in table            !!!cp ('t227');
5901            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5902            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in row') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
5903    
5904              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table', text => $token->{tag_name},
5905              ## ISSUE: Spec says that "whenever a node would be inserted                          token => $token);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq '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';  
5906    
5907                push @$active_formatting_elements, ['#marker', ''];          $insert = $insert_to_foster;
5908                          #
5909                !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
5910                redo B;              if ($token->{tag_name} eq 'tr' and
5911              } elsif ({                  $self->{insertion_mode} == IN_ROW_IM) {
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
5912                ## have an element in table scope                ## have an element in table scope
5913                my $i;                my $i;
5914                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5915                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5916                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5917                      !!!cp ('t228');
5918                    $i = $_;                    $i = $_;
5919                    last INSCOPE;                    last INSCOPE;
5920                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5921                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5922                    last INSCOPE;                    last INSCOPE;
5923                  }                  }
5924                } # INSCOPE                } # INSCOPE
5925                unless (defined $i) {                unless (defined $i) {
5926                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
5927                    !!!parse-error (type => 'unmatched end tag',
5928                                    text => $token->{tag_name}, token => $token);
5929                  ## Ignore the token                  ## Ignore the token
5930                    !!!nack ('t230.1');
5931                  !!!next-token;                  !!!next-token;
5932                  redo B;                  next B;
5933                  } else {
5934                    !!!cp ('t232');
5935                }                }
5936    
5937                ## Clear back to table row context                ## Clear back to table row context
5938                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5939                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5940                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
5941                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
5942                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5943                }                }
5944    
5945                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5946                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5947                ## reprocess                !!!next-token;
5948                redo B;                !!!nack ('t231.1');
5949                  next B;
5950              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5951                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
5952                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
5953                    ## have an element in table scope
5954                ## As if </table>                  my $i;
5955                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5956                my $i;                    my $node = $self->{open_elements}->[$_];
5957                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
5958                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
5959                  if ($node->[1] eq 'table') {                      $i = $_;
5960                    $i = $_;                      last INSCOPE;
5961                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5962                  } elsif ({                      !!!cp ('t234');
5963                            table => 1, html => 1,                      last INSCOPE;
5964                           }->{$node->[1]}) {                    }
5965                    last INSCOPE;                  } # INSCOPE
5966                    unless (defined $i) {
5967                      !!!cp ('t235');
5968    ## TODO: The following is wrong.
5969                      !!!parse-error (type => 'unmatched end tag',
5970                                      text => $token->{type}, token => $token);
5971                      ## Ignore the token
5972                      !!!nack ('t236.1');
5973                      !!!next-token;
5974                      next B;
5975                  }                  }
5976                } # INSCOPE                  
5977                unless (defined $i) {                  ## Clear back to table row context
5978                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
5979                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
5980                  !!!next-token;                    !!!cp ('t236');
5981                  redo B;  ## ISSUE: Can this state be reached?
5982                }                    pop @{$self->{open_elements}};
5983                                  }
5984                ## generate implied end tags                  
5985                if ({                  pop @{$self->{open_elements}}; # tr
5986                     dd => 1, dt => 1, li => 1, p => 1,                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5987                     td => 1, th => 1, tr => 1,                  ## reprocess in the "in table body" insertion mode...
                    tbody => 1, tfoot=> 1, thead => 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;  
5988                }                }
5989    
5990                if ($self->{open_elements}->[-1]->[1] ne 'table') {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5991                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  ## have an element in table scope
5992                    my $i;
5993                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5994                      my $node = $self->{open_elements}->[$_];
5995                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5996                        !!!cp ('t237');
5997                        $i = $_;
5998                        last INSCOPE;
5999                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6000                        !!!cp ('t238');
6001                        last INSCOPE;
6002                      }
6003                    } # INSCOPE
6004                    unless (defined $i) {
6005                      !!!cp ('t239');
6006                      !!!parse-error (type => 'unmatched end tag',
6007                                      text => $token->{tag_name}, token => $token);
6008                      ## Ignore the token
6009                      !!!nack ('t239.1');
6010                      !!!next-token;
6011                      next B;
6012                    }
6013                    
6014                    ## Clear back to table body context
6015                    while (not ($self->{open_elements}->[-1]->[1]
6016                                    & TABLE_ROWS_SCOPING_EL)) {
6017                      !!!cp ('t240');
6018                      pop @{$self->{open_elements}};
6019                    }
6020                    
6021                    ## As if <{current node}>
6022                    ## have an element in table scope
6023                    ## true by definition
6024                    
6025                    ## Clear back to table body context
6026                    ## nop by definition
6027                    
6028                    pop @{$self->{open_elements}};
6029                    $self->{insertion_mode} = IN_TABLE_IM;
6030                    ## reprocess in the "in table" insertion mode...
6031                }                }
6032    
6033                splice @{$self->{open_elements}}, $i;                ## NOTE: </table> in the "in table" insertion mode.
6034                  ## When you edit the code fragment below, please ensure that
6035                $self->_reset_insertion_mode;                ## the code for <table> in the "in table" insertion mode
6036                  ## is synced with it.
6037    
6038                ## 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  
6039                my $i;                my $i;
6040                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6041                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6042                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6043                      !!!cp ('t241');
6044                    $i = $_;                    $i = $_;
6045                    last INSCOPE;                    last INSCOPE;
6046                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6047                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6048                    last INSCOPE;                    last INSCOPE;
6049                  }                  }
6050                } # INSCOPE                } # INSCOPE
6051                unless (defined $i) {                unless (defined $i) {
6052                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6053                    !!!parse-error (type => 'unmatched end tag',
6054                                    text => $token->{tag_name}, token => $token);
6055                  ## Ignore the token                  ## Ignore the token
6056                    !!!nack ('t243.1');
6057                  !!!next-token;                  !!!next-token;
6058                  redo B;                  next B;
6059                }                }
6060                    
6061                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6062                while (not {                pop @{$open_tables};
6063                  tr => 1, html => 1,                
6064                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6065                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6066                  pop @{$self->{open_elements}};                !!!next-token;
6067                  next B;
6068                } elsif ({
6069                          tbody => 1, tfoot => 1, thead => 1,
6070                         }->{$token->{tag_name}} and
6071                         $self->{insertion_mode} & ROW_IMS) {
6072                  if ($self->{insertion_mode} == IN_ROW_IM) {
6073                    ## have an element in table scope
6074                    my $i;
6075                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6076                      my $node = $self->{open_elements}->[$_];
6077                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6078                        !!!cp ('t247');
6079                        $i = $_;
6080                        last INSCOPE;
6081                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6082                        !!!cp ('t248');
6083                        last INSCOPE;
6084                      }
6085                    } # INSCOPE
6086                      unless (defined $i) {
6087                        !!!cp ('t249');
6088                        !!!parse-error (type => 'unmatched end tag',
6089                                        text => $token->{tag_name}, token => $token);
6090                        ## Ignore the token
6091                        !!!nack ('t249.1');
6092                        !!!next-token;
6093                        next B;
6094                      }
6095                    
6096                    ## As if </tr>
6097                    ## have an element in table scope
6098                    my $i;
6099                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6100                      my $node = $self->{open_elements}->[$_];
6101                      if ($node->[1] & TABLE_ROW_EL) {
6102                        !!!cp ('t250');
6103                        $i = $_;
6104                        last INSCOPE;
6105                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6106                        !!!cp ('t251');
6107                        last INSCOPE;
6108                      }
6109                    } # INSCOPE
6110                      unless (defined $i) {
6111                        !!!cp ('t252');
6112                        !!!parse-error (type => 'unmatched end tag',
6113                                        text => 'tr', token => $token);
6114                        ## Ignore the token
6115                        !!!nack ('t252.1');
6116                        !!!next-token;
6117                        next B;
6118                      }
6119                    
6120                    ## Clear back to table row context
6121                    while (not ($self->{open_elements}->[-1]->[1]
6122                                    & TABLE_ROW_SCOPING_EL)) {
6123                      !!!cp ('t253');
6124    ## ISSUE: Can this case be reached?
6125                      pop @{$self->{open_elements}};
6126                    }
6127                    
6128                    pop @{$self->{open_elements}}; # tr
6129                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
6130                    ## reprocess in the "in table body" insertion mode...
6131                }                }
6132    
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </tr>  
6133                ## have an element in table scope                ## have an element in table scope
6134                my $i;                my $i;
6135                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6136                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6137                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6138                      !!!cp ('t254');
6139                    $i = $_;                    $i = $_;
6140                    last INSCOPE;                    last INSCOPE;
6141                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6142                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6143                    last INSCOPE;                    last INSCOPE;
6144                  }                  }
6145                } # INSCOPE                } # INSCOPE
6146                unless (defined $i) {                unless (defined $i) {
6147                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t256');
6148                    !!!parse-error (type => 'unmatched end tag',
6149                                    text => $token->{tag_name}, token => $token);
6150                  ## Ignore the token                  ## Ignore the token
6151                    !!!nack ('t256.1');
6152                  !!!next-token;                  !!!next-token;
6153                  redo B;                  next B;
6154                }                }
6155    
6156                ## Clear back to table row context                ## Clear back to table body context
6157                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6158                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6159                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6160                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6161                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6162                }                }
6163    
6164                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6165                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6166                ## reprocess                !!!nack ('t257.1');
6167                redo B;                !!!next-token;
6168                  next B;
6169              } elsif ({              } elsif ({
6170                        tbody => 1, tfoot => 1, thead => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6171                          html => 1, td => 1, th => 1,
6172                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6173                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6174                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6175                ## have an element in table scope            !!!cp ('t258');
6176                my $i;            !!!parse-error (type => 'unmatched end tag',
6177                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
6178                  my $node = $self->{open_elements}->[$_];            ## Ignore the token
6179                  if ($node->[1] eq $token->{tag_name}) {            !!!nack ('t258.1');
6180                    $i = $_;             !!!next-token;
6181                    last INSCOPE;            next B;
6182                  } elsif ({          } else {
6183                            table => 1, html => 1,            !!!cp ('t259');
6184                           }->{$node->[1]}) {            !!!parse-error (type => 'in table:/',
6185                    last INSCOPE;                            text => $token->{tag_name}, token => $token);
6186                  }  
6187                } # INSCOPE            $insert = $insert_to_foster;
6188                unless (defined $i) {            #
6189                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          }
6190                  ## Ignore the token        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6191            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6192                    @{$self->{open_elements}} == 1) { # redundant, maybe
6193              !!!parse-error (type => 'in body:#eof', token => $token);
6194              !!!cp ('t259.1');
6195              #
6196            } else {
6197              !!!cp ('t259.2');
6198              #
6199            }
6200    
6201            ## Stop parsing
6202            last B;
6203          } else {
6204            die "$0: $token->{type}: Unknown token type";
6205          }
6206        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6207              if ($token->{type} == CHARACTER_TOKEN) {
6208                if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6209                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6210                  unless (length $token->{data}) {
6211                    !!!cp ('t260');
6212                  !!!next-token;                  !!!next-token;
6213                  redo B;                  next B;
6214                }                }
6215                }
6216                ## As if </tr>              
6217                ## have an element in table scope              !!!cp ('t261');
6218                my $i;              #
6219                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6220                  my $node = $self->{open_elements}->[$_];              if ($token->{tag_name} eq 'col') {
6221                  if ($node->[1] eq 'tr') {                !!!cp ('t262');
6222                    $i = $_;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6223                    last INSCOPE;                pop @{$self->{open_elements}};
6224                  } elsif ({                !!!ack ('t262.1');
6225                            table => 1, html => 1,                !!!next-token;
6226                           }->{$node->[1]}) {                next B;
6227                    last INSCOPE;              } else {
6228                  }                !!!cp ('t263');
6229                } # INSCOPE                #
6230                unless (defined $i) {              }
6231                  !!!parse-error (type => 'unmatched end tag:tr');            } elsif ($token->{type} == END_TAG_TOKEN) {
6232                if ($token->{tag_name} eq 'colgroup') {
6233                  if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6234                    !!!cp ('t264');
6235                    !!!parse-error (type => 'unmatched end tag',
6236                                    text => 'colgroup', token => $token);
6237                  ## Ignore the token                  ## Ignore the token
6238                  !!!next-token;                  !!!next-token;
6239                  redo B;                  next B;
6240                }                } else {
6241                    !!!cp ('t265');
6242                ## Clear back to table row context                  pop @{$self->{open_elements}}; # colgroup
6243                while (not {                  $self->{insertion_mode} = IN_TABLE_IM;
6244                  tr => 1, html => 1,                  !!!next-token;
6245                }->{$self->{open_elements}->[-1]->[1]}) {                  next B;            
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
6246                }                }
6247                } elsif ($token->{tag_name} eq 'col') {
6248                pop @{$self->{open_elements}}; # tr                !!!cp ('t266');
6249                $self->{insertion_mode} = 'in table body';                !!!parse-error (type => 'unmatched end tag',
6250                ## 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});  
6251                ## Ignore the token                ## Ignore the token
6252                !!!next-token;                !!!next-token;
6253                redo B;                next B;
6254              } else {              } else {
6255                #                !!!cp ('t267');
6256                  #
6257              }              }
6258            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6259              #          if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6260            }              @{$self->{open_elements}} == 1) { # redundant, maybe
6261              !!!cp ('t270.2');
6262              ## Stop parsing.
6263              last B;
6264            } else {
6265              ## NOTE: As if </colgroup>.
6266              !!!cp ('t270.1');
6267              pop @{$self->{open_elements}}; # colgroup
6268              $self->{insertion_mode} = IN_TABLE_IM;
6269              ## Reprocess.
6270              next B;
6271            }
6272          } else {
6273            die "$0: $token->{type}: Unknown token type";
6274          }
6275    
6276            ## As if in table            ## As if </colgroup>
6277            !!!parse-error (type => 'in table:'.$token->{tag_name});            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6278            $in_body->($insert_to_foster);              !!!cp ('t269');
6279            redo B;  ## TODO: Wrong error type?
6280          } elsif ($self->{insertion_mode} eq 'in select') {              !!!parse-error (type => 'unmatched end tag',
6281            if ($token->{type} eq 'character') {                              text => 'colgroup', token => $token);
6282              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              ## Ignore the token
6283                !!!nack ('t269.1');
6284              !!!next-token;              !!!next-token;
6285              redo B;              next B;
6286            } elsif ($token->{type} eq 'start tag') {            } else {
6287              if ($token->{tag_name} eq 'option') {              !!!cp ('t270');
6288                if ($self->{open_elements}->[-1]->[1] eq 'option') {              pop @{$self->{open_elements}}; # colgroup
6289                  ## As if </option>              $self->{insertion_mode} = IN_TABLE_IM;
6290                  pop @{$self->{open_elements}};              !!!ack-later;
6291                }              ## reprocess
6292                next B;
6293              }
6294        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6295          if ($token->{type} == CHARACTER_TOKEN) {
6296            !!!cp ('t271');
6297            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6298            !!!next-token;
6299            next B;
6300          } elsif ($token->{type} == START_TAG_TOKEN) {
6301            if ($token->{tag_name} eq 'option') {
6302              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6303                !!!cp ('t272');
6304                ## As if </option>
6305                pop @{$self->{open_elements}};
6306              } else {
6307                !!!cp ('t273');
6308              }
6309    
6310                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6311                !!!next-token;            !!!nack ('t273.1');
6312                redo B;            !!!next-token;
6313              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6314                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6315                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6316                  pop @{$self->{open_elements}};              !!!cp ('t274');
6317                }              ## As if </option>
6318                pop @{$self->{open_elements}};
6319              } else {
6320                !!!cp ('t275');
6321              }
6322    
6323                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6324                  ## As if </optgroup>              !!!cp ('t276');
6325                  pop @{$self->{open_elements}};              ## As if </optgroup>
6326                }              pop @{$self->{open_elements}};
6327              } else {
6328                !!!cp ('t277');
6329              }
6330    
6331                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6332                !!!next-token;            !!!nack ('t277.1');
6333                redo B;            !!!next-token;
6334              } elsif ($token->{tag_name} eq 'select') {            next B;
6335                !!!parse-error (type => 'not closed:select');          } elsif ({
6336                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6337                ## have an element in table scope                   }->{$token->{tag_name}} or
6338                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6339                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6340                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6341                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6342                    $i = $_;                     tr => 1, td => 1, th => 1,
6343                    last INSCOPE;                    }->{$token->{tag_name}})) {
6344                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6345                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6346                           }->{$node->[1]}) {                            token => $token);
6347                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6348                  }            ## as if there were </select> (otherwise).
6349                } # INSCOPE            ## have an element in table scope
6350                unless (defined $i) {            my $i;
6351                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6352                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6353                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6354                  redo B;                !!!cp ('t278');
6355                }                $i = $_;
6356                  last INSCOPE;
6357                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6358                  !!!cp ('t279');
6359                  last INSCOPE;
6360                }
6361              } # INSCOPE
6362              unless (defined $i) {
6363                !!!cp ('t280');
6364                !!!parse-error (type => 'unmatched end tag',
6365                                text => 'select', token => $token);
6366                ## Ignore the token
6367                !!!nack ('t280.1');
6368                !!!next-token;
6369                next B;
6370              }
6371                                
6372                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6373              splice @{$self->{open_elements}}, $i;
6374    
6375                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6376    
6377                !!!next-token;            if ($token->{tag_name} eq 'select') {
6378                redo B;              !!!nack ('t281.2');
6379              } else {              !!!next-token;
6380                #              next B;
6381              } else {
6382                !!!cp ('t281.1');
6383                !!!ack-later;
6384                ## Reprocess the token.
6385                next B;
6386              }
6387            } else {
6388              !!!cp ('t282');
6389              !!!parse-error (type => 'in select',
6390                              text => $token->{tag_name}, token => $token);
6391              ## Ignore the token
6392              !!!nack ('t282.1');
6393              !!!next-token;
6394              next B;
6395            }
6396          } elsif ($token->{type} == END_TAG_TOKEN) {
6397            if ($token->{tag_name} eq 'optgroup') {
6398              if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6399                  $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6400                !!!cp ('t283');
6401                ## As if </option>
6402                splice @{$self->{open_elements}}, -2;
6403              } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6404                !!!cp ('t284');
6405                pop @{$self->{open_elements}};
6406              } else {
6407                !!!cp ('t285');
6408                !!!parse-error (type => 'unmatched end tag',
6409                                text => $token->{tag_name}, token => $token);
6410                ## Ignore the token
6411              }
6412              !!!nack ('t285.1');
6413              !!!next-token;
6414              next B;
6415            } elsif ($token->{tag_name} eq 'option') {
6416              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6417                !!!cp ('t286');
6418                pop @{$self->{open_elements}};
6419              } else {
6420                !!!cp ('t287');
6421                !!!parse-error (type => 'unmatched end tag',
6422                                text => $token->{tag_name}, token => $token);
6423                ## Ignore the token
6424              }
6425              !!!nack ('t287.1');
6426              !!!next-token;
6427              next B;
6428            } elsif ($token->{tag_name} eq 'select') {
6429              ## have an element in table scope
6430              my $i;
6431              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6432                my $node = $self->{open_elements}->[$_];
6433                if ($node->[1] & SELECT_EL) {
6434                  !!!cp ('t288');
6435                  $i = $_;
6436                  last INSCOPE;
6437                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6438                  !!!cp ('t289');
6439                  last INSCOPE;
6440              }              }
6441            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6442              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
6443                if ($self->{open_elements}->[-1]->[1] eq 'option' and              !!!cp ('t290');
6444                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag',
6445                  ## As if </option>                              text => $token->{tag_name}, token => $token);
6446                  splice @{$self->{open_elements}}, -2;              ## Ignore the token
6447                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              !!!nack ('t290.1');
6448                  pop @{$self->{open_elements}};              !!!next-token;
6449                } else {              next B;
6450                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            }
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 pop @{$self->{open_elements}};  
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6451                                
6452                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6453              splice @{$self->{open_elements}}, $i;
6454    
6455                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6456    
6457                !!!next-token;            !!!nack ('t291.1');
6458                redo B;            !!!next-token;
6459              } elsif ({            next B;
6460                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6461                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6462                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6463                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6464                                   }->{$token->{tag_name}}) {
6465                ## have an element in table scope  ## TODO: The following is wrong?
6466                my $i;            !!!parse-error (type => 'unmatched end tag',
6467                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6468                                
6469                ## As if </select>            ## have an element in table scope
6470                ## have an element in table scope            my $i;
6471                undef $i;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6472                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              my $node = $self->{open_elements}->[$_];
6473                  my $node = $self->{open_elements}->[$_];              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6474                  if ($node->[1] eq 'select') {                !!!cp ('t292');
6475                    $i = $_;                $i = $_;
6476                    last INSCOPE;                last INSCOPE;
6477                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6478                            table => 1, html => 1,                !!!cp ('t293');
6479                           }->{$node->[1]}) {                last INSCOPE;
6480                    last INSCOPE;              }
6481                  }            } # INSCOPE
6482                } # INSCOPE            unless (defined $i) {
6483                unless (defined $i) {              !!!cp ('t294');
6484                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
6485                  ## Ignore the </select> token              !!!nack ('t294.1');
6486                  !!!next-token; ## TODO: ok?              !!!next-token;
6487                  redo B;              next B;
6488                }            }
6489                                
6490                splice @{$self->{open_elements}}, $i;            ## As if </select>
6491              ## have an element in table scope
6492                $self->_reset_insertion_mode;            undef $i;
6493              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6494                ## reprocess              my $node = $self->{open_elements}->[$_];
6495                redo B;              if ($node->[1] & SELECT_EL) {
6496              } else {                !!!cp ('t295');
6497                #                $i = $_;
6498                  last INSCOPE;
6499                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6500    ## ISSUE: Can this state be reached?
6501                  !!!cp ('t296');
6502                  last INSCOPE;
6503              }              }
6504            } else {            } # INSCOPE
6505              #            unless (defined $i) {
6506                !!!cp ('t297');
6507    ## TODO: The following error type is correct?
6508                !!!parse-error (type => 'unmatched end tag',
6509                                text => 'select', token => $token);
6510                ## Ignore the </select> token
6511                !!!nack ('t297.1');
6512                !!!next-token; ## TODO: ok?
6513                next B;
6514            }            }
6515                  
6516              !!!cp ('t298');
6517              splice @{$self->{open_elements}}, $i;
6518    
6519              $self->_reset_insertion_mode;
6520    
6521            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!ack-later;
6522              ## reprocess
6523              next B;
6524            } else {
6525              !!!cp ('t299');
6526              !!!parse-error (type => 'in select:/',
6527                              text => $token->{tag_name}, token => $token);
6528            ## Ignore the token            ## Ignore the token
6529              !!!nack ('t299.3');
6530            !!!next-token;            !!!next-token;
6531            redo B;            next B;
6532          } elsif ($self->{insertion_mode} eq 'after body') {          }
6533            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6534              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6535                my $data = $1;                  @{$self->{open_elements}} == 1) { # redundant, maybe
6536                ## As if in body            !!!cp ('t299.1');
6537                $reconstruct_active_formatting_elements->($insert_to_current);            !!!parse-error (type => 'in body:#eof', token => $token);
6538            } else {
6539              !!!cp ('t299.2');
6540            }
6541    
6542            ## Stop parsing.
6543            last B;
6544          } else {
6545            die "$0: $token->{type}: Unknown token type";
6546          }
6547        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6548          if ($token->{type} == CHARACTER_TOKEN) {
6549            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6550              my $data = $1;
6551              ## As if in body
6552              $reconstruct_active_formatting_elements->($insert_to_current);
6553                                
6554                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6555              
6556              unless (length $token->{data}) {
6557                !!!cp ('t300');
6558                !!!next-token;
6559                next B;
6560              }
6561            }
6562            
6563            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6564              !!!cp ('t301');
6565              !!!parse-error (type => 'after html:#text', token => $token);
6566              #
6567            } else {
6568              !!!cp ('t302');
6569              ## "after body" insertion mode
6570              !!!parse-error (type => 'after body:#text', token => $token);
6571              #
6572            }
6573    
6574                unless (length $token->{data}) {          $self->{insertion_mode} = IN_BODY_IM;
6575                  !!!next-token;          ## reprocess
6576                  redo B;          next B;
6577                }        } elsif ($token->{type} == START_TAG_TOKEN) {
6578              }          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6579                          !!!cp ('t303');
6580              #            !!!parse-error (type => 'after html',
6581              !!!parse-error (type => 'after body:#character');                            text => $token->{tag_name}, token => $token);
6582            } elsif ($token->{type} eq 'start tag') {            #
6583              !!!parse-error (type => 'after body:'.$token->{tag_name});          } else {
6584              #            !!!cp ('t304');
6585            } elsif ($token->{type} eq 'end tag') {            ## "after body" insertion mode
6586              if ($token->{tag_name} eq 'html') {            !!!parse-error (type => 'after body',
6587                if (defined $self->{inner_html_node}) {                            text => $token->{tag_name}, token => $token);
6588                  !!!parse-error (type => 'unmatched end tag:html');            #
6589                  ## Ignore the token          }
6590                  !!!next-token;  
6591                  redo B;          $self->{insertion_mode} = IN_BODY_IM;
6592                } else {          !!!ack-later;
6593                  $previous_insertion_mode = $self->{insertion_mode};          ## reprocess
6594                  $self->{insertion_mode} = 'trailing end';          next B;
6595                  !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
6596                  redo B;          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6597                }            !!!cp ('t305');
6598              } else {            !!!parse-error (type => 'after html:/',
6599                !!!parse-error (type => 'after body:/'.$token->{tag_name});                            text => $token->{tag_name}, token => $token);
6600              }            
6601              $self->{insertion_mode} = IN_BODY_IM;
6602              ## Reprocess.
6603              next B;
6604            } else {
6605              !!!cp ('t306');
6606            }
6607    
6608            ## "after body" insertion mode
6609            if ($token->{tag_name} eq 'html') {
6610              if (defined $self->{inner_html_node}) {
6611                !!!cp ('t307');
6612                !!!parse-error (type => 'unmatched end tag',
6613                                text => 'html', token => $token);
6614                ## Ignore the token
6615                !!!next-token;
6616                next B;
6617            } else {            } else {
6618              die "$0: $token->{type}: Unknown token type";              !!!cp ('t308');
6619                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6620                !!!next-token;
6621                next B;
6622            }            }
6623            } else {
6624              !!!cp ('t309');
6625              !!!parse-error (type => 'after body:/',
6626                              text => $token->{tag_name}, token => $token);
6627    
6628            $self->{insertion_mode} = 'in body';            $self->{insertion_mode} = IN_BODY_IM;
6629            ## reprocess            ## reprocess
6630            redo B;            next B;
6631      } elsif ($self->{insertion_mode} eq 'in frameset') {          }
6632        if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6633          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t309.2');
6634            ## Stop parsing
6635            last B;
6636          } else {
6637            die "$0: $token->{type}: Unknown token type";
6638          }
6639        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6640          if ($token->{type} == CHARACTER_TOKEN) {
6641            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6642            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6643              
6644            unless (length $token->{data}) {            unless (length $token->{data}) {
6645                !!!cp ('t310');
6646              !!!next-token;              !!!next-token;
6647              redo B;              next B;
6648            }            }
6649          }          }
6650            
6651          !!!parse-error (type => 'in frameset:#character');          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6652          ## Ignore the token            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6653          !!!next-token;              !!!cp ('t311');
6654          redo B;              !!!parse-error (type => 'in frameset:#text', token => $token);
6655        } elsif ($token->{type} eq 'start tag') {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6656          if ($token->{tag_name} eq 'frameset') {              !!!cp ('t312');
6657            !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!parse-error (type => 'after frameset:#text', token => $token);
6658              } else { # "after after frameset"
6659                !!!cp ('t313');
6660                !!!parse-error (type => 'after html:#text', token => $token);
6661              }
6662              
6663              ## Ignore the token.
6664              if (length $token->{data}) {
6665                !!!cp ('t314');
6666                ## reprocess the rest of characters
6667              } else {
6668                !!!cp ('t315');
6669                !!!next-token;
6670              }
6671              next B;
6672            }
6673            
6674            die qq[$0: Character "$token->{data}"];
6675          } elsif ($token->{type} == START_TAG_TOKEN) {
6676            if ($token->{tag_name} eq 'frameset' and
6677                $self->{insertion_mode} == IN_FRAMESET_IM) {
6678              !!!cp ('t318');
6679              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6680              !!!nack ('t318.1');
6681            !!!next-token;            !!!next-token;
6682            redo B;            next B;
6683          } elsif ($token->{tag_name} eq 'frame') {          } elsif ($token->{tag_name} eq 'frame' and
6684            !!!insert-element ($token->{tag_name}, $token->{attributes});                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6685              !!!cp ('t319');
6686              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6687            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6688              !!!ack ('t319.1');
6689            !!!next-token;            !!!next-token;
6690            redo B;            next B;
6691          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6692            ## NOTE: As if in body.            !!!cp ('t320');
6693            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);            ## NOTE: As if in head.
6694            redo B;            $parse_rcdata->(CDATA_CONTENT_MODEL);
6695          } else {            next B;
6696            !!!parse-error (type => 'in frameset:'.$token->{tag_name});  
6697              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6698              ## has no parse error.
6699            } else {
6700              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6701                !!!cp ('t321');
6702                !!!parse-error (type => 'in frameset',
6703                                text => $token->{tag_name}, token => $token);
6704              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6705                !!!cp ('t322');
6706                !!!parse-error (type => 'after frameset',
6707                                text => $token->{tag_name}, token => $token);
6708              } else { # "after after frameset"
6709                !!!cp ('t322.2');
6710                !!!parse-error (type => 'after after frameset',
6711                                text => $token->{tag_name}, token => $token);
6712              }
6713            ## Ignore the token            ## Ignore the token
6714              !!!nack ('t322.1');
6715            !!!next-token;            !!!next-token;
6716            redo B;            next B;
6717          }          }
6718        } elsif ($token->{type} eq 'end tag') {        } elsif ($token->{type} == END_TAG_TOKEN) {
6719          if ($token->{tag_name} eq 'frameset') {          if ($token->{tag_name} eq 'frameset' and
6720            if ($self->{open_elements}->[-1]->[1] eq 'html' and              $self->{insertion_mode} == IN_FRAMESET_IM) {
6721              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6722                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6723              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6724                !!!parse-error (type => 'unmatched end tag',
6725                                text => $token->{tag_name}, token => $token);
6726              ## Ignore the token              ## Ignore the token
6727              !!!next-token;              !!!next-token;
6728            } else {            } else {
6729                !!!cp ('t326');
6730              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6731              !!!next-token;              !!!next-token;
6732            }            }
6733    
6734            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6735                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6736              $self->{insertion_mode} = 'after frameset';              !!!cp ('t327');
6737                $self->{insertion_mode} = AFTER_FRAMESET_IM;
6738              } else {
6739                !!!cp ('t328');
6740            }            }
6741            redo B;            next B;
6742            } elsif ($token->{tag_name} eq 'html' and
6743                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6744              !!!cp ('t329');
6745              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6746              !!!next-token;
6747              next B;
6748          } else {          } else {
6749            !!!parse-error (type => 'in frameset:/'.$token->{tag_name});            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6750                !!!cp ('t330');
6751                !!!parse-error (type => 'in frameset:/',
6752                                text => $token->{tag_name}, token => $token);
6753              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6754                !!!cp ('t330.1');
6755                !!!parse-error (type => 'after frameset:/',
6756                                text => $token->{tag_name}, token => $token);
6757              } else { # "after after html"
6758                !!!cp ('t331');
6759                !!!parse-error (type => 'after after frameset:/',
6760                                text => $token->{tag_name}, token => $token);
6761              }
6762            ## Ignore the token            ## Ignore the token
6763            !!!next-token;            !!!next-token;
6764            redo B;            next B;
6765          }          }
6766          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6767            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6768                    @{$self->{open_elements}} == 1) { # redundant, maybe
6769              !!!cp ('t331.1');
6770              !!!parse-error (type => 'in body:#eof', token => $token);
6771            } else {
6772              !!!cp ('t331.2');
6773            }
6774            
6775            ## Stop parsing
6776            last B;
6777        } else {        } else {
6778          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6779        }        }
     } elsif ($self->{insertion_mode} eq 'after frameset') {  
       if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
6780    
6781                unless (length $token->{data}) {        ## ISSUE: An issue in spec here
6782                  !!!next-token;      } else {
6783                  redo B;        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6784                }      }
             }  
6785    
6786              if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {      ## "in body" insertion mode
6787                !!!parse-error (type => 'after frameset:#character');      if ($token->{type} == START_TAG_TOKEN) {
6788          if ($token->{tag_name} eq 'script') {
6789            !!!cp ('t332');
6790            ## NOTE: This is an "as if in head" code clone
6791            $script_start_tag->();
6792            next B;
6793          } elsif ($token->{tag_name} eq 'style') {
6794            !!!cp ('t333');
6795            ## NOTE: This is an "as if in head" code clone
6796            $parse_rcdata->(CDATA_CONTENT_MODEL);
6797            next B;
6798          } elsif ({
6799                    base => 1, link => 1,
6800                   }->{$token->{tag_name}}) {
6801            !!!cp ('t334');
6802            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6803            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6804            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6805            !!!ack ('t334.1');
6806            !!!next-token;
6807            next B;
6808          } elsif ($token->{tag_name} eq 'meta') {
6809            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6810            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6811            my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6812    
6813                ## Ignore the token.          unless ($self->{confident}) {
6814                if (length $token->{data}) {            if ($token->{attributes}->{charset}) {
6815                  ## reprocess the rest of characters              !!!cp ('t335');
6816                } else {              ## NOTE: Whether the encoding is supported or not is handled
6817                  !!!next-token;              ## in the {change_encoding} callback.
6818                }              $self->{change_encoding}
6819                redo B;                  ->($self, $token->{attributes}->{charset}->{value}, $token);
6820                
6821                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6822                    ->set_user_data (manakai_has_reference =>
6823                                         $token->{attributes}->{charset}
6824                                             ->{has_reference});
6825              } elsif ($token->{attributes}->{content}) {
6826                if ($token->{attributes}->{content}->{value}
6827                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6828                        [\x09\x0A\x0C\x0D\x20]*=
6829                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6830                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6831                       /x) {
6832                  !!!cp ('t336');
6833                  ## NOTE: Whether the encoding is supported or not is handled
6834                  ## in the {change_encoding} callback.
6835                  $self->{change_encoding}
6836                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6837                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6838                      ->set_user_data (manakai_has_reference =>
6839                                           $token->{attributes}->{content}
6840                                                 ->{has_reference});
6841              }              }
6842              }
6843            } else {
6844              if ($token->{attributes}->{charset}) {
6845                !!!cp ('t337');
6846                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6847                    ->set_user_data (manakai_has_reference =>
6848                                         $token->{attributes}->{charset}
6849                                             ->{has_reference});
6850              }
6851              if ($token->{attributes}->{content}) {
6852                !!!cp ('t338');
6853                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6854                    ->set_user_data (manakai_has_reference =>
6855                                         $token->{attributes}->{content}
6856                                             ->{has_reference});
6857              }
6858            }
6859    
6860          die qq[$0: Character "$token->{data}"];          !!!ack ('t338.1');
6861        } elsif ($token->{type} eq 'start tag') {          !!!next-token;
6862          if ($token->{tag_name} eq 'noframes') {          next B;
6863            ## NOTE: As if in body.        } elsif ($token->{tag_name} eq 'title') {
6864            $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);          !!!cp ('t341');
6865            redo B;          ## NOTE: This is an "as if in head" code clone
6866            $parse_rcdata->(RCDATA_CONTENT_MODEL);
6867            next B;
6868          } elsif ($token->{tag_name} eq 'body') {
6869            !!!parse-error (type => 'in body', text => 'body', token => $token);
6870                  
6871            if (@{$self->{open_elements}} == 1 or
6872                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6873              !!!cp ('t342');
6874              ## Ignore the token
6875          } else {          } else {
6876            !!!parse-error (type => 'after frameset:'.$token->{tag_name});            my $body_el = $self->{open_elements}->[1]->[0];
6877              for my $attr_name (keys %{$token->{attributes}}) {
6878                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6879                  !!!cp ('t343');
6880                  $body_el->set_attribute_ns
6881                    (undef, [undef, $attr_name],
6882                     $token->{attributes}->{$attr_name}->{value});
6883                }
6884              }
6885            }
6886            !!!nack ('t343.1');
6887            !!!next-token;
6888            next B;
6889          } elsif ({
6890                    address => 1, blockquote => 1, center => 1, dir => 1,
6891                    div => 1, dl => 1, fieldset => 1,
6892                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6893                    menu => 1, ol => 1, p => 1, ul => 1,
6894                    pre => 1, listing => 1,
6895                    form => 1,
6896                    table => 1,
6897                    hr => 1,
6898                   }->{$token->{tag_name}}) {
6899            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6900              !!!cp ('t350');
6901              !!!parse-error (type => 'in form:form', token => $token);
6902            ## Ignore the token            ## Ignore the token
6903              !!!nack ('t350.1');
6904            !!!next-token;            !!!next-token;
6905            redo B;            next B;
6906          }          }
6907        } elsif ($token->{type} eq 'end tag') {  
6908          if ($token->{tag_name} eq 'html') {          ## has a p element in scope
6909            $previous_insertion_mode = $self->{insertion_mode};          INSCOPE: for (reverse @{$self->{open_elements}}) {
6910            $self->{insertion_mode} = 'trailing end';            if ($_->[1] & P_EL) {
6911                !!!cp ('t344');
6912                !!!back-token; # <form>
6913                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6914                          line => $token->{line}, column => $token->{column}};
6915                next B;
6916              } elsif ($_->[1] & SCOPING_EL) {
6917                !!!cp ('t345');
6918                last INSCOPE;
6919              }
6920            } # INSCOPE
6921              
6922            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6923            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6924              !!!nack ('t346.1');
6925              !!!next-token;
6926              if ($token->{type} == CHARACTER_TOKEN) {
6927                $token->{data} =~ s/^\x0A//;
6928                unless (length $token->{data}) {
6929                  !!!cp ('t346');
6930                  !!!next-token;
6931                } else {
6932                  !!!cp ('t349');
6933                }
6934              } else {
6935                !!!cp ('t348');
6936              }
6937            } elsif ($token->{tag_name} eq 'form') {
6938              !!!cp ('t347.1');
6939              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6940    
6941              !!!nack ('t347.2');
6942              !!!next-token;
6943            } elsif ($token->{tag_name} eq 'table') {
6944              !!!cp ('t382');
6945              push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6946              
6947              $self->{insertion_mode} = IN_TABLE_IM;
6948    
6949              !!!nack ('t382.1');
6950              !!!next-token;
6951            } elsif ($token->{tag_name} eq 'hr') {
6952              !!!cp ('t386');
6953              pop @{$self->{open_elements}};
6954            
6955              !!!nack ('t386.1');
6956            !!!next-token;            !!!next-token;
           redo B;  
6957          } else {          } else {
6958            !!!parse-error (type => 'after frameset:/'.$token->{tag_name});            !!!nack ('t347.1');
           ## Ignore the token  
6959            !!!next-token;            !!!next-token;
           redo B;  
6960          }          }
6961        } else {          next B;
6962          die "$0: $token->{type}: Unknown token type";        } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6963        }          ## has a p element in scope
6964            INSCOPE: for (reverse @{$self->{open_elements}}) {
6965              if ($_->[1] & P_EL) {
6966                !!!cp ('t353');
6967                !!!back-token; # <x>
6968                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6969                          line => $token->{line}, column => $token->{column}};
6970                next B;
6971              } elsif ($_->[1] & SCOPING_EL) {
6972                !!!cp ('t354');
6973                last INSCOPE;
6974              }
6975            } # INSCOPE
6976    
6977        ## ISSUE: An issue in spec here          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6978      } elsif ($self->{insertion_mode} eq 'trailing end') {            ## Interpreted as <li><foo/></li><li/> (non-conforming)
6979        ## states in the main stage is preserved yet # MUST            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6980                    ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6981        if ($token->{type} eq 'character') {            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6982          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            ## object (Fx)
6983            my $data = $1;            ## Generate non-tree (non-conforming)
6984            ## As if in the main phase.            ## basefont (IE7 (where basefont is non-void)), center (IE),
6985            ## NOTE: The insertion mode in the main phase            ## form (IE), hn (IE)
6986            ## just before the phase has been changed to the trailing          ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
6987            ## end phase is either "after body" or "after frameset".            ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
6988            $reconstruct_active_formatting_elements->($insert_to_current);            ## div (Fx, S)
6989              
6990            ## Step 1
6991            my $i = -1;
6992            my $node = $self->{open_elements}->[$i];
6993            my $li_or_dtdd = {li => {li => 1},
6994                              dt => {dt => 1, dd => 1},
6995                              dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
6996            LI: {
6997              ## Step 2
6998              if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
6999                if ($i != -1) {
7000                  !!!cp ('t355');
7001                  !!!parse-error (type => 'not closed',
7002                                  text => $self->{open_elements}->[-1]->[0]
7003                                      ->manakai_local_name,
7004                                  token => $token);
7005                } else {
7006                  !!!cp ('t356');
7007                }
7008                splice @{$self->{open_elements}}, $i;
7009                last LI;
7010              } else {
7011                !!!cp ('t357');
7012              }
7013              
7014              ## Step 3
7015              if (not ($node->[1] & FORMATTING_EL) and
7016                  #not $phrasing_category->{$node->[1]} and
7017                  ($node->[1] & SPECIAL_EL or
7018                   $node->[1] & SCOPING_EL) and
7019                  not ($node->[1] & ADDRESS_EL) and
7020                  not ($node->[1] & DIV_EL)) {
7021                !!!cp ('t358');
7022                last LI;
7023              }
7024              
7025              !!!cp ('t359');
7026              ## Step 4
7027              $i--;
7028              $node = $self->{open_elements}->[$i];
7029              redo LI;
7030            } # LI
7031              
7032            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7033            !!!nack ('t359.1');
7034            !!!next-token;
7035            next B;
7036          } elsif ($token->{tag_name} eq 'plaintext') {
7037            ## has a p element in scope
7038            INSCOPE: for (reverse @{$self->{open_elements}}) {
7039              if ($_->[1] & P_EL) {
7040                !!!cp ('t367');
7041                !!!back-token; # <plaintext>
7042                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7043                          line => $token->{line}, column => $token->{column}};
7044                next B;
7045              } elsif ($_->[1] & SCOPING_EL) {
7046                !!!cp ('t368');
7047                last INSCOPE;
7048              }
7049            } # INSCOPE
7050              
7051            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7052              
7053            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7054              
7055            !!!nack ('t368.1');
7056            !!!next-token;
7057            next B;
7058          } elsif ($token->{tag_name} eq 'a') {
7059            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7060              my $node = $active_formatting_elements->[$i];
7061              if ($node->[1] & A_EL) {
7062                !!!cp ('t371');
7063                !!!parse-error (type => 'in a:a', token => $token);
7064                
7065                !!!back-token; # <a>
7066                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7067                          line => $token->{line}, column => $token->{column}};
7068                $formatting_end_tag->($token);
7069                
7070                AFE2: for (reverse 0..$#$active_formatting_elements) {
7071                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7072                    !!!cp ('t372');
7073                    splice @$active_formatting_elements, $_, 1;
7074                    last AFE2;
7075                  }
7076                } # AFE2
7077                OE: for (reverse 0..$#{$self->{open_elements}}) {
7078                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7079                    !!!cp ('t373');
7080                    splice @{$self->{open_elements}}, $_, 1;
7081                    last OE;
7082                  }
7083                } # OE
7084                last AFE;
7085              } elsif ($node->[0] eq '#marker') {
7086                !!!cp ('t374');
7087                last AFE;
7088              }
7089            } # AFE
7090                        
7091            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);          $reconstruct_active_formatting_elements->($insert_to_current);
7092    
7093            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7094            push @$active_formatting_elements, $self->{open_elements}->[-1];
7095    
7096            !!!nack ('t374.1');
7097            !!!next-token;
7098            next B;
7099          } elsif ($token->{tag_name} eq 'nobr') {
7100            $reconstruct_active_formatting_elements->($insert_to_current);
7101    
7102            ## has a |nobr| element in scope
7103            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7104              my $node = $self->{open_elements}->[$_];
7105              if ($node->[1] & NOBR_EL) {
7106                !!!cp ('t376');
7107                !!!parse-error (type => 'in nobr:nobr', token => $token);
7108                !!!back-token; # <nobr>
7109                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7110                          line => $token->{line}, column => $token->{column}};
7111                next B;
7112              } elsif ($node->[1] & SCOPING_EL) {
7113                !!!cp ('t377');
7114                last INSCOPE;
7115              }
7116            } # INSCOPE
7117            
7118            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7119            push @$active_formatting_elements, $self->{open_elements}->[-1];
7120            
7121            !!!nack ('t377.1');
7122            !!!next-token;
7123            next B;
7124          } elsif ($token->{tag_name} eq 'button') {
7125            ## has a button element in scope
7126            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7127              my $node = $self->{open_elements}->[$_];
7128              if ($node->[1] & BUTTON_EL) {
7129                !!!cp ('t378');
7130                !!!parse-error (type => 'in button:button', token => $token);
7131                !!!back-token; # <button>
7132                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7133                          line => $token->{line}, column => $token->{column}};
7134                next B;
7135              } elsif ($node->[1] & SCOPING_EL) {
7136                !!!cp ('t379');
7137                last INSCOPE;
7138              }
7139            } # INSCOPE
7140              
7141            $reconstruct_active_formatting_elements->($insert_to_current);
7142                        
7143            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7144    
7145            ## TODO: associate with $self->{form_element} if defined
7146    
7147            push @$active_formatting_elements, ['#marker', ''];
7148    
7149            !!!nack ('t379.1');
7150            !!!next-token;
7151            next B;
7152          } elsif ({
7153                    xmp => 1,
7154                    iframe => 1,
7155                    noembed => 1,
7156                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7157                    noscript => 0, ## TODO: 1 if scripting is enabled
7158                   }->{$token->{tag_name}}) {
7159            if ($token->{tag_name} eq 'xmp') {
7160              !!!cp ('t381');
7161              $reconstruct_active_formatting_elements->($insert_to_current);
7162            } else {
7163              !!!cp ('t399');
7164            }
7165            ## NOTE: There is an "as if in body" code clone.
7166            $parse_rcdata->(CDATA_CONTENT_MODEL);
7167            next B;
7168          } elsif ($token->{tag_name} eq 'isindex') {
7169            !!!parse-error (type => 'isindex', token => $token);
7170            
7171            if (defined $self->{form_element}) {
7172              !!!cp ('t389');
7173              ## Ignore the token
7174              !!!nack ('t389'); ## NOTE: Not acknowledged.
7175              !!!next-token;
7176              next B;
7177            } else {
7178              !!!ack ('t391.1');
7179    
7180              my $at = $token->{attributes};
7181              my $form_attrs;
7182              $form_attrs->{action} = $at->{action} if $at->{action};
7183              my $prompt_attr = $at->{prompt};
7184              $at->{name} = {name => 'name', value => 'isindex'};
7185              delete $at->{action};
7186              delete $at->{prompt};
7187              my @tokens = (
7188                            {type => START_TAG_TOKEN, tag_name => 'form',
7189                             attributes => $form_attrs,
7190                             line => $token->{line}, column => $token->{column}},
7191                            {type => START_TAG_TOKEN, tag_name => 'hr',
7192                             line => $token->{line}, column => $token->{column}},
7193                            {type => START_TAG_TOKEN, tag_name => 'p',
7194                             line => $token->{line}, column => $token->{column}},
7195                            {type => START_TAG_TOKEN, tag_name => 'label',
7196                             line => $token->{line}, column => $token->{column}},
7197                           );
7198              if ($prompt_attr) {
7199                !!!cp ('t390');
7200                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7201                               #line => $token->{line}, column => $token->{column},
7202                              };
7203              } else {
7204                !!!cp ('t391');
7205                push @tokens, {type => CHARACTER_TOKEN,
7206                               data => 'This is a searchable index. Insert your search keywords here: ',
7207                               #line => $token->{line}, column => $token->{column},
7208                              }; # SHOULD
7209                ## TODO: make this configurable
7210              }
7211              push @tokens,
7212                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7213                             line => $token->{line}, column => $token->{column}},
7214                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7215                            {type => END_TAG_TOKEN, tag_name => 'label',
7216                             line => $token->{line}, column => $token->{column}},
7217                            {type => END_TAG_TOKEN, tag_name => 'p',
7218                             line => $token->{line}, column => $token->{column}},
7219                            {type => START_TAG_TOKEN, tag_name => 'hr',
7220                             line => $token->{line}, column => $token->{column}},
7221                            {type => END_TAG_TOKEN, tag_name => 'form',
7222                             line => $token->{line}, column => $token->{column}};
7223              !!!back-token (@tokens);
7224              !!!next-token;
7225              next B;
7226            }
7227          } elsif ($token->{tag_name} eq 'textarea') {
7228            my $tag_name = $token->{tag_name};
7229            my $el;
7230            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7231            
7232            ## TODO: $self->{form_element} if defined
7233            $self->{content_model} = RCDATA_CONTENT_MODEL;
7234            delete $self->{escape}; # MUST
7235            
7236            $insert->($el);
7237            
7238            my $text = '';
7239            !!!nack ('t392.1');
7240            !!!next-token;
7241            if ($token->{type} == CHARACTER_TOKEN) {
7242              $token->{data} =~ s/^\x0A//;
7243            unless (length $token->{data}) {            unless (length $token->{data}) {
7244                !!!cp ('t392');
7245              !!!next-token;              !!!next-token;
7246              redo B;            } else {
7247                !!!cp ('t393');
7248            }            }
7249            } else {
7250              !!!cp ('t394');
7251          }          }
7252            while ($token->{type} == CHARACTER_TOKEN) {
7253              !!!cp ('t395');
7254              $text .= $token->{data};
7255              !!!next-token;
7256            }
7257            if (length $text) {
7258              !!!cp ('t396');
7259              $el->manakai_append_text ($text);
7260            }
7261            
7262            $self->{content_model} = PCDATA_CONTENT_MODEL;
7263            
7264            if ($token->{type} == END_TAG_TOKEN and
7265                $token->{tag_name} eq $tag_name) {
7266              !!!cp ('t397');
7267              ## Ignore the token
7268            } else {
7269              !!!cp ('t398');
7270              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7271            }
7272            !!!next-token;
7273            next B;
7274          } elsif ($token->{tag_name} eq 'rt' or
7275                   $token->{tag_name} eq 'rp') {
7276            ## has a |ruby| element in scope
7277            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7278              my $node = $self->{open_elements}->[$_];
7279              if ($node->[1] & RUBY_EL) {
7280                !!!cp ('t398.1');
7281                ## generate implied end tags
7282                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7283                  !!!cp ('t398.2');
7284                  pop @{$self->{open_elements}};
7285                }
7286                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7287                  !!!cp ('t398.3');
7288                  !!!parse-error (type => 'not closed',
7289                                  text => $self->{open_elements}->[-1]->[0]
7290                                      ->manakai_local_name,
7291                                  token => $token);
7292                  pop @{$self->{open_elements}}
7293                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7294                }
7295                last INSCOPE;
7296              } elsif ($node->[1] & SCOPING_EL) {
7297                !!!cp ('t398.4');
7298                last INSCOPE;
7299              }
7300            } # INSCOPE
7301    
7302          !!!parse-error (type => 'after html:#character');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7303          $self->{insertion_mode} = $previous_insertion_mode;  
7304          ## reprocess          !!!nack ('t398.5');
7305          redo B;          !!!next-token;
       } elsif ($token->{type} eq 'start tag') {  
         !!!parse-error (type => 'after html:'.$token->{tag_name});  
         $self->{insertion_mode} = $previous_insertion_mode;  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'end tag') {  
         !!!parse-error (type => 'after html:/'.$token->{tag_name});  
         $self->{insertion_mode} = $previous_insertion_mode;  
         ## reprocess  
7306          redo B;          redo B;
7307          } elsif ($token->{tag_name} eq 'math' or
7308                   $token->{tag_name} eq 'svg') {
7309            $reconstruct_active_formatting_elements->($insert_to_current);
7310    
7311            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7312    
7313            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7314    
7315            ## "adjust foreign attributes" - done in insert-element-f
7316            
7317            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7318            
7319            if ($self->{self_closing}) {
7320              pop @{$self->{open_elements}};
7321              !!!ack ('t398.1');
7322            } else {
7323              !!!cp ('t398.2');
7324              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7325              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7326              ## mode, "in body" (not "in foreign content") secondary insertion
7327              ## mode, maybe.
7328            }
7329    
7330            !!!next-token;
7331            next B;
7332          } elsif ({
7333                    caption => 1, col => 1, colgroup => 1, frame => 1,
7334                    frameset => 1, head => 1, option => 1, optgroup => 1,
7335                    tbody => 1, td => 1, tfoot => 1, th => 1,
7336                    thead => 1, tr => 1,
7337                   }->{$token->{tag_name}}) {
7338            !!!cp ('t401');
7339            !!!parse-error (type => 'in body',
7340                            text => $token->{tag_name}, token => $token);
7341            ## Ignore the token
7342            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7343            !!!next-token;
7344            next B;
7345            
7346            ## ISSUE: An issue on HTML5 new elements in the spec.
7347        } else {        } else {
7348          die "$0: $token->{type}: Unknown token";          if ($token->{tag_name} eq 'image') {
7349              !!!cp ('t384');
7350              !!!parse-error (type => 'image', token => $token);
7351              $token->{tag_name} = 'img';
7352            } else {
7353              !!!cp ('t385');
7354            }
7355    
7356            ## NOTE: There is an "as if <br>" code clone.
7357            $reconstruct_active_formatting_elements->($insert_to_current);
7358            
7359            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7360    
7361            if ({
7362                 applet => 1, marquee => 1, object => 1,
7363                }->{$token->{tag_name}}) {
7364              !!!cp ('t380');
7365              push @$active_formatting_elements, ['#marker', ''];
7366              !!!nack ('t380.1');
7367            } elsif ({
7368                      b => 1, big => 1, em => 1, font => 1, i => 1,
7369                      s => 1, small => 1, strike => 1,
7370                      strong => 1, tt => 1, u => 1,
7371                     }->{$token->{tag_name}}) {
7372              !!!cp ('t375');
7373              push @$active_formatting_elements, $self->{open_elements}->[-1];
7374              !!!nack ('t375.1');
7375            } elsif ($token->{tag_name} eq 'input') {
7376              !!!cp ('t388');
7377              ## TODO: associate with $self->{form_element} if defined
7378              pop @{$self->{open_elements}};
7379              !!!ack ('t388.2');
7380            } elsif ({
7381                      area => 1, basefont => 1, bgsound => 1, br => 1,
7382                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
7383                      #image => 1,
7384                     }->{$token->{tag_name}}) {
7385              !!!cp ('t388.1');
7386              pop @{$self->{open_elements}};
7387              !!!ack ('t388.3');
7388            } elsif ($token->{tag_name} eq 'select') {
7389              ## TODO: associate with $self->{form_element} if defined
7390            
7391              if ($self->{insertion_mode} & TABLE_IMS or
7392                  $self->{insertion_mode} & BODY_TABLE_IMS or
7393                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7394                !!!cp ('t400.1');
7395                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7396              } else {
7397                !!!cp ('t400.2');
7398                $self->{insertion_mode} = IN_SELECT_IM;
7399              }
7400              !!!nack ('t400.3');
7401            } else {
7402              !!!nack ('t402');
7403            }
7404            
7405            !!!next-token;
7406            next B;
7407          }
7408        } elsif ($token->{type} == END_TAG_TOKEN) {
7409          if ($token->{tag_name} eq 'body') {
7410            ## has a |body| element in scope
7411            my $i;
7412            INSCOPE: {
7413              for (reverse @{$self->{open_elements}}) {
7414                if ($_->[1] & BODY_EL) {
7415                  !!!cp ('t405');
7416                  $i = $_;
7417                  last INSCOPE;
7418                } elsif ($_->[1] & SCOPING_EL) {
7419                  !!!cp ('t405.1');
7420                  last;
7421                }
7422              }
7423    
7424              !!!parse-error (type => 'start tag not allowed',
7425                              text => $token->{tag_name}, token => $token);
7426              ## NOTE: Ignore the token.
7427              !!!next-token;
7428              next B;
7429            } # INSCOPE
7430    
7431            for (@{$self->{open_elements}}) {
7432              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7433                !!!cp ('t403');
7434                !!!parse-error (type => 'not closed',
7435                                text => $_->[0]->manakai_local_name,
7436                                token => $token);
7437                last;
7438              } else {
7439                !!!cp ('t404');
7440              }
7441            }
7442    
7443            $self->{insertion_mode} = AFTER_BODY_IM;
7444            !!!next-token;
7445            next B;
7446          } elsif ($token->{tag_name} eq 'html') {
7447            ## TODO: Update this code.  It seems that the code below is not
7448            ## up-to-date, though it has same effect as speced.
7449            if (@{$self->{open_elements}} > 1 and
7450                $self->{open_elements}->[1]->[1] & BODY_EL) {
7451              ## ISSUE: There is an issue in the spec.
7452              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7453                !!!cp ('t406');
7454                !!!parse-error (type => 'not closed',
7455                                text => $self->{open_elements}->[1]->[0]
7456                                    ->manakai_local_name,
7457                                token => $token);
7458              } else {
7459                !!!cp ('t407');
7460              }
7461              $self->{insertion_mode} = AFTER_BODY_IM;
7462              ## reprocess
7463              next B;
7464            } else {
7465              !!!cp ('t408');
7466              !!!parse-error (type => 'unmatched end tag',
7467                              text => $token->{tag_name}, token => $token);
7468              ## Ignore the token
7469              !!!next-token;
7470              next B;
7471            }
7472          } elsif ({
7473                    address => 1, blockquote => 1, center => 1, dir => 1,
7474                    div => 1, dl => 1, fieldset => 1, listing => 1,
7475                    menu => 1, ol => 1, pre => 1, ul => 1,
7476                    dd => 1, dt => 1, li => 1,
7477                    applet => 1, button => 1, marquee => 1, object => 1,
7478                   }->{$token->{tag_name}}) {
7479            ## has an element in scope
7480            my $i;
7481            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7482              my $node = $self->{open_elements}->[$_];
7483              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7484                !!!cp ('t410');
7485                $i = $_;
7486                last INSCOPE;
7487              } elsif ($node->[1] & SCOPING_EL) {
7488                !!!cp ('t411');
7489                last INSCOPE;
7490              }
7491            } # INSCOPE
7492    
7493            unless (defined $i) { # has an element in scope
7494              !!!cp ('t413');
7495              !!!parse-error (type => 'unmatched end tag',
7496                              text => $token->{tag_name}, token => $token);
7497              ## NOTE: Ignore the token.
7498            } else {
7499              ## Step 1. generate implied end tags
7500              while ({
7501                      ## END_TAG_OPTIONAL_EL
7502                      dd => ($token->{tag_name} ne 'dd'),
7503                      dt => ($token->{tag_name} ne 'dt'),
7504                      li => ($token->{tag_name} ne 'li'),
7505                      p => 1,
7506                      rt => 1,
7507                      rp => 1,
7508                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7509                !!!cp ('t409');
7510                pop @{$self->{open_elements}};
7511              }
7512    
7513              ## Step 2.
7514              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7515                      ne $token->{tag_name}) {
7516                !!!cp ('t412');
7517                !!!parse-error (type => 'not closed',
7518                                text => $self->{open_elements}->[-1]->[0]
7519                                    ->manakai_local_name,
7520                                token => $token);
7521              } else {
7522                !!!cp ('t414');
7523              }
7524    
7525              ## Step 3.
7526              splice @{$self->{open_elements}}, $i;
7527    
7528              ## Step 4.
7529              $clear_up_to_marker->()
7530                  if {
7531                    applet => 1, button => 1, marquee => 1, object => 1,
7532                  }->{$token->{tag_name}};
7533            }
7534            !!!next-token;
7535            next B;
7536          } elsif ($token->{tag_name} eq 'form') {
7537            undef $self->{form_element};
7538    
7539            ## has an element in scope
7540            my $i;
7541            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7542              my $node = $self->{open_elements}->[$_];
7543              if ($node->[1] & FORM_EL) {
7544                !!!cp ('t418');
7545                $i = $_;
7546                last INSCOPE;
7547              } elsif ($node->[1] & SCOPING_EL) {
7548                !!!cp ('t419');
7549                last INSCOPE;
7550              }
7551            } # INSCOPE
7552    
7553            unless (defined $i) { # has an element in scope
7554              !!!cp ('t421');
7555              !!!parse-error (type => 'unmatched end tag',
7556                              text => $token->{tag_name}, token => $token);
7557              ## NOTE: Ignore the token.
7558            } else {
7559              ## Step 1. generate implied end tags
7560              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7561                !!!cp ('t417');
7562                pop @{$self->{open_elements}};
7563              }
7564              
7565              ## Step 2.
7566              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7567                      ne $token->{tag_name}) {
7568                !!!cp ('t417.1');
7569                !!!parse-error (type => 'not closed',
7570                                text => $self->{open_elements}->[-1]->[0]
7571                                    ->manakai_local_name,
7572                                token => $token);
7573              } else {
7574                !!!cp ('t420');
7575              }  
7576              
7577              ## Step 3.
7578              splice @{$self->{open_elements}}, $i;
7579            }
7580    
7581            !!!next-token;
7582            next B;
7583          } elsif ({
7584                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7585                   }->{$token->{tag_name}}) {
7586            ## has an element in scope
7587            my $i;
7588            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7589              my $node = $self->{open_elements}->[$_];
7590              if ($node->[1] & HEADING_EL) {
7591                !!!cp ('t423');
7592                $i = $_;
7593                last INSCOPE;
7594              } elsif ($node->[1] & SCOPING_EL) {
7595                !!!cp ('t424');
7596                last INSCOPE;
7597              }
7598            } # INSCOPE
7599    
7600            unless (defined $i) { # has an element in scope
7601              !!!cp ('t425.1');
7602              !!!parse-error (type => 'unmatched end tag',
7603                              text => $token->{tag_name}, token => $token);
7604              ## NOTE: Ignore the token.
7605            } else {
7606              ## Step 1. generate implied end tags
7607              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7608                !!!cp ('t422');
7609                pop @{$self->{open_elements}};
7610              }
7611              
7612              ## Step 2.
7613              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7614                      ne $token->{tag_name}) {
7615                !!!cp ('t425');
7616                !!!parse-error (type => 'unmatched end tag',
7617                                text => $token->{tag_name}, token => $token);
7618              } else {
7619                !!!cp ('t426');
7620              }
7621    
7622              ## Step 3.
7623              splice @{$self->{open_elements}}, $i;
7624            }
7625            
7626            !!!next-token;
7627            next B;
7628          } elsif ($token->{tag_name} eq 'p') {
7629            ## has an element in scope
7630            my $i;
7631            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7632              my $node = $self->{open_elements}->[$_];
7633              if ($node->[1] & P_EL) {
7634                !!!cp ('t410.1');
7635                $i = $_;
7636                last INSCOPE;
7637              } elsif ($node->[1] & SCOPING_EL) {
7638                !!!cp ('t411.1');
7639                last INSCOPE;
7640              }
7641            } # INSCOPE
7642    
7643            if (defined $i) {
7644              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7645                      ne $token->{tag_name}) {
7646                !!!cp ('t412.1');
7647                !!!parse-error (type => 'not closed',
7648                                text => $self->{open_elements}->[-1]->[0]
7649                                    ->manakai_local_name,
7650                                token => $token);
7651              } else {
7652                !!!cp ('t414.1');
7653              }
7654    
7655              splice @{$self->{open_elements}}, $i;
7656            } else {
7657              !!!cp ('t413.1');
7658              !!!parse-error (type => 'unmatched end tag',
7659                              text => $token->{tag_name}, token => $token);
7660    
7661              !!!cp ('t415.1');
7662              ## As if <p>, then reprocess the current token
7663              my $el;
7664              !!!create-element ($el, $HTML_NS, 'p',, $token);
7665              $insert->($el);
7666              ## NOTE: Not inserted into |$self->{open_elements}|.
7667            }
7668    
7669            !!!next-token;
7670            next B;
7671          } elsif ({
7672                    a => 1,
7673                    b => 1, big => 1, em => 1, font => 1, i => 1,
7674                    nobr => 1, s => 1, small => 1, strike => 1,
7675                    strong => 1, tt => 1, u => 1,
7676                   }->{$token->{tag_name}}) {
7677            !!!cp ('t427');
7678            $formatting_end_tag->($token);
7679            next B;
7680          } elsif ($token->{tag_name} eq 'br') {
7681            !!!cp ('t428');
7682            !!!parse-error (type => 'unmatched end tag',
7683                            text => 'br', token => $token);
7684    
7685            ## As if <br>
7686            $reconstruct_active_formatting_elements->($insert_to_current);
7687            
7688            my $el;
7689            !!!create-element ($el, $HTML_NS, 'br',, $token);
7690            $insert->($el);
7691            
7692            ## Ignore the token.
7693            !!!next-token;
7694            next B;
7695          } elsif ({
7696                    caption => 1, col => 1, colgroup => 1, frame => 1,
7697                    frameset => 1, head => 1, option => 1, optgroup => 1,
7698                    tbody => 1, td => 1, tfoot => 1, th => 1,
7699                    thead => 1, tr => 1,
7700                    area => 1, basefont => 1, bgsound => 1,
7701                    embed => 1, hr => 1, iframe => 1, image => 1,
7702                    img => 1, input => 1, isindex => 1, noembed => 1,
7703                    noframes => 1, param => 1, select => 1, spacer => 1,
7704                    table => 1, textarea => 1, wbr => 1,
7705                    noscript => 0, ## TODO: if scripting is enabled
7706                   }->{$token->{tag_name}}) {
7707            !!!cp ('t429');
7708            !!!parse-error (type => 'unmatched end tag',
7709                            text => $token->{tag_name}, token => $token);
7710            ## Ignore the token
7711            !!!next-token;
7712            next B;
7713            
7714            ## ISSUE: Issue on HTML5 new elements in spec
7715            
7716          } else {
7717            ## Step 1
7718            my $node_i = -1;
7719            my $node = $self->{open_elements}->[$node_i];
7720    
7721            ## Step 2
7722            S2: {
7723              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7724                ## Step 1
7725                ## generate implied end tags
7726                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7727                  !!!cp ('t430');
7728                  ## NOTE: |<ruby><rt></ruby>|.
7729                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7730                  ## which seems wrong.
7731                  pop @{$self->{open_elements}};
7732                  $node_i++;
7733                }
7734            
7735                ## Step 2
7736                if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7737                        ne $token->{tag_name}) {
7738                  !!!cp ('t431');
7739                  ## NOTE: <x><y></x>
7740                  !!!parse-error (type => 'not closed',
7741                                  text => $self->{open_elements}->[-1]->[0]
7742                                      ->manakai_local_name,
7743                                  token => $token);
7744                } else {
7745                  !!!cp ('t432');
7746                }
7747                
7748                ## Step 3
7749                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7750    
7751                !!!next-token;
7752                last S2;
7753              } else {
7754                ## Step 3
7755                if (not ($node->[1] & FORMATTING_EL) and
7756                    #not $phrasing_category->{$node->[1]} and
7757                    ($node->[1] & SPECIAL_EL or
7758                     $node->[1] & SCOPING_EL)) {
7759                  !!!cp ('t433');
7760                  !!!parse-error (type => 'unmatched end tag',
7761                                  text => $token->{tag_name}, token => $token);
7762                  ## Ignore the token
7763                  !!!next-token;
7764                  last S2;
7765    
7766                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7767                  ## 9.27, "a" is a child of <dd> (conforming).  In
7768                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7769                  ## "a" is a child of both <body> and <dd>.
7770                }
7771                
7772                !!!cp ('t434');
7773              }
7774              
7775              ## Step 4
7776              $node_i--;
7777              $node = $self->{open_elements}->[$node_i];
7778              
7779              ## Step 5;
7780              redo S2;
7781            } # S2
7782            next B;
7783        }        }
7784      } else {      }
7785        die "$0: $self->{insertion_mode}: Unknown insertion mode";      next B;
7786      } continue { # B
7787        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7788          ## NOTE: The code below is executed in cases where it does not have
7789          ## to be, but it it is harmless even in those cases.
7790          ## has an element in scope
7791          INSCOPE: {
7792            for (reverse 0..$#{$self->{open_elements}}) {
7793              my $node = $self->{open_elements}->[$_];
7794              if ($node->[1] & FOREIGN_EL) {
7795                last INSCOPE;
7796              } elsif ($node->[1] & SCOPING_EL) {
7797                last;
7798              }
7799            }
7800            
7801            ## NOTE: No foreign element in scope.
7802            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7803          } # INSCOPE
7804      }      }
7805    } # B    } # B
7806    
# Line 5208  sub _tree_construction_main ($) { Line 7809  sub _tree_construction_main ($) {
7809    ## TODO: script stuffs    ## TODO: script stuffs
7810  } # _tree_construct_main  } # _tree_construct_main
7811    
7812  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7813    my $class = shift;    my $class = shift;
7814    my $node = shift;    my $node = shift;
7815    my $s = \$_[0];    #my $s = \$_[0];
7816    my $onerror = $_[1];    my $onerror = $_[1];
7817      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7818    
7819      ## ISSUE: Should {confident} be true?
7820    
7821    my $nt = $node->node_type;    my $nt = $node->node_type;
7822    if ($nt == 9) {    if ($nt == 9) {
# Line 5229  sub set_inner_html ($$$) { Line 7833  sub set_inner_html ($$$) {
7833      }      }
7834    
7835      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7836      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7837    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7838      ## TODO: If non-html element      ## TODO: If non-html element
7839    
7840      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7841    
7842    ## TODO: Support for $get_wrapper
7843    
7844      ## Step 1 # MUST      ## Step 1 # MUST
7845      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7846      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5242  sub set_inner_html ($$$) { Line 7848  sub set_inner_html ($$$) {
7848      my $p = $class->new;      my $p = $class->new;
7849      $p->{document} = $doc;      $p->{document} = $doc;
7850    
7851      ## Step 9 # MUST      ## Step 8 # MUST
7852      my $i = 0;      my $i = 0;
7853      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
7854      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
7855      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
7856        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7857        $input = $get_wrapper->($input);
7858        $p->{set_nc} = sub {
7859        my $self = shift;        my $self = shift;
7860    
7861        pop @{$self->{prev_input_character}};        my $char = '';
7862        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
7863            $char = $self->{next_nc};
7864            delete $self->{next_nc};
7865            $self->{nc} = ord $char;
7866          } else {
7867            $self->{char_buffer} = '';
7868            $self->{char_buffer_pos} = 0;
7869            
7870            my $count = $input->manakai_read_until
7871                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7872                 $self->{char_buffer_pos});
7873            if ($count) {
7874              $self->{line_prev} = $self->{line};
7875              $self->{column_prev} = $self->{column};
7876              $self->{column}++;
7877              $self->{nc}
7878                  = ord substr ($self->{char_buffer},
7879                                $self->{char_buffer_pos}++, 1);
7880              return;
7881            }
7882            
7883            if ($input->read ($char, 1)) {
7884              $self->{nc} = ord $char;
7885            } else {
7886              $self->{nc} = -1;
7887              return;
7888            }
7889          }
7890    
7891        $self->{next_input_character} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7892        $self->{next_input_character} = ord substr $$s, $i++, 1;        $p->{column}++;
7893        $column++;  
7894          if ($self->{nc} == 0x000A) { # LF
7895        if ($self->{next_input_character} == 0x000A) { # LF          $p->{line}++;
7896          $line++;          $p->{column} = 0;
7897          $column = 0;          !!!cp ('i1');
7898        } elsif ($self->{next_input_character} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7899          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7900          $self->{next_input_character} = 0x000A; # LF # MUST          my $next = '';
7901          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
7902          $column = 0;            $self->{next_nc} = $next;
7903        } elsif ($self->{next_input_character} > 0x10FFFF) {          }
7904          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
7905        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $p->{line}++;
7906            $p->{column} = 0;
7907            !!!cp ('i2');
7908          } elsif ($self->{nc} == 0x0000) { # NULL
7909            !!!cp ('i4');
7910          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7911          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7912        }        }
7913      };      };
7914      $p->{prev_input_character} = [-1, -1, -1];  
7915      $p->{next_input_character} = -1;      $p->{read_until} = sub {
7916              #my ($scalar, $specials_range, $offset) = @_;
7917          return 0 if defined $p->{next_nc};
7918    
7919          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7920          my $offset = $_[2] || 0;
7921          
7922          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7923            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7924            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7925              substr ($_[0], $offset)
7926                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7927              my $count = $+[0] - $-[0];
7928              if ($count) {
7929                $p->{column} += $count;
7930                $p->{char_buffer_pos} += $count;
7931                $p->{line_prev} = $p->{line};
7932                $p->{column_prev} = $p->{column} - 1;
7933                $p->{nc} = -1;
7934              }
7935              return $count;
7936            } else {
7937              return 0;
7938            }
7939          } else {
7940            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7941            if ($count) {
7942              $p->{column} += $count;
7943              $p->{column_prev} += $count;
7944              $p->{nc} = -1;
7945            }
7946            return $count;
7947          }
7948        }; # $p->{read_until}
7949    
7950      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7951        my (%opt) = @_;        my (%opt) = @_;
7952        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
7953          my $column = $opt{column};
7954          if (defined $opt{token} and defined $opt{token}->{line}) {
7955            $line = $opt{token}->{line};
7956            $column = $opt{token}->{column};
7957          }
7958          warn "Parse error ($opt{type}) at line $line column $column\n";
7959      };      };
7960      $p->{parse_error} = sub {      $p->{parse_error} = sub {
7961        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7962      };      };
7963            
7964        my $char_onerror = sub {
7965          my (undef, $type, %opt) = @_;
7966          $ponerror->(layer => 'encode',
7967                      line => $p->{line}, column => $p->{column} + 1,
7968                      %opt, type => $type);
7969        }; # $char_onerror
7970        $input->onerror ($char_onerror);
7971    
7972      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7973      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7974    
7975      ## Step 2      ## Step 2
7976      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
7977      $p->{content_model} = {      $p->{content_model} = {
7978        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
7979        textarea => RCDATA_CONTENT_MODEL,        textarea => RCDATA_CONTENT_MODEL,
# Line 5303  sub set_inner_html ($$$) { Line 7990  sub set_inner_html ($$$) {
7990          unless defined $p->{content_model};          unless defined $p->{content_model};
7991          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
7992    
7993      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
7994          ## TODO: Foreign element OK?
7995    
7996      ## Step 4      ## Step 3
7997      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
7998        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
7999    
8000      ## Step 5 # MUST      ## Step 4 # MUST
8001      $doc->append_child ($root);      $doc->append_child ($root);
8002    
8003      ## Step 6 # MUST      ## Step 5 # MUST
8004      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8005    
8006      undef $p->{head_element};      undef $p->{head_element};
8007    
8008      ## Step 7 # MUST      ## Step 6 # MUST
8009      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8010    
8011      ## Step 8 # MUST      ## Step 7 # MUST
8012      my $anode = $node;      my $anode = $node;
8013      AN: while (defined $anode) {      AN: while (defined $anode) {
8014        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8015          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8016          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8017            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8018                !!!cp ('i5');
8019              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8020              last AN;              last AN;
8021            }            }
# Line 5335  sub set_inner_html ($$$) { Line 8024  sub set_inner_html ($$$) {
8024        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8025      } # AN      } # AN
8026            
8027      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8028      {      {
8029        my $self = $p;        my $self = $p;
8030        !!!next-token;        !!!next-token;
8031      }      }
8032      $p->_tree_construction_main;      $p->_tree_construction_main;
8033    
8034      ## Step 11 # MUST      ## Step 10 # MUST
8035      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8036      for (@cn) {      for (@cn) {
8037        $node->remove_child ($_);        $node->remove_child ($_);
8038      }      }
8039      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8040    
8041      ## Step 12 # MUST      ## Step 11 # MUST
8042      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8043      for (@cn) {      for (@cn) {
8044        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5359  sub set_inner_html ($$$) { Line 8047  sub set_inner_html ($$$) {
8047      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8048    
8049      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8050    
8051        delete $p->{parse_error}; # delete loop
8052    } else {    } else {
8053      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";
8054    }    }
# Line 5366  sub set_inner_html ($$$) { Line 8056  sub set_inner_html ($$$) {
8056    
8057  } # tree construction stage  } # tree construction stage
8058    
8059  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8060    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 = $child->tag_name; ## TODO: manakai_tag_name  
       $s .= '<' . $tag_name;  
       ## NOTE: Non-HTML case:  
       ## <http://permalink.gmane.org/gmane.org.w3c.whatwg.discuss/11191>  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = $attr->name; ## TODO: manakai_name  
         $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};  
   
       $s .= "\x0A" if $tag_name eq 'pre' or $tag_name eq 'textarea';  
   
       if (not $in_cdata and {  
         style => 1, script => 1, xmp => 1, iframe => 1,  
         noembed => 1, noframes => 1, noscript => 1,  
         plaintext => 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  
8061    
8062  1;  1;
8063  # $Date$  # $Date$

Legend:
Removed from v.1.44  
changed lines
  Added in v.1.193

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24