/[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.31 by wakaba, Sat Jun 30 14:13:19 2007 UTC revision 1.165 by wakaba, Sat Sep 13 07:51: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  ## ISSUE:  ## ISSUE:
7  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
8  ## doc.write ('');  ## doc.write ('');
9  ## alert (doc.compatMode);  ## alert (doc.compatMode);
10    
11  ## ISSUE: HTML5 revision 967 says that the encoding layer MUST NOT  require IO::Handle;
12  ## strip BOM and the HTML layer MUST ignore it.  Whether we can do it  
13  ## is not yet clear.  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
14  ## "{U+FEFF}..." in UTF-16BE/UTF-16LE is three or four characters?  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
15  ## "{U+FEFF}..." in GB18030?  my $SVG_NS = q<http://www.w3.org/2000/svg>;
16    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
17  my $permitted_slash_tag_name = {  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
18    base => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
19    link => 1,  
20    meta => 1,  sub A_EL () { 0b1 }
21    hr => 1,  sub ADDRESS_EL () { 0b10 }
22    br => 1,  sub BODY_EL () { 0b100 }
23    img=> 1,  sub BUTTON_EL () { 0b1000 }
24    embed => 1,  sub CAPTION_EL () { 0b10000 }
25    param => 1,  sub DD_EL () { 0b100000 }
26    area => 1,  sub DIV_EL () { 0b1000000 }
27    col => 1,  sub DT_EL () { 0b10000000 }
28    input => 1,  sub FORM_EL () { 0b100000000 }
29    sub FORMATTING_EL () { 0b1000000000 }
30    sub FRAMESET_EL () { 0b10000000000 }
31    sub HEADING_EL () { 0b100000000000 }
32    sub HTML_EL () { 0b1000000000000 }
33    sub LI_EL () { 0b10000000000000 }
34    sub NOBR_EL () { 0b100000000000000 }
35    sub OPTION_EL () { 0b1000000000000000 }
36    sub OPTGROUP_EL () { 0b10000000000000000 }
37    sub P_EL () { 0b100000000000000000 }
38    sub SELECT_EL () { 0b1000000000000000000 }
39    sub TABLE_EL () { 0b10000000000000000000 }
40    sub TABLE_CELL_EL () { 0b100000000000000000000 }
41    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
42    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
43    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
44    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
45    sub FOREIGN_EL () { 0b10000000000000000000000000 }
46    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
47    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
48    sub RUBY_EL () { 0b10000000000000000000000000000 }
49    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
50    
51    sub TABLE_ROWS_EL () {
52      TABLE_EL |
53      TABLE_ROW_EL |
54      TABLE_ROW_GROUP_EL
55    }
56    
57    ## NOTE: Used in "generate implied end tags" algorithm.
58    ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL
59    ## is used in "generate implied end tags" implementation (search for the
60    ## function mae).
61    sub END_TAG_OPTIONAL_EL () {
62      DD_EL |
63      DT_EL |
64      LI_EL |
65      P_EL |
66      RUBY_COMPONENT_EL
67    }
68    
69    ## NOTE: Used in </body> and EOF algorithms.
70    sub ALL_END_TAG_OPTIONAL_EL () {
71      DD_EL |
72      DT_EL |
73      LI_EL |
74      P_EL |
75    
76      BODY_EL |
77      HTML_EL |
78      TABLE_CELL_EL |
79      TABLE_ROW_EL |
80      TABLE_ROW_GROUP_EL
81    }
82    
83    sub SCOPING_EL () {
84      BUTTON_EL |
85      CAPTION_EL |
86      HTML_EL |
87      TABLE_EL |
88      TABLE_CELL_EL |
89      MISC_SCOPING_EL
90    }
91    
92    sub TABLE_SCOPING_EL () {
93      HTML_EL |
94      TABLE_EL
95    }
96    
97    sub TABLE_ROWS_SCOPING_EL () {
98      HTML_EL |
99      TABLE_ROW_GROUP_EL
100    }
101    
102    sub TABLE_ROW_SCOPING_EL () {
103      HTML_EL |
104      TABLE_ROW_EL
105    }
106    
107    sub SPECIAL_EL () {
108      ADDRESS_EL |
109      BODY_EL |
110      DIV_EL |
111    
112      DD_EL |
113      DT_EL |
114      LI_EL |
115      P_EL |
116    
117      FORM_EL |
118      FRAMESET_EL |
119      HEADING_EL |
120      OPTION_EL |
121      OPTGROUP_EL |
122      SELECT_EL |
123      TABLE_ROW_EL |
124      TABLE_ROW_GROUP_EL |
125      MISC_SPECIAL_EL
126    }
127    
128    my $el_category = {
129      a => A_EL | FORMATTING_EL,
130      address => ADDRESS_EL,
131      applet => MISC_SCOPING_EL,
132      area => MISC_SPECIAL_EL,
133      b => FORMATTING_EL,
134      base => MISC_SPECIAL_EL,
135      basefont => MISC_SPECIAL_EL,
136      bgsound => MISC_SPECIAL_EL,
137      big => FORMATTING_EL,
138      blockquote => MISC_SPECIAL_EL,
139      body => BODY_EL,
140      br => MISC_SPECIAL_EL,
141      button => BUTTON_EL,
142      caption => CAPTION_EL,
143      center => MISC_SPECIAL_EL,
144      col => MISC_SPECIAL_EL,
145      colgroup => MISC_SPECIAL_EL,
146      dd => DD_EL,
147      dir => MISC_SPECIAL_EL,
148      div => DIV_EL,
149      dl => MISC_SPECIAL_EL,
150      dt => DT_EL,
151      em => FORMATTING_EL,
152      embed => MISC_SPECIAL_EL,
153      fieldset => MISC_SPECIAL_EL,
154      font => FORMATTING_EL,
155      form => FORM_EL,
156      frame => MISC_SPECIAL_EL,
157      frameset => FRAMESET_EL,
158      h1 => HEADING_EL,
159      h2 => HEADING_EL,
160      h3 => HEADING_EL,
161      h4 => HEADING_EL,
162      h5 => HEADING_EL,
163      h6 => HEADING_EL,
164      head => MISC_SPECIAL_EL,
165      hr => MISC_SPECIAL_EL,
166      html => HTML_EL,
167      i => FORMATTING_EL,
168      iframe => MISC_SPECIAL_EL,
169      img => MISC_SPECIAL_EL,
170      input => MISC_SPECIAL_EL,
171      isindex => MISC_SPECIAL_EL,
172      li => LI_EL,
173      link => MISC_SPECIAL_EL,
174      listing => MISC_SPECIAL_EL,
175      marquee => MISC_SCOPING_EL,
176      menu => MISC_SPECIAL_EL,
177      meta => MISC_SPECIAL_EL,
178      nobr => NOBR_EL | FORMATTING_EL,
179      noembed => MISC_SPECIAL_EL,
180      noframes => MISC_SPECIAL_EL,
181      noscript => MISC_SPECIAL_EL,
182      object => MISC_SCOPING_EL,
183      ol => MISC_SPECIAL_EL,
184      optgroup => OPTGROUP_EL,
185      option => OPTION_EL,
186      p => P_EL,
187      param => MISC_SPECIAL_EL,
188      plaintext => MISC_SPECIAL_EL,
189      pre => MISC_SPECIAL_EL,
190      rp => RUBY_COMPONENT_EL,
191      rt => RUBY_COMPONENT_EL,
192      ruby => RUBY_EL,
193      s => FORMATTING_EL,
194      script => MISC_SPECIAL_EL,
195      select => SELECT_EL,
196      small => FORMATTING_EL,
197      spacer => MISC_SPECIAL_EL,
198      strike => FORMATTING_EL,
199      strong => FORMATTING_EL,
200      style => MISC_SPECIAL_EL,
201      table => TABLE_EL,
202      tbody => TABLE_ROW_GROUP_EL,
203      td => TABLE_CELL_EL,
204      textarea => MISC_SPECIAL_EL,
205      tfoot => TABLE_ROW_GROUP_EL,
206      th => TABLE_CELL_EL,
207      thead => TABLE_ROW_GROUP_EL,
208      title => MISC_SPECIAL_EL,
209      tr => TABLE_ROW_EL,
210      tt => FORMATTING_EL,
211      u => FORMATTING_EL,
212      ul => MISC_SPECIAL_EL,
213      wbr => MISC_SPECIAL_EL,
214    };
215    
216    my $el_category_f = {
217      $MML_NS => {
218        'annotation-xml' => MML_AXML_EL,
219        mi => FOREIGN_FLOW_CONTENT_EL,
220        mo => FOREIGN_FLOW_CONTENT_EL,
221        mn => FOREIGN_FLOW_CONTENT_EL,
222        ms => FOREIGN_FLOW_CONTENT_EL,
223        mtext => FOREIGN_FLOW_CONTENT_EL,
224      },
225      $SVG_NS => {
226        foreignObject => FOREIGN_FLOW_CONTENT_EL,
227        desc => FOREIGN_FLOW_CONTENT_EL,
228        title => FOREIGN_FLOW_CONTENT_EL,
229      },
230      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
231  };  };
232    
233    my $svg_attr_name = {
234      attributename => 'attributeName',
235      attributetype => 'attributeType',
236      basefrequency => 'baseFrequency',
237      baseprofile => 'baseProfile',
238      calcmode => 'calcMode',
239      clippathunits => 'clipPathUnits',
240      contentscripttype => 'contentScriptType',
241      contentstyletype => 'contentStyleType',
242      diffuseconstant => 'diffuseConstant',
243      edgemode => 'edgeMode',
244      externalresourcesrequired => 'externalResourcesRequired',
245      filterres => 'filterRes',
246      filterunits => 'filterUnits',
247      glyphref => 'glyphRef',
248      gradienttransform => 'gradientTransform',
249      gradientunits => 'gradientUnits',
250      kernelmatrix => 'kernelMatrix',
251      kernelunitlength => 'kernelUnitLength',
252      keypoints => 'keyPoints',
253      keysplines => 'keySplines',
254      keytimes => 'keyTimes',
255      lengthadjust => 'lengthAdjust',
256      limitingconeangle => 'limitingConeAngle',
257      markerheight => 'markerHeight',
258      markerunits => 'markerUnits',
259      markerwidth => 'markerWidth',
260      maskcontentunits => 'maskContentUnits',
261      maskunits => 'maskUnits',
262      numoctaves => 'numOctaves',
263      pathlength => 'pathLength',
264      patterncontentunits => 'patternContentUnits',
265      patterntransform => 'patternTransform',
266      patternunits => 'patternUnits',
267      pointsatx => 'pointsAtX',
268      pointsaty => 'pointsAtY',
269      pointsatz => 'pointsAtZ',
270      preservealpha => 'preserveAlpha',
271      preserveaspectratio => 'preserveAspectRatio',
272      primitiveunits => 'primitiveUnits',
273      refx => 'refX',
274      refy => 'refY',
275      repeatcount => 'repeatCount',
276      repeatdur => 'repeatDur',
277      requiredextensions => 'requiredExtensions',
278      requiredfeatures => 'requiredFeatures',
279      specularconstant => 'specularConstant',
280      specularexponent => 'specularExponent',
281      spreadmethod => 'spreadMethod',
282      startoffset => 'startOffset',
283      stddeviation => 'stdDeviation',
284      stitchtiles => 'stitchTiles',
285      surfacescale => 'surfaceScale',
286      systemlanguage => 'systemLanguage',
287      tablevalues => 'tableValues',
288      targetx => 'targetX',
289      targety => 'targetY',
290      textlength => 'textLength',
291      viewbox => 'viewBox',
292      viewtarget => 'viewTarget',
293      xchannelselector => 'xChannelSelector',
294      ychannelselector => 'yChannelSelector',
295      zoomandpan => 'zoomAndPan',
296    };
297    
298    my $foreign_attr_xname = {
299      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
300      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
301      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
302      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
303      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
304      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
305      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
306      'xml:base' => [$XML_NS, ['xml', 'base']],
307      'xml:lang' => [$XML_NS, ['xml', 'lang']],
308      'xml:space' => [$XML_NS, ['xml', 'space']],
309      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
310      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
311    };
312    
313    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
314    
315  my $c1_entity_char = {  my $c1_entity_char = {
316    0x80 => 0x20AC,    0x80 => 0x20AC,
317    0x81 => 0xFFFD,    0x81 => 0xFFFD,
# Line 62  my $c1_entity_char = { Line 347  my $c1_entity_char = {
347    0x9F => 0x0178,    0x9F => 0x0178,
348  }; # $c1_entity_char  }; # $c1_entity_char
349    
350  my $special_category = {  sub parse_byte_string ($$$$;$) {
351    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,    my $self = shift;
352    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,    my $charset_name = shift;
353    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
354    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
355    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  } # parse_byte_string
356    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
357    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  sub parse_byte_stream ($$$$;$$) {
358    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
359    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,    my $self = ref $_[0] ? shift : shift->new;
360    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,    my $charset_name = shift;
361  };    my $byte_stream = $_[0];
 my $scoping_category = {  
   button => 1, caption => 1, html => 1, marquee => 1, object => 1,  
   table => 1, td => 1, th => 1,  
 };  
 my $formatting_category = {  
   a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,  
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
362    
363  sub parse_string ($$$;$) {    my $onerror = $_[2] || sub {
364    my $self = shift->new;      my (%opt) = @_;
365    my $s = \$_[0];      warn "Parse error ($opt{type})\n";
366      };
367      $self->{parse_error} = $onerror; # updated later by parse_char_string
368    
369      my $get_wrapper = $_[3] || sub ($) {
370        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
371      };
372    
373      ## HTML5 encoding sniffing algorithm
374      require Message::Charset::Info;
375      my $charset;
376      my $buffer;
377      my ($char_stream, $e_status);
378    
379      SNIFFING: {
380        ## NOTE: By setting |allow_fallback| option true when the
381        ## |get_decode_handle| method is invoked, we ignore what the HTML5
382        ## spec requires, i.e. unsupported encoding should be ignored.
383          ## TODO: We should not do this unless the parser is invoked
384          ## in the conformance checking mode, in which this behavior
385          ## would be useful.
386    
387        ## Step 1
388        if (defined $charset_name) {
389          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
390              ## TODO: Is this ok?  Transfer protocol's parameter should be
391              ## interpreted in its semantics?
392    
393          ## ISSUE: Unsupported encoding is not ignored according to the spec.
394          ($char_stream, $e_status) = $charset->get_decode_handle
395              ($byte_stream, allow_error_reporting => 1,
396               allow_fallback => 1);
397          if ($char_stream) {
398            $self->{confident} = 1;
399            last SNIFFING;
400          } else {
401            ## TODO: unsupported error
402          }
403        }
404    
405        ## Step 2
406        my $byte_buffer = '';
407        for (1..1024) {
408          my $char = $byte_stream->getc;
409          last unless defined $char;
410          $byte_buffer .= $char;
411        } ## TODO: timeout
412    
413        ## Step 3
414        if ($byte_buffer =~ /^\xFE\xFF/) {
415          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
416          ($char_stream, $e_status) = $charset->get_decode_handle
417              ($byte_stream, allow_error_reporting => 1,
418               allow_fallback => 1, byte_buffer => \$byte_buffer);
419          $self->{confident} = 1;
420          last SNIFFING;
421        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
422          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
423          ($char_stream, $e_status) = $charset->get_decode_handle
424              ($byte_stream, allow_error_reporting => 1,
425               allow_fallback => 1, byte_buffer => \$byte_buffer);
426          $self->{confident} = 1;
427          last SNIFFING;
428        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
429          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
430          ($char_stream, $e_status) = $charset->get_decode_handle
431              ($byte_stream, allow_error_reporting => 1,
432               allow_fallback => 1, byte_buffer => \$byte_buffer);
433          $self->{confident} = 1;
434          last SNIFFING;
435        }
436    
437        ## Step 4
438        ## TODO: <meta charset>
439    
440        ## Step 5
441        ## TODO: from history
442    
443        ## Step 6
444        require Whatpm::Charset::UniversalCharDet;
445        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
446            ($byte_buffer);
447        if (defined $charset_name) {
448          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
449    
450          ## ISSUE: Unsupported encoding is not ignored according to the spec.
451          require Whatpm::Charset::DecodeHandle;
452          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
453              ($byte_stream);
454          ($char_stream, $e_status) = $charset->get_decode_handle
455              ($buffer, allow_error_reporting => 1,
456               allow_fallback => 1, byte_buffer => \$byte_buffer);
457          if ($char_stream) {
458            $buffer->{buffer} = $byte_buffer;
459            !!!parse-error (type => 'sniffing:chardet',
460                            text => $charset_name,
461                            level => $self->{level}->{info},
462                            layer => 'encode',
463                            line => 1, column => 1);
464            $self->{confident} = 0;
465            last SNIFFING;
466          }
467        }
468    
469        ## Step 7: default
470        ## TODO: Make this configurable.
471        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
472            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
473            ## detectable in the step 6.
474        require Whatpm::Charset::DecodeHandle;
475        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
476            ($byte_stream);
477        ($char_stream, $e_status)
478            = $charset->get_decode_handle ($buffer,
479                                           allow_error_reporting => 1,
480                                           allow_fallback => 1,
481                                           byte_buffer => \$byte_buffer);
482        $buffer->{buffer} = $byte_buffer;
483        !!!parse-error (type => 'sniffing:default',
484                        text => 'windows-1252',
485                        level => $self->{level}->{info},
486                        line => 1, column => 1,
487                        layer => 'encode');
488        $self->{confident} = 0;
489      } # SNIFFING
490    
491      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
492        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
493        !!!parse-error (type => 'chardecode:fallback',
494                        #text => $self->{input_encoding},
495                        level => $self->{level}->{uncertain},
496                        line => 1, column => 1,
497                        layer => 'encode');
498      } elsif (not ($e_status &
499                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {
500        $self->{input_encoding} = $charset->get_iana_name;
501        !!!parse-error (type => 'chardecode:no error',
502                        text => $self->{input_encoding},
503                        level => $self->{level}->{uncertain},
504                        line => 1, column => 1,
505                        layer => 'encode');
506      } else {
507        $self->{input_encoding} = $charset->get_iana_name;
508      }
509    
510      $self->{change_encoding} = sub {
511        my $self = shift;
512        $charset_name = shift;
513        my $token = shift;
514    
515        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
516        ($char_stream, $e_status) = $charset->get_decode_handle
517            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
518             byte_buffer => \ $buffer->{buffer});
519        
520        if ($char_stream) { # if supported
521          ## "Change the encoding" algorithm:
522    
523          ## Step 1    
524          if ($charset->{category} &
525              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
526            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
527            ($char_stream, $e_status) = $charset->get_decode_handle
528                ($byte_stream,
529                 byte_buffer => \ $buffer->{buffer});
530          }
531          $charset_name = $charset->get_iana_name;
532          
533          ## Step 2
534          if (defined $self->{input_encoding} and
535              $self->{input_encoding} eq $charset_name) {
536            !!!parse-error (type => 'charset label:matching',
537                            text => $charset_name,
538                            level => $self->{level}->{info});
539            $self->{confident} = 1;
540            return;
541          }
542    
543          !!!parse-error (type => 'charset label detected',
544                          text => $self->{input_encoding},
545                          value => $charset_name,
546                          level => $self->{level}->{warn},
547                          token => $token);
548          
549          ## Step 3
550          # if (can) {
551            ## change the encoding on the fly.
552            #$self->{confident} = 1;
553            #return;
554          # }
555          
556          ## Step 4
557          throw Whatpm::HTML::RestartParser ();
558        }
559      }; # $self->{change_encoding}
560    
561      my $char_onerror = sub {
562        my (undef, $type, %opt) = @_;
563        !!!parse-error (layer => 'encode',
564                        %opt, type => $type,
565                        line => $self->{line}, column => $self->{column} + 1);
566        if ($opt{octets}) {
567          ${$opt{octets}} = "\x{FFFD}"; # relacement character
568        }
569      };
570    
571      my $wrapped_char_stream = $get_wrapper->($char_stream);
572      $wrapped_char_stream->onerror ($char_onerror);
573    
574      my @args = @_; shift @args; # $s
575      my $return;
576      try {
577        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
578      } catch Whatpm::HTML::RestartParser with {
579        ## NOTE: Invoked after {change_encoding}.
580    
581        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
582          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
583          !!!parse-error (type => 'chardecode:fallback',
584                          level => $self->{level}->{uncertain},
585                          #text => $self->{input_encoding},
586                          line => 1, column => 1,
587                          layer => 'encode');
588        } elsif (not ($e_status &
589                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL())) {
590          $self->{input_encoding} = $charset->get_iana_name;
591          !!!parse-error (type => 'chardecode:no error',
592                          text => $self->{input_encoding},
593                          level => $self->{level}->{uncertain},
594                          line => 1, column => 1,
595                          layer => 'encode');
596        } else {
597          $self->{input_encoding} = $charset->get_iana_name;
598        }
599        $self->{confident} = 1;
600    
601        $wrapped_char_stream = $get_wrapper->($char_stream);
602        $wrapped_char_stream->onerror ($char_onerror);
603    
604        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
605      };
606      return $return;
607    } # parse_byte_stream
608    
609    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
610    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
611    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
612    ## because the core part of our HTML parser expects a string of character,
613    ## not a string of bytes or code units or anything which might contain a BOM.
614    ## Therefore, any parser interface that accepts a string of bytes,
615    ## such as |parse_byte_string| in this module, must ensure that it does
616    ## strip the BOM and never strip any ZWNBSP.
617    
618    sub parse_char_string ($$$;$$) {
619      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
620      my $self = shift;
621      require utf8;
622      my $s = ref $_[0] ? $_[0] : \($_[0]);
623      open my $input, '<' . (utf8::is_utf8 ($$s) ? ':utf8' : ''), $s;
624      if ($_[3]) {
625        $input = $_[3]->($input);
626      }
627      return $self->parse_char_stream ($input, @_[1..$#_]);
628    } # parse_char_string
629    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
630    
631    sub parse_char_stream ($$$;$) {
632      my $self = ref $_[0] ? shift : shift->new;
633      my $input = $_[0];
634    $self->{document} = $_[1];    $self->{document} = $_[1];
635      @{$self->{document}->child_nodes} = ();
636    
637    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
638    
639      $self->{confident} = 1 unless exists $self->{confident};
640      $self->{document}->input_encoding ($self->{input_encoding})
641          if defined $self->{input_encoding};
642    
643    my $i = 0;    my $i = 0;
644    my $line = 1;    $self->{line_prev} = $self->{line} = 1;
645    my $column = 0;    $self->{column_prev} = $self->{column} = 0;
646    $self->{set_next_input_character} = sub {    $self->{set_next_char} = sub {
647      my $self = shift;      my $self = shift;
648    
649      pop @{$self->{prev_input_character}};      pop @{$self->{prev_char}};
650      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      unshift @{$self->{prev_char}}, $self->{next_char};
651    
652      $self->{next_input_character} = -1 and return if $i >= length $$s;      my $char;
653      $self->{next_input_character} = ord substr $$s, $i++, 1;      if (defined $self->{next_next_char}) {
654      $column++;        $char = $self->{next_next_char};
655          delete $self->{next_next_char};
656        } else {
657          $char = $input->getc;
658        }
659        $self->{next_char} = -1 and return unless defined $char;
660        $self->{next_char} = ord $char;
661    
662        ($self->{line_prev}, $self->{column_prev})
663            = ($self->{line}, $self->{column});
664        $self->{column}++;
665            
666      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{next_char} == 0x000A) { # LF
667        $line++;        !!!cp ('j1');
668        $column = 0;        $self->{line}++;
669      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
670        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{next_char} == 0x000D) { # CR
671        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
672        $line++;        my $next = $input->getc;
673        $column = 0;        if (defined $next and $next ne "\x0A") {
674      } elsif ($self->{next_input_character} > 0x10FFFF) {          $self->{next_next_char} = $next;
675        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        }
676      } elsif ($self->{next_input_character} == 0x0000) { # NULL        $self->{next_char} = 0x000A; # LF # MUST
677          $self->{line}++;
678          $self->{column} = 0;
679        } elsif ($self->{next_char} > 0x10FFFF) {
680          !!!cp ('j3');
681          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
682        } elsif ($self->{next_char} == 0x0000) { # NULL
683          !!!cp ('j4');
684        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
685        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
686        } elsif ($self->{next_char} <= 0x0008 or
687                 (0x000E <= $self->{next_char} and $self->{next_char} <= 0x001F) or
688                 (0x007F <= $self->{next_char} and $self->{next_char} <= 0x009F) or
689                 (0xD800 <= $self->{next_char} and $self->{next_char} <= 0xDFFF) or
690                 (0xFDD0 <= $self->{next_char} and $self->{next_char} <= 0xFDDF) or
691                 {
692                  0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,
693                  0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,
694                  0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,
695                  0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,
696                  0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,
697                  0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,
698                  0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,
699                  0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,
700                  0x10FFFE => 1, 0x10FFFF => 1,
701                 }->{$self->{next_char}}) {
702          !!!cp ('j5');
703          if ($self->{next_char} < 0x10000) {
704            !!!parse-error (type => 'control char',
705                            text => (sprintf 'U+%04X', $self->{next_char}));
706          } else {
707            !!!parse-error (type => 'control char',
708                            text => (sprintf 'U-%08X', $self->{next_char}));
709          }
710      }      }
711    };    };
712    $self->{prev_input_character} = [-1, -1, -1];    $self->{prev_char} = [-1, -1, -1];
713    $self->{next_input_character} = -1;    $self->{next_char} = -1;
714    
715    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
716      my (%opt) = @_;      my (%opt) = @_;
717      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
718        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
719        warn "Parse error ($opt{type}) at line $line column $column\n";
720    };    };
721    $self->{parse_error} = sub {    $self->{parse_error} = sub {
722      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
723    };    };
724    
725    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
# Line 135  sub parse_string ($$$;$) { Line 727  sub parse_string ($$$;$) {
727    $self->_construct_tree;    $self->_construct_tree;
728    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
729    
730      delete $self->{parse_error}; # remove loop
731    
732    return $self->{document};    return $self->{document};
733  } # parse_string  } # parse_char_stream
734    
735  sub new ($) {  sub new ($) {
736    my $class = shift;    my $class = shift;
737    my $self = bless {}, $class;    my $self = bless {
738    $self->{set_next_input_character} = sub {      level => {must => 'm',
739      $self->{next_input_character} = -1;                should => 's',
740                  warn => 'w',
741                  info => 'i',
742                  uncertain => 'u'},
743      }, $class;
744      $self->{set_next_char} = sub {
745        $self->{next_char} = -1;
746    };    };
747    $self->{parse_error} = sub {    $self->{parse_error} = sub {
748      #      #
749    };    };
750      $self->{change_encoding} = sub {
751        # if ($_[0] is a supported encoding) {
752        #   run "change the encoding" algorithm;
753        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
754        # }
755      };
756      $self->{application_cache_selection} = sub {
757        #
758      };
759    return $self;    return $self;
760  } # new  } # new
761    
762    sub CM_ENTITY () { 0b001 } # & markup in data
763    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
764    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
765    
766    sub PLAINTEXT_CONTENT_MODEL () { 0 }
767    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
768    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
769    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
770    
771    sub DATA_STATE () { 0 }
772    sub ENTITY_DATA_STATE () { 1 }
773    sub TAG_OPEN_STATE () { 2 }
774    sub CLOSE_TAG_OPEN_STATE () { 3 }
775    sub TAG_NAME_STATE () { 4 }
776    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
777    sub ATTRIBUTE_NAME_STATE () { 6 }
778    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
779    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
780    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
781    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
782    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
783    sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
784    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
785    sub COMMENT_START_STATE () { 14 }
786    sub COMMENT_START_DASH_STATE () { 15 }
787    sub COMMENT_STATE () { 16 }
788    sub COMMENT_END_STATE () { 17 }
789    sub COMMENT_END_DASH_STATE () { 18 }
790    sub BOGUS_COMMENT_STATE () { 19 }
791    sub DOCTYPE_STATE () { 20 }
792    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
793    sub DOCTYPE_NAME_STATE () { 22 }
794    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
795    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
796    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
797    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
798    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
799    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
800    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
801    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
802    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
803    sub BOGUS_DOCTYPE_STATE () { 32 }
804    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
805    sub SELF_CLOSING_START_TAG_STATE () { 34 }
806    sub CDATA_SECTION_STATE () { 35 }
807    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
808    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
809    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
810    sub CDATA_PCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
811    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
812    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
813    
814    sub DOCTYPE_TOKEN () { 1 }
815    sub COMMENT_TOKEN () { 2 }
816    sub START_TAG_TOKEN () { 3 }
817    sub END_TAG_TOKEN () { 4 }
818    sub END_OF_FILE_TOKEN () { 5 }
819    sub CHARACTER_TOKEN () { 6 }
820    
821    sub AFTER_HTML_IMS () { 0b100 }
822    sub HEAD_IMS ()       { 0b1000 }
823    sub BODY_IMS ()       { 0b10000 }
824    sub BODY_TABLE_IMS () { 0b100000 }
825    sub TABLE_IMS ()      { 0b1000000 }
826    sub ROW_IMS ()        { 0b10000000 }
827    sub BODY_AFTER_IMS () { 0b100000000 }
828    sub FRAME_IMS ()      { 0b1000000000 }
829    sub SELECT_IMS ()     { 0b10000000000 }
830    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
831        ## NOTE: "in foreign content" insertion mode is special; it is combined
832        ## with the secondary insertion mode.  In this parser, they are stored
833        ## together in the bit-or'ed form.
834    
835    ## NOTE: "initial" and "before html" insertion modes have no constants.
836    
837    ## NOTE: "after after body" insertion mode.
838    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
839    
840    ## NOTE: "after after frameset" insertion mode.
841    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
842    
843    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
844    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
845    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
846    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
847    sub IN_BODY_IM () { BODY_IMS }
848    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
849    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
850    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
851    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
852    sub IN_TABLE_IM () { TABLE_IMS }
853    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
854    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
855    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
856    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
857    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
858    sub IN_COLUMN_GROUP_IM () { 0b10 }
859    
860  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
861    
862  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
863    my $self = shift;    my $self = shift;
864    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
865    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{state_keyword}; # initialized when used
866    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
867      undef $self->{current_token};
868    undef $self->{current_attribute};    undef $self->{current_attribute};
869    undef $self->{last_emitted_start_tag_name};    undef $self->{last_emitted_start_tag_name};
870    undef $self->{last_attribute_value_state};    undef $self->{last_attribute_value_state};
871      delete $self->{self_closing};
872    $self->{char} = [];    $self->{char} = [];
873    # $self->{next_input_character}    # $self->{next_char}
874    !!!next-input-character;    !!!next-input-character;
875    $self->{token} = [];    $self->{token} = [];
876    # $self->{escape}    # $self->{escape}
877  } # _initialize_tokenizer  } # _initialize_tokenizer
878    
879  ## A token has:  ## A token has:
880  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
881  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
882  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
883  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
884  ##   ->{system_identifier} (DOCTYPE)  ##   ->{public_identifier} (DOCTYPE_TOKEN)
885  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{system_identifier} (DOCTYPE_TOKEN)
886  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
887  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
888    ##        ->{name}
889    ##        ->{value}
890    ##        ->{has_reference} == 1 or 0
891    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
892    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
893    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
894    ##     while the token is pushed back to the stack.
895    
896  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
897    
# Line 185  sub _initialize_tokenizer ($) { Line 901  sub _initialize_tokenizer ($) {
901  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
902  ## and removed from the list.  ## and removed from the list.
903    
904    ## NOTE: HTML5 "Writing HTML documents" section, applied to
905    ## documents and not to user agents and conformance checkers,
906    ## contains some requirements that are not detected by the
907    ## parsing algorithm:
908    ## - Some requirements on character encoding declarations. ## TODO
909    ## - "Elements MUST NOT contain content that their content model disallows."
910    ##   ... Some are parse error, some are not (will be reported by c.c.).
911    ## - Polytheistic slash SHOULD NOT be used. (Applied only to atheists.) ## TODO
912    ## - Text (in elements, attributes, and comments) SHOULD NOT contain
913    ##   control characters other than space characters. ## TODO: (what is control character? C0, C1 and DEL?  Unicode control character?)
914    
915    ## TODO: HTML5 poses authors two SHOULD-level requirements that cannot
916    ## be detected by the HTML5 parsing algorithm:
917    ## - Text,
918    
919  sub _get_next_token ($) {  sub _get_next_token ($) {
920    my $self = shift;    my $self = shift;
921    
922      if ($self->{self_closing}) {
923        !!!parse-error (type => 'nestc', token => $self->{current_token});
924        ## NOTE: The |self_closing| flag is only set by start tag token.
925        ## In addition, when a start tag token is emitted, it is always set to
926        ## |current_token|.
927        delete $self->{self_closing};
928      }
929    
930    if (@{$self->{token}}) {    if (@{$self->{token}}) {
931        $self->{self_closing} = $self->{token}->[0]->{self_closing};
932      return shift @{$self->{token}};      return shift @{$self->{token}};
933    }    }
934    
935    A: {    A: {
936      if ($self->{state} eq 'data') {      if ($self->{state} == DATA_STATE) {
937        if ($self->{next_input_character} == 0x0026) { # &        if ($self->{next_char} == 0x0026) { # &
938          if ($self->{content_model_flag} eq 'PCDATA' or          if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
939              $self->{content_model_flag} eq 'RCDATA') {              not $self->{escape}) {
940            $self->{state} = 'entity data';            !!!cp (1);
941              $self->{state} = ENTITY_DATA_STATE;
942            !!!next-input-character;            !!!next-input-character;
943            redo A;            redo A;
944          } else {          } else {
945              !!!cp (2);
946            #            #
947          }          }
948        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{next_char} == 0x002D) { # -
949          if ($self->{content_model_flag} eq 'RCDATA' or          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
             $self->{content_model_flag} eq 'CDATA') {  
950            unless ($self->{escape}) {            unless ($self->{escape}) {
951              if ($self->{prev_input_character}->[0] == 0x002D and # -              if ($self->{prev_char}->[0] == 0x002D and # -
952                  $self->{prev_input_character}->[1] == 0x0021 and # !                  $self->{prev_char}->[1] == 0x0021 and # !
953                  $self->{prev_input_character}->[2] == 0x003C) { # <                  $self->{prev_char}->[2] == 0x003C) { # <
954                  !!!cp (3);
955                $self->{escape} = 1;                $self->{escape} = 1;
956                } else {
957                  !!!cp (4);
958              }              }
959              } else {
960                !!!cp (5);
961            }            }
962          }          }
963                    
964          #          #
965        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{next_char} == 0x003C) { # <
966          if ($self->{content_model_flag} eq 'PCDATA' or          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
967              (($self->{content_model_flag} eq 'CDATA' or              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
               $self->{content_model_flag} eq 'RCDATA') and  
968               not $self->{escape})) {               not $self->{escape})) {
969            $self->{state} = 'tag open';            !!!cp (6);
970              $self->{state} = TAG_OPEN_STATE;
971            !!!next-input-character;            !!!next-input-character;
972            redo A;            redo A;
973          } else {          } else {
974              !!!cp (7);
975            #            #
976          }          }
977        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
978          if ($self->{escape} and          if ($self->{escape} and
979              ($self->{content_model_flag} eq 'RCDATA' or              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
980               $self->{content_model_flag} eq 'CDATA')) {            if ($self->{prev_char}->[0] == 0x002D and # -
981            if ($self->{prev_input_character}->[0] == 0x002D and # -                $self->{prev_char}->[1] == 0x002D) { # -
982                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
983              delete $self->{escape};              delete $self->{escape};
984              } else {
985                !!!cp (9);
986            }            }
987            } else {
988              !!!cp (10);
989          }          }
990                    
991          #          #
992        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
993          !!!emit ({type => 'end-of-file'});          !!!cp (11);
994            !!!emit ({type => END_OF_FILE_TOKEN,
995                      line => $self->{line}, column => $self->{column}});
996          last A; ## TODO: ok?          last A; ## TODO: ok?
997          } else {
998            !!!cp (12);
999        }        }
1000        # Anything else        # Anything else
1001        my $token = {type => 'character',        my $token = {type => CHARACTER_TOKEN,
1002                     data => chr $self->{next_input_character}};                     data => chr $self->{next_char},
1003                       line => $self->{line}, column => $self->{column},
1004                      };
1005        ## Stay in the data state        ## Stay in the data state
1006        !!!next-input-character;        !!!next-input-character;
1007    
1008        !!!emit ($token);        !!!emit ($token);
1009    
1010        redo A;        redo A;
1011      } elsif ($self->{state} eq 'entity data') {      } elsif ($self->{state} == ENTITY_DATA_STATE) {
1012        ## (cannot happen in CDATA state)        ## (cannot happen in CDATA state)
1013    
1014          my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
1015                
1016        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);        my $token = $self->_tokenize_attempt_to_consume_an_entity (0, -1);
1017    
1018        $self->{state} = 'data';        $self->{state} = DATA_STATE;
1019        # next-input-character is already done        # next-input-character is already done
1020    
1021        unless (defined $token) {        unless (defined $token) {
1022          !!!emit ({type => 'character', data => '&'});          !!!cp (13);
1023            !!!emit ({type => CHARACTER_TOKEN, data => '&',
1024                      line => $l, column => $c,
1025                     });
1026        } else {        } else {
1027            !!!cp (14);
1028          !!!emit ($token);          !!!emit ($token);
1029        }        }
1030    
1031        redo A;        redo A;
1032      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1033        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1034            $self->{content_model_flag} eq 'CDATA') {          if ($self->{next_char} == 0x002F) { # /
1035          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1036            !!!next-input-character;            !!!next-input-character;
1037            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1038            redo A;            redo A;
1039          } else {          } else {
1040              !!!cp (16);
1041            ## reconsume            ## reconsume
1042            $self->{state} = 'data';            $self->{state} = DATA_STATE;
1043    
1044            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1045                        line => $self->{line_prev},
1046                        column => $self->{column_prev},
1047                       });
1048    
1049            redo A;            redo A;
1050          }          }
1051        } elsif ($self->{content_model_flag} eq 'PCDATA') {        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1052          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{next_char} == 0x0021) { # !
1053            $self->{state} = 'markup declaration open';            !!!cp (17);
1054              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1055            !!!next-input-character;            !!!next-input-character;
1056            redo A;            redo A;
1057          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{next_char} == 0x002F) { # /
1058            $self->{state} = 'close tag open';            !!!cp (18);
1059              $self->{state} = CLOSE_TAG_OPEN_STATE;
1060            !!!next-input-character;            !!!next-input-character;
1061            redo A;            redo A;
1062          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{next_char} and
1063                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{next_char} <= 0x005A) { # A..Z
1064              !!!cp (19);
1065            $self->{current_token}            $self->{current_token}
1066              = {type => 'start tag',              = {type => START_TAG_TOKEN,
1067                 tag_name => chr ($self->{next_input_character} + 0x0020)};                 tag_name => chr ($self->{next_char} + 0x0020),
1068            $self->{state} = 'tag name';                 line => $self->{line_prev},
1069                   column => $self->{column_prev}};
1070              $self->{state} = TAG_NAME_STATE;
1071            !!!next-input-character;            !!!next-input-character;
1072            redo A;            redo A;
1073          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{next_char} and
1074                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{next_char} <= 0x007A) { # a..z
1075            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1076                              tag_name => chr ($self->{next_input_character})};            $self->{current_token} = {type => START_TAG_TOKEN,
1077            $self->{state} = 'tag name';                                      tag_name => chr ($self->{next_char}),
1078                                        line => $self->{line_prev},
1079                                        column => $self->{column_prev}};
1080              $self->{state} = TAG_NAME_STATE;
1081            !!!next-input-character;            !!!next-input-character;
1082            redo A;            redo A;
1083          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{next_char} == 0x003E) { # >
1084            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1085            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1086                              line => $self->{line_prev},
1087                              column => $self->{column_prev});
1088              $self->{state} = DATA_STATE;
1089            !!!next-input-character;            !!!next-input-character;
1090    
1091            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1092                        line => $self->{line_prev},
1093                        column => $self->{column_prev},
1094                       });
1095    
1096            redo A;            redo A;
1097          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{next_char} == 0x003F) { # ?
1098            !!!parse-error (type => 'pio');            !!!cp (22);
1099            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1100            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1101                              column => $self->{column_prev});
1102              $self->{state} = BOGUS_COMMENT_STATE;
1103              $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1104                                        line => $self->{line_prev},
1105                                        column => $self->{column_prev},
1106                                       };
1107              ## $self->{next_char} is intentionally left as is
1108            redo A;            redo A;
1109          } else {          } else {
1110            !!!parse-error (type => 'bare stago');            !!!cp (23);
1111            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1112                              line => $self->{line_prev},
1113                              column => $self->{column_prev});
1114              $self->{state} = DATA_STATE;
1115            ## reconsume            ## reconsume
1116    
1117            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1118                        line => $self->{line_prev},
1119                        column => $self->{column_prev},
1120                       });
1121    
1122            redo A;            redo A;
1123          }          }
1124        } else {        } else {
1125          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1126        }        }
1127      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1128        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1129            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_PCDATA_CLOSE_TAG_STATE|.
         if (defined $self->{last_emitted_start_tag_name}) {  
           ## 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';  
1130    
1131                !!!emit ({type => 'character', data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1132            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1133                redo A;          if (defined $self->{last_emitted_start_tag_name}) {
1134              }            $self->{state} = CDATA_PCDATA_CLOSE_TAG_STATE;
1135            }            $self->{state_keyword} = '';
1136            push @next_char, $self->{next_input_character};            ## Reconsume.
1137                    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...  
           }  
1138          } else {          } else {
1139            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1140            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1141            $self->{state} = 'data';            !!!cp (28);
1142            !!!emit ({type => 'character', data => '</'});            $self->{state} = DATA_STATE;
1143              ## Reconsume.
1144              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1145                        line => $l, column => $c,
1146                       });
1147            redo A;            redo A;
1148          }          }
1149        }        }
1150          
1151        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{next_char} and
1152            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{next_char} <= 0x005A) { # A..Z
1153          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1154                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{current_token}
1155          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1156          !!!next-input-character;                 tag_name => chr ($self->{next_char} + 0x0020),
1157          redo A;                 line => $l, column => $c};
1158        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1159                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1160          $self->{current_token} = {type => 'end tag',          redo A;
1161                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{next_char} and
1162          $self->{state} = 'tag name';                 $self->{next_char} <= 0x007A) { # a..z
1163          !!!next-input-character;          !!!cp (30);
1164          redo A;          $self->{current_token} = {type => END_TAG_TOKEN,
1165        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{next_char}),
1166          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1167          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1168            !!!next-input-character;
1169            redo A;
1170          } elsif ($self->{next_char} == 0x003E) { # >
1171            !!!cp (31);
1172            !!!parse-error (type => 'empty end tag',
1173                            line => $self->{line_prev}, ## "<" in "</>"
1174                            column => $self->{column_prev} - 1);
1175            $self->{state} = DATA_STATE;
1176          !!!next-input-character;          !!!next-input-character;
1177          redo A;          redo A;
1178        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1179            !!!cp (32);
1180          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1181          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1182          # reconsume          # reconsume
1183    
1184          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1185                      line => $l, column => $c,
1186                     });
1187    
1188          redo A;          redo A;
1189        } else {        } else {
1190            !!!cp (33);
1191          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1192          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1193          ## $self->{next_input_character} is intentionally left as is          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
1194          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1195                                      column => $self->{column_prev} - 1,
1196                                     };
1197            ## NOTE: $self->{next_char} is intentionally left as is.
1198            ## Although the "anything else" case of the spec not explicitly
1199            ## states that the next input character is to be reconsumed,
1200            ## it will be included to the |data| of the comment token
1201            ## generated from the bogus end tag, as defined in the
1202            ## "bogus comment state" entry.
1203            redo A;
1204          }
1205        } elsif ($self->{state} == CDATA_PCDATA_CLOSE_TAG_STATE) {
1206          my $ch = substr $self->{last_emitted_start_tag_name}, length $self->{state_keyword}, 1;
1207          if (length $ch) {
1208            my $CH = $ch;
1209            $ch =~ tr/a-z/A-Z/;
1210            my $nch = chr $self->{next_char};
1211            if ($nch eq $ch or $nch eq $CH) {
1212              !!!cp (24);
1213              ## Stay in the state.
1214              $self->{state_keyword} .= $nch;
1215              !!!next-input-character;
1216              redo A;
1217            } else {
1218              !!!cp (25);
1219              $self->{state} = DATA_STATE;
1220              ## Reconsume.
1221              !!!emit ({type => CHARACTER_TOKEN,
1222                        data => '</' . $self->{state_keyword},
1223                        line => $self->{line_prev},
1224                        column => $self->{column_prev} - 1 - length $self->{state_keyword},
1225                       });
1226              redo A;
1227            }
1228          } else { # after "<{tag-name}"
1229            unless ({
1230                     0x0009 => 1, # HT
1231                     0x000A => 1, # LF
1232                     0x000B => 1, # VT
1233                     0x000C => 1, # FF
1234                     0x0020 => 1, # SP
1235                     0x003E => 1, # >
1236                     0x002F => 1, # /
1237                     -1 => 1, # EOF
1238                    }->{$self->{next_char}}) {
1239              !!!cp (26);
1240              ## Reconsume.
1241              $self->{state} = DATA_STATE;
1242              !!!emit ({type => CHARACTER_TOKEN,
1243                        data => '</' . $self->{state_keyword},
1244                        line => $self->{line_prev},
1245                        column => $self->{column_prev} - 1 - length $self->{state_keyword},
1246                       });
1247              redo A;
1248            } else {
1249              !!!cp (27);
1250              $self->{current_token}
1251                  = {type => END_TAG_TOKEN,
1252                     tag_name => $self->{last_emitted_start_tag_name},
1253                     line => $self->{line_prev},
1254                     column => $self->{column_prev} - 1 - length $self->{state_keyword}};
1255              $self->{state} = TAG_NAME_STATE;
1256              ## Reconsume.
1257              redo A;
1258            }
1259        }        }
1260      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1261        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1262            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1263            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1264            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1265            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1266          $self->{state} = 'before attribute name';          !!!cp (34);
1267          !!!next-input-character;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1268          redo A;          !!!next-input-character;
1269        } elsif ($self->{next_input_character} == 0x003E) { # >          redo A;
1270          if ($self->{current_token}->{type} eq 'start tag') {        } elsif ($self->{next_char} == 0x003E) { # >
1271            $self->{current_token}->{first_start_tag}          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1272                = not defined $self->{last_emitted_start_tag_name};            !!!cp (35);
1273            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1274          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1275            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1276            if ($self->{current_token}->{attributes}) {            #if ($self->{current_token}->{attributes}) {
1277              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1278            }            #  !!! cp (36);
1279              #  !!! parse-error (type => 'end tag attribute');
1280              #} else {
1281                !!!cp (37);
1282              #}
1283          } else {          } else {
1284            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1285          }          }
1286          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1287          !!!next-input-character;          !!!next-input-character;
1288    
1289          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1290    
1291          redo A;          redo A;
1292        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
1293                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1294          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1295            $self->{current_token}->{tag_name} .= chr ($self->{next_char} + 0x0020);
1296            # start tag or end tag            # start tag or end tag
1297          ## Stay in this state          ## Stay in this state
1298          !!!next-input-character;          !!!next-input-character;
1299          redo A;          redo A;
1300        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1301          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1302          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1303            $self->{current_token}->{first_start_tag}            !!!cp (39);
               = not defined $self->{last_emitted_start_tag_name};  
1304            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1305          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1306            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1307            if ($self->{current_token}->{attributes}) {            #if ($self->{current_token}->{attributes}) {
1308              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1309            }            #  !!! cp (40);
1310              #  !!! parse-error (type => 'end tag attribute');
1311              #} else {
1312                !!!cp (41);
1313              #}
1314          } else {          } else {
1315            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1316          }          }
1317          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1318          # reconsume          # reconsume
1319    
1320          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1321    
1322          redo A;          redo A;
1323        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1324            !!!cp (42);
1325            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1326          !!!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  
1327          redo A;          redo A;
1328        } else {        } else {
1329          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1330            $self->{current_token}->{tag_name} .= chr $self->{next_char};
1331            # start tag or end tag            # start tag or end tag
1332          ## Stay in the state          ## Stay in the state
1333          !!!next-input-character;          !!!next-input-character;
1334          redo A;          redo A;
1335        }        }
1336      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1337        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1338            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1339            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1340            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1341            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1342            !!!cp (45);
1343          ## Stay in the state          ## Stay in the state
1344          !!!next-input-character;          !!!next-input-character;
1345          redo A;          redo A;
1346        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1347          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1348            $self->{current_token}->{first_start_tag}            !!!cp (46);
               = not defined $self->{last_emitted_start_tag_name};  
1349            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1350          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1351            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1352            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1353                !!!cp (47);
1354              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1355              } else {
1356                !!!cp (48);
1357            }            }
1358          } else {          } else {
1359            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1360          }          }
1361          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1362          !!!next-input-character;          !!!next-input-character;
1363    
1364          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1365    
1366          redo A;          redo A;
1367        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
1368                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1369          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1370                                value => ''};          $self->{current_attribute}
1371          $self->{state} = 'attribute name';              = {name => chr ($self->{next_char} + 0x0020),
1372                   value => '',
1373                   line => $self->{line}, column => $self->{column}};
1374            $self->{state} = ATTRIBUTE_NAME_STATE;
1375            !!!next-input-character;
1376            redo A;
1377          } elsif ($self->{next_char} == 0x002F) { # /
1378            !!!cp (50);
1379            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1380          !!!next-input-character;          !!!next-input-character;
1381          redo A;          redo A;
1382        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == -1) {
         !!!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  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1383          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1384          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1385            $self->{current_token}->{first_start_tag}            !!!cp (52);
               = not defined $self->{last_emitted_start_tag_name};  
1386            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1387          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1388            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1389            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1390                !!!cp (53);
1391              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1392              } else {
1393                !!!cp (54);
1394            }            }
1395          } else {          } else {
1396            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1397          }          }
1398          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1399          # reconsume          # reconsume
1400    
1401          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1402    
1403          redo A;          redo A;
1404        } else {        } else {
1405          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1406                                value => ''};               0x0022 => 1, # "
1407          $self->{state} = 'attribute name';               0x0027 => 1, # '
1408                 0x003D => 1, # =
1409                }->{$self->{next_char}}) {
1410              !!!cp (55);
1411              !!!parse-error (type => 'bad attribute name');
1412            } else {
1413              !!!cp (56);
1414            }
1415            $self->{current_attribute}
1416                = {name => chr ($self->{next_char}),
1417                   value => '',
1418                   line => $self->{line}, column => $self->{column}};
1419            $self->{state} = ATTRIBUTE_NAME_STATE;
1420          !!!next-input-character;          !!!next-input-character;
1421          redo A;          redo A;
1422        }        }
1423      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1424        my $before_leave = sub {        my $before_leave = sub {
1425          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{current_token}->{attributes} # start tag or end tag
1426              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{current_attribute}->{name}}) { # MUST
1427            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1428              !!!parse-error (type => 'duplicate attribute', text => $self->{current_attribute}->{name}, line => $self->{current_attribute}->{line}, column => $self->{current_attribute}->{column});
1429            ## Discard $self->{current_attribute} # MUST            ## Discard $self->{current_attribute} # MUST
1430          } else {          } else {
1431              !!!cp (58);
1432            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}
1433              = $self->{current_attribute};              = $self->{current_attribute};
1434          }          }
1435        }; # $before_leave        }; # $before_leave
1436    
1437        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1438            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1439            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1440            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1441            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1442            !!!cp (59);
1443          $before_leave->();          $before_leave->();
1444          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1445          !!!next-input-character;          !!!next-input-character;
1446          redo A;          redo A;
1447        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_char} == 0x003D) { # =
1448            !!!cp (60);
1449          $before_leave->();          $before_leave->();
1450          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1451          !!!next-input-character;          !!!next-input-character;
1452          redo A;          redo A;
1453        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1454          $before_leave->();          $before_leave->();
1455          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1456            $self->{current_token}->{first_start_tag}            !!!cp (61);
               = not defined $self->{last_emitted_start_tag_name};  
1457            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1458          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1459            $self->{content_model_flag} = 'PCDATA'; # MUST            !!!cp (62);
1460              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1461            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1462              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1463            }            }
1464          } else {          } else {
1465            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1466          }          }
1467          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1468          !!!next-input-character;          !!!next-input-character;
1469    
1470          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1471    
1472          redo A;          redo A;
1473        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
1474                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1475          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1476            $self->{current_attribute}->{name} .= chr ($self->{next_char} + 0x0020);
1477          ## Stay in the state          ## Stay in the state
1478          !!!next-input-character;          !!!next-input-character;
1479          redo A;          redo A;
1480        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == 0x002F) { # /
1481            !!!cp (64);
1482          $before_leave->();          $before_leave->();
1483            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1484          !!!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  
1485          redo A;          redo A;
1486        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1487          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1488          $before_leave->();          $before_leave->();
1489          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1490            $self->{current_token}->{first_start_tag}            !!!cp (66);
               = not defined $self->{last_emitted_start_tag_name};  
1491            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1492          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1493            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1494            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1495                !!!cp (67);
1496              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1497              } else {
1498                ## NOTE: This state should never be reached.
1499                !!!cp (68);
1500            }            }
1501          } else {          } else {
1502            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1503          }          }
1504          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1505          # reconsume          # reconsume
1506    
1507          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1508    
1509          redo A;          redo A;
1510        } else {        } else {
1511          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{next_char} == 0x0022 or # "
1512                $self->{next_char} == 0x0027) { # '
1513              !!!cp (69);
1514              !!!parse-error (type => 'bad attribute name');
1515            } else {
1516              !!!cp (70);
1517            }
1518            $self->{current_attribute}->{name} .= chr ($self->{next_char});
1519          ## Stay in the state          ## Stay in the state
1520          !!!next-input-character;          !!!next-input-character;
1521          redo A;          redo A;
1522        }        }
1523      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1524        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1525            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1526            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1527            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1528            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1529            !!!cp (71);
1530          ## Stay in the state          ## Stay in the state
1531          !!!next-input-character;          !!!next-input-character;
1532          redo A;          redo A;
1533        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{next_char} == 0x003D) { # =
1534          $self->{state} = 'before attribute value';          !!!cp (72);
1535            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1536          !!!next-input-character;          !!!next-input-character;
1537          redo A;          redo A;
1538        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1539          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1540            $self->{current_token}->{first_start_tag}            !!!cp (73);
               = not defined $self->{last_emitted_start_tag_name};  
1541            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1542          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1543            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1544            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1545                !!!cp (74);
1546              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1547              } else {
1548                ## NOTE: This state should never be reached.
1549                !!!cp (75);
1550            }            }
1551          } else {          } else {
1552            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1553          }          }
1554          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1555          !!!next-input-character;          !!!next-input-character;
1556    
1557          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1558    
1559          redo A;          redo A;
1560        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{next_char} and
1561                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{next_char} <= 0x005A) { # A..Z
1562          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1563                                value => ''};          $self->{current_attribute}
1564          $self->{state} = 'attribute name';              = {name => chr ($self->{next_char} + 0x0020),
1565                   value => '',
1566                   line => $self->{line}, column => $self->{column}};
1567            $self->{state} = ATTRIBUTE_NAME_STATE;
1568            !!!next-input-character;
1569            redo A;
1570          } elsif ($self->{next_char} == 0x002F) { # /
1571            !!!cp (77);
1572            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1573          !!!next-input-character;          !!!next-input-character;
1574          redo A;          redo A;
1575        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{next_char} == -1) {
         !!!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  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1576          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1577          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1578            $self->{current_token}->{first_start_tag}            !!!cp (79);
               = not defined $self->{last_emitted_start_tag_name};  
1579            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1580          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1581            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1582            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1583                !!!cp (80);
1584              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1585              } else {
1586                ## NOTE: This state should never be reached.
1587                !!!cp (81);
1588            }            }
1589          } else {          } else {
1590            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1591          }          }
1592          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1593          # reconsume          # reconsume
1594    
1595          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1596    
1597          redo A;          redo A;
1598        } else {        } else {
1599          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{next_char} == 0x0022 or # "
1600                                value => ''};              $self->{next_char} == 0x0027) { # '
1601          $self->{state} = 'attribute name';            !!!cp (78);
1602              !!!parse-error (type => 'bad attribute name');
1603            } else {
1604              !!!cp (82);
1605            }
1606            $self->{current_attribute}
1607                = {name => chr ($self->{next_char}),
1608                   value => '',
1609                   line => $self->{line}, column => $self->{column}};
1610            $self->{state} = ATTRIBUTE_NAME_STATE;
1611          !!!next-input-character;          !!!next-input-character;
1612          redo A;                  redo A;        
1613        }        }
1614      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1615        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1616            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1617            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
1618            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1619            $self->{next_input_character} == 0x0020) { # SP                  $self->{next_char} == 0x0020) { # SP      
1620            !!!cp (83);
1621          ## Stay in the state          ## Stay in the state
1622          !!!next-input-character;          !!!next-input-character;
1623          redo A;          redo A;
1624        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
1625          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1626            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1627          !!!next-input-character;          !!!next-input-character;
1628          redo A;          redo A;
1629        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1630          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1631            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1632          ## reconsume          ## reconsume
1633          redo A;          redo A;
1634        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
1635          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1636            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1637          !!!next-input-character;          !!!next-input-character;
1638          redo A;          redo A;
1639        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
1640          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1641            $self->{current_token}->{first_start_tag}          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1642                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1643            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1644          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1645            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1646            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1647                !!!cp (88);
1648              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1649              } else {
1650                ## NOTE: This state should never be reached.
1651                !!!cp (89);
1652            }            }
1653          } else {          } else {
1654            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1655          }          }
1656          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1657          !!!next-input-character;          !!!next-input-character;
1658    
1659          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1660    
1661          redo A;          redo A;
1662        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1663          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1664          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1665            $self->{current_token}->{first_start_tag}            !!!cp (90);
               = not defined $self->{last_emitted_start_tag_name};  
1666            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1667          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1668            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1669            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1670                !!!cp (91);
1671              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1672              } else {
1673                ## NOTE: This state should never be reached.
1674                !!!cp (92);
1675            }            }
1676          } else {          } else {
1677            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1678          }          }
1679          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1680          ## reconsume          ## reconsume
1681    
1682          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1683    
1684          redo A;          redo A;
1685        } else {        } else {
1686          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{next_char} == 0x003D) { # =
1687          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1688              !!!parse-error (type => 'bad attribute value');
1689            } else {
1690              !!!cp (94);
1691            }
1692            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1693            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1694          !!!next-input-character;          !!!next-input-character;
1695          redo A;          redo A;
1696        }        }
1697      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1698        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
1699          $self->{state} = 'before attribute name';          !!!cp (95);
1700            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1701          !!!next-input-character;          !!!next-input-character;
1702          redo A;          redo A;
1703        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1704          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1705          $self->{state} = 'entity in attribute value';          $self->{last_attribute_value_state} = $self->{state};
1706            $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1707          !!!next-input-character;          !!!next-input-character;
1708          redo A;          redo A;
1709        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1710          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1711          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1712            $self->{current_token}->{first_start_tag}            !!!cp (97);
               = not defined $self->{last_emitted_start_tag_name};  
1713            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1714          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1715            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1716            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1717                !!!cp (98);
1718              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1719              } else {
1720                ## NOTE: This state should never be reached.
1721                !!!cp (99);
1722            }            }
1723          } else {          } else {
1724            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1725          }          }
1726          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1727          ## reconsume          ## reconsume
1728    
1729          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1730    
1731          redo A;          redo A;
1732        } else {        } else {
1733          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1734            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1735          ## Stay in the state          ## Stay in the state
1736          !!!next-input-character;          !!!next-input-character;
1737          redo A;          redo A;
1738        }        }
1739      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1740        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
1741          $self->{state} = 'before attribute name';          !!!cp (101);
1742            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1743          !!!next-input-character;          !!!next-input-character;
1744          redo A;          redo A;
1745        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{next_char} == 0x0026) { # &
1746          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';          !!!cp (102);
1747          $self->{state} = 'entity in attribute value';          $self->{last_attribute_value_state} = $self->{state};
1748            $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1749          !!!next-input-character;          !!!next-input-character;
1750          redo A;          redo A;
1751        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1752          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1753          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1754            $self->{current_token}->{first_start_tag}            !!!cp (103);
               = not defined $self->{last_emitted_start_tag_name};  
1755            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1756          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1757            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1758            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1759                !!!cp (104);
1760              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1761              } else {
1762                ## NOTE: This state should never be reached.
1763                !!!cp (105);
1764            }            }
1765          } else {          } else {
1766            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1767          }          }
1768          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1769          ## reconsume          ## reconsume
1770    
1771          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1772    
1773          redo A;          redo A;
1774        } else {        } else {
1775          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1776            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1777          ## Stay in the state          ## Stay in the state
1778          !!!next-input-character;          !!!next-input-character;
1779          redo A;          redo A;
1780        }        }
1781      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1782        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
1783            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
1784            $self->{next_input_character} == 0x000B or # HT            $self->{next_char} == 0x000B or # HT
1785            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
1786            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
1787          $self->{state} = 'before attribute name';          !!!cp (107);
1788          !!!next-input-character;          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1789          redo A;          !!!next-input-character;
1790        } elsif ($self->{next_input_character} == 0x0026) { # &          redo A;
1791          $self->{last_attribute_value_state} = 'attribute value (unquoted)';        } elsif ($self->{next_char} == 0x0026) { # &
1792          $self->{state} = 'entity in attribute value';          !!!cp (108);
1793          !!!next-input-character;          $self->{last_attribute_value_state} = $self->{state};
1794          redo A;          $self->{state} = ENTITY_IN_ATTRIBUTE_VALUE_STATE;
1795        } elsif ($self->{next_input_character} == 0x003E) { # >          !!!next-input-character;
1796          if ($self->{current_token}->{type} eq 'start tag') {          redo A;
1797            $self->{current_token}->{first_start_tag}        } elsif ($self->{next_char} == 0x003E) { # >
1798                = not defined $self->{last_emitted_start_tag_name};          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1799              !!!cp (109);
1800            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1801          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1802            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1803            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1804                !!!cp (110);
1805              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1806              } else {
1807                ## NOTE: This state should never be reached.
1808                !!!cp (111);
1809            }            }
1810          } else {          } else {
1811            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1812          }          }
1813          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1814          !!!next-input-character;          !!!next-input-character;
1815    
1816          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1817    
1818          redo A;          redo A;
1819        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
1820          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1821          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1822            $self->{current_token}->{first_start_tag}            !!!cp (112);
               = not defined $self->{last_emitted_start_tag_name};  
1823            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1824          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1825            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1826            if ($self->{current_token}->{attributes}) {            if ($self->{current_token}->{attributes}) {
1827                !!!cp (113);
1828              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1829              } else {
1830                ## NOTE: This state should never be reached.
1831                !!!cp (114);
1832            }            }
1833          } else {          } else {
1834            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{current_token}->{type}: Unknown token type";
1835          }          }
1836          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1837          ## reconsume          ## reconsume
1838    
1839          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{current_token}); # start tag or end tag
1840    
1841          redo A;          redo A;
1842        } else {        } else {
1843          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1844                 0x0022 => 1, # "
1845                 0x0027 => 1, # '
1846                 0x003D => 1, # =
1847                }->{$self->{next_char}}) {
1848              !!!cp (115);
1849              !!!parse-error (type => 'bad attribute value');
1850            } else {
1851              !!!cp (116);
1852            }
1853            $self->{current_attribute}->{value} .= chr ($self->{next_char});
1854          ## Stay in the state          ## Stay in the state
1855          !!!next-input-character;          !!!next-input-character;
1856          redo A;          redo A;
1857        }        }
1858      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == ENTITY_IN_ATTRIBUTE_VALUE_STATE) {
1859        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        my $token = $self->_tokenize_attempt_to_consume_an_entity
1860              (1,
1861               $self->{last_attribute_value_state}
1862                 == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE ? 0x0022 : # "
1863               $self->{last_attribute_value_state}
1864                 == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE ? 0x0027 : # '
1865               -1);
1866    
1867        unless (defined $token) {        unless (defined $token) {
1868            !!!cp (117);
1869          $self->{current_attribute}->{value} .= '&';          $self->{current_attribute}->{value} .= '&';
1870        } else {        } else {
1871            !!!cp (118);
1872          $self->{current_attribute}->{value} .= $token->{data};          $self->{current_attribute}->{value} .= $token->{data};
1873            $self->{current_attribute}->{has_reference} = $token->{has_reference};
1874          ## ISSUE: spec says "append the returned character token to the current attribute's value"          ## ISSUE: spec says "append the returned character token to the current attribute's value"
1875        }        }
1876    
1877        $self->{state} = $self->{last_attribute_value_state};        $self->{state} = $self->{last_attribute_value_state};
1878        # next-input-character is already done        # next-input-character is already done
1879        redo A;        redo A;
1880      } elsif ($self->{state} eq 'bogus comment') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1881          if ($self->{next_char} == 0x0009 or # HT
1882              $self->{next_char} == 0x000A or # LF
1883              $self->{next_char} == 0x000B or # VT
1884              $self->{next_char} == 0x000C or # FF
1885              $self->{next_char} == 0x0020) { # SP
1886            !!!cp (118);
1887            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1888            !!!next-input-character;
1889            redo A;
1890          } elsif ($self->{next_char} == 0x003E) { # >
1891            if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1892              !!!cp (119);
1893              $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1894            } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1895              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896              if ($self->{current_token}->{attributes}) {
1897                !!!cp (120);
1898                !!!parse-error (type => 'end tag attribute');
1899              } else {
1900                ## NOTE: This state should never be reached.
1901                !!!cp (121);
1902              }
1903            } else {
1904              die "$0: $self->{current_token}->{type}: Unknown token type";
1905            }
1906            $self->{state} = DATA_STATE;
1907            !!!next-input-character;
1908    
1909            !!!emit ($self->{current_token}); # start tag or end tag
1910    
1911            redo A;
1912          } elsif ($self->{next_char} == 0x002F) { # /
1913            !!!cp (122);
1914            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1915            !!!next-input-character;
1916            redo A;
1917          } elsif ($self->{next_char} == -1) {
1918            !!!parse-error (type => 'unclosed tag');
1919            if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1920              !!!cp (122.3);
1921              $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1922            } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1923              if ($self->{current_token}->{attributes}) {
1924                !!!cp (122.1);
1925                !!!parse-error (type => 'end tag attribute');
1926              } else {
1927                ## NOTE: This state should never be reached.
1928                !!!cp (122.2);
1929              }
1930            } else {
1931              die "$0: $self->{current_token}->{type}: Unknown token type";
1932            }
1933            $self->{state} = DATA_STATE;
1934            ## Reconsume.
1935            !!!emit ($self->{current_token}); # start tag or end tag
1936            redo A;
1937          } else {
1938            !!!cp ('124.1');
1939            !!!parse-error (type => 'no space between attributes');
1940            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1941            ## reconsume
1942            redo A;
1943          }
1944        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
1945          if ($self->{next_char} == 0x003E) { # >
1946            if ($self->{current_token}->{type} == END_TAG_TOKEN) {
1947              !!!cp ('124.2');
1948              !!!parse-error (type => 'nestc', token => $self->{current_token});
1949              ## TODO: Different type than slash in start tag
1950              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1951              if ($self->{current_token}->{attributes}) {
1952                !!!cp ('124.4');
1953                !!!parse-error (type => 'end tag attribute');
1954              } else {
1955                !!!cp ('124.5');
1956              }
1957              ## TODO: Test |<title></title/>|
1958            } else {
1959              !!!cp ('124.3');
1960              $self->{self_closing} = 1;
1961            }
1962    
1963            $self->{state} = DATA_STATE;
1964            !!!next-input-character;
1965    
1966            !!!emit ($self->{current_token}); # start tag or end tag
1967    
1968            redo A;
1969          } elsif ($self->{next_char} == -1) {
1970            !!!parse-error (type => 'unclosed tag');
1971            if ($self->{current_token}->{type} == START_TAG_TOKEN) {
1972              !!!cp (124.7);
1973              $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};
1974            } elsif ($self->{current_token}->{type} == END_TAG_TOKEN) {
1975              if ($self->{current_token}->{attributes}) {
1976                !!!cp (124.5);
1977                !!!parse-error (type => 'end tag attribute');
1978              } else {
1979                ## NOTE: This state should never be reached.
1980                !!!cp (124.6);
1981              }
1982            } else {
1983              die "$0: $self->{current_token}->{type}: Unknown token type";
1984            }
1985            $self->{state} = DATA_STATE;
1986            ## Reconsume.
1987            !!!emit ($self->{current_token}); # start tag or end tag
1988            redo A;
1989          } else {
1990            !!!cp ('124.4');
1991            !!!parse-error (type => 'nestc');
1992            ## TODO: This error type is wrong.
1993            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1994            ## Reconsume.
1995            redo A;
1996          }
1997        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
1998        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
1999                
2000        my $token = {type => 'comment', data => ''};        ## NOTE: Set by the previous state
2001          #my $token = {type => COMMENT_TOKEN, data => ''};
2002    
2003        BC: {        BC: {
2004          if ($self->{next_input_character} == 0x003E) { # >          if ($self->{next_char} == 0x003E) { # >
2005            $self->{state} = 'data';            !!!cp (124);
2006              $self->{state} = DATA_STATE;
2007            !!!next-input-character;            !!!next-input-character;
2008    
2009            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
2010    
2011            redo A;            redo A;
2012          } elsif ($self->{next_input_character} == -1) {          } elsif ($self->{next_char} == -1) {
2013            $self->{state} = 'data';            !!!cp (125);
2014              $self->{state} = DATA_STATE;
2015            ## reconsume            ## reconsume
2016    
2017            !!!emit ($token);            !!!emit ($self->{current_token}); # comment
2018    
2019            redo A;            redo A;
2020          } else {          } else {
2021            $token->{data} .= chr ($self->{next_input_character});            !!!cp (126);
2022              $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
2023            !!!next-input-character;            !!!next-input-character;
2024            redo BC;            redo BC;
2025          }          }
2026        } # BC        } # BC
     } elsif ($self->{state} eq 'markup declaration open') {  
       ## (only happen if PCDATA state)  
2027    
2028        my @next_char;        die "$0: _get_next_token: unexpected case [BC]";
2029        push @next_char, $self->{next_input_character};      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2030          ## (only happen if PCDATA state)
2031                
2032        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
2033          !!!next-input-character;          !!!cp (133);
2034          push @next_char, $self->{next_input_character};          $self->{state} = MD_HYPHEN_STATE;
2035          if ($self->{next_input_character} == 0x002D) { # -          !!!next-input-character;
2036            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2037            $self->{state} = 'comment start';        } elsif ($self->{next_char} == 0x0044 or # D
2038            !!!next-input-character;                 $self->{next_char} == 0x0064) { # d
2039            redo A;          ## ASCII case-insensitive.
2040          }          !!!cp (130);
2041        } elsif ($self->{next_input_character} == 0x0044 or # D          $self->{state} = MD_DOCTYPE_STATE;
2042                 $self->{next_input_character} == 0x0064) { # d          $self->{state_keyword} = chr $self->{next_char};
2043            !!!next-input-character;
2044            redo A;
2045          } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2046                   $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2047                   $self->{next_char} == 0x005B) { # [
2048            !!!cp (135.4);                
2049            $self->{state} = MD_CDATA_STATE;
2050            $self->{state_keyword} = '[';
2051          !!!next-input-character;          !!!next-input-character;
2052          push @next_char, $self->{next_input_character};          redo A;
2053          if ($self->{next_input_character} == 0x004F or # O        } else {
2054              $self->{next_input_character} == 0x006F) { # o          !!!cp (136);
           !!!next-input-character;  
           push @next_char, $self->{next_input_character};  
           if ($self->{next_input_character} == 0x0043 or # C  
               $self->{next_input_character} == 0x0063) { # c  
             !!!next-input-character;  
             push @next_char, $self->{next_input_character};  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               push @next_char, $self->{next_input_character};  
               if ($self->{next_input_character} == 0x0059 or # Y  
                   $self->{next_input_character} == 0x0079) { # y  
                 !!!next-input-character;  
                 push @next_char, $self->{next_input_character};  
                 if ($self->{next_input_character} == 0x0050 or # P  
                     $self->{next_input_character} == 0x0070) { # p  
                   !!!next-input-character;  
                   push @next_char, $self->{next_input_character};  
                   if ($self->{next_input_character} == 0x0045 or # E  
                       $self->{next_input_character} == 0x0065) { # e  
                     ## ISSUE: What a stupid code this is!  
                     $self->{state} = 'DOCTYPE';  
                     !!!next-input-character;  
                     redo A;  
                   }  
                 }  
               }  
             }  
           }  
         }  
2055        }        }
2056    
2057        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2058        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2059        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2060        $self->{state} = 'bogus comment';        ## Reconsume.
2061          $self->{state} = BOGUS_COMMENT_STATE;
2062          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
2063                                    line => $self->{line_prev},
2064                                    column => $self->{column_prev} - 1,
2065                                   };
2066        redo A;        redo A;
2067              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2068        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{next_char} == 0x002D) { # -
2069        ## 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);
2070      } elsif ($self->{state} eq 'comment start') {          $self->{current_token} = {type => COMMENT_TOKEN, data => '',
2071        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2072          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2073                                     };
2074            $self->{state} = COMMENT_START_STATE;
2075          !!!next-input-character;          !!!next-input-character;
2076          redo A;          redo A;
2077        } elsif ($self->{next_input_character} == 0x003E) { # >        } else {
2078            !!!cp (128);
2079            !!!parse-error (type => 'bogus comment',
2080                            line => $self->{line_prev},
2081                            column => $self->{column_prev} - 2);
2082            $self->{state} = BOGUS_COMMENT_STATE;
2083            ## Reconsume.
2084            $self->{current_token} = {type => COMMENT_TOKEN,
2085                                      data => '-',
2086                                      line => $self->{line_prev},
2087                                      column => $self->{column_prev} - 2,
2088                                     };
2089            redo A;
2090          }
2091        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2092          ## ASCII case-insensitive.
2093          if ($self->{next_char} == [
2094                undef,
2095                0x004F, # O
2096                0x0043, # C
2097                0x0054, # T
2098                0x0059, # Y
2099                0x0050, # P
2100              ]->[length $self->{state_keyword}] or
2101              $self->{next_char} == [
2102                undef,
2103                0x006F, # o
2104                0x0063, # c
2105                0x0074, # t
2106                0x0079, # y
2107                0x0070, # p
2108              ]->[length $self->{state_keyword}]) {
2109            !!!cp (131);
2110            ## Stay in the state.
2111            $self->{state_keyword} .= chr $self->{next_char};
2112            !!!next-input-character;
2113            redo A;
2114          } elsif ((length $self->{state_keyword}) == 6 and
2115                   ($self->{next_char} == 0x0045 or # E
2116                    $self->{next_char} == 0x0065)) { # e
2117            !!!cp (129);
2118            $self->{state} = DOCTYPE_STATE;
2119            $self->{current_token} = {type => DOCTYPE_TOKEN,
2120                                      quirks => 1,
2121                                      line => $self->{line_prev},
2122                                      column => $self->{column_prev} - 7,
2123                                     };
2124            !!!next-input-character;
2125            redo A;
2126          } else {
2127            !!!cp (132);        
2128            !!!parse-error (type => 'bogus comment',
2129                            line => $self->{line_prev},
2130                            column => $self->{column_prev} - 1 - length $self->{state_keyword});
2131            $self->{state} = BOGUS_COMMENT_STATE;
2132            ## Reconsume.
2133            $self->{current_token} = {type => COMMENT_TOKEN,
2134                                      data => $self->{state_keyword},
2135                                      line => $self->{line_prev},
2136                                      column => $self->{column_prev} - 1 - length $self->{state_keyword},
2137                                     };
2138            redo A;
2139          }
2140        } elsif ($self->{state} == MD_CDATA_STATE) {
2141          if ($self->{next_char} == {
2142                '[' => 0x0043, # C
2143                '[C' => 0x0044, # D
2144                '[CD' => 0x0041, # A
2145                '[CDA' => 0x0054, # T
2146                '[CDAT' => 0x0041, # A
2147              }->{$self->{state_keyword}}) {
2148            !!!cp (135.1);
2149            ## Stay in the state.
2150            $self->{state_keyword} .= chr $self->{next_char};
2151            !!!next-input-character;
2152            redo A;
2153          } elsif ($self->{state_keyword} eq '[CDATA' and
2154                   $self->{next_char} == 0x005B) { # [
2155            !!!cp (135.2);
2156            $self->{current_token} = {type => CHARACTER_TOKEN,
2157                                      data => '',
2158                                      line => $self->{line_prev},
2159                                      column => $self->{column_prev} - 7};
2160            $self->{state} = CDATA_SECTION_STATE;
2161            !!!next-input-character;
2162            redo A;
2163          } else {
2164            !!!cp (135.3);
2165            !!!parse-error (type => 'bogus comment',
2166                            line => $self->{line_prev},
2167                            column => $self->{column_prev} - 1 - length $self->{state_keyword});
2168            $self->{state} = BOGUS_COMMENT_STATE;
2169            ## Reconsume.
2170            $self->{current_token} = {type => COMMENT_TOKEN,
2171                                      data => $self->{state_keyword},
2172                                      line => $self->{line_prev},
2173                                      column => $self->{column_prev} - 1 - length $self->{state_keyword},
2174                                     };
2175            redo A;
2176          }
2177        } elsif ($self->{state} == COMMENT_START_STATE) {
2178          if ($self->{next_char} == 0x002D) { # -
2179            !!!cp (137);
2180            $self->{state} = COMMENT_START_DASH_STATE;
2181            !!!next-input-character;
2182            redo A;
2183          } elsif ($self->{next_char} == 0x003E) { # >
2184            !!!cp (138);
2185          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2186          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2187          !!!next-input-character;          !!!next-input-character;
2188    
2189          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2190    
2191          redo A;          redo A;
2192        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2193            !!!cp (139);
2194          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2195          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2196          ## reconsume          ## reconsume
2197    
2198          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2199    
2200          redo A;          redo A;
2201        } else {        } else {
2202            !!!cp (140);
2203          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
2204              .= chr ($self->{next_input_character});              .= chr ($self->{next_char});
2205          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
2206          !!!next-input-character;          !!!next-input-character;
2207          redo A;          redo A;
2208        }        }
2209      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2210        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
2211          $self->{state} = 'comment end';          !!!cp (141);
2212            $self->{state} = COMMENT_END_STATE;
2213          !!!next-input-character;          !!!next-input-character;
2214          redo A;          redo A;
2215        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2216            !!!cp (142);
2217          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2218          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2219          !!!next-input-character;          !!!next-input-character;
2220    
2221          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2222    
2223          redo A;          redo A;
2224        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2225            !!!cp (143);
2226          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2227          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2228          ## reconsume          ## reconsume
2229    
2230          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2231    
2232          redo A;          redo A;
2233        } else {        } else {
2234            !!!cp (144);
2235          $self->{current_token}->{data} # comment          $self->{current_token}->{data} # comment
2236              .= chr ($self->{next_input_character});              .= '-' . chr ($self->{next_char});
2237          $self->{state} = 'comment';          $self->{state} = COMMENT_STATE;
2238          !!!next-input-character;          !!!next-input-character;
2239          redo A;          redo A;
2240        }        }
2241      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2242        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
2243          $self->{state} = 'comment end dash';          !!!cp (145);
2244            $self->{state} = COMMENT_END_DASH_STATE;
2245          !!!next-input-character;          !!!next-input-character;
2246          redo A;          redo A;
2247        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2248            !!!cp (146);
2249          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2250          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2251          ## reconsume          ## reconsume
2252    
2253          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2254    
2255          redo A;          redo A;
2256        } else {        } else {
2257          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2258            $self->{current_token}->{data} .= chr ($self->{next_char}); # comment
2259          ## Stay in the state          ## Stay in the state
2260          !!!next-input-character;          !!!next-input-character;
2261          redo A;          redo A;
2262        }        }
2263      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2264        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{next_char} == 0x002D) { # -
2265          $self->{state} = 'comment end';          !!!cp (148);
2266            $self->{state} = COMMENT_END_STATE;
2267          !!!next-input-character;          !!!next-input-character;
2268          redo A;          redo A;
2269        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2270            !!!cp (149);
2271          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2272          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2273          ## reconsume          ## reconsume
2274    
2275          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2276    
2277          redo A;          redo A;
2278        } else {        } else {
2279          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2280          $self->{state} = 'comment';          $self->{current_token}->{data} .= '-' . chr ($self->{next_char}); # comment
2281            $self->{state} = COMMENT_STATE;
2282          !!!next-input-character;          !!!next-input-character;
2283          redo A;          redo A;
2284        }        }
2285      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2286        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_char} == 0x003E) { # >
2287          $self->{state} = 'data';          !!!cp (151);
2288            $self->{state} = DATA_STATE;
2289          !!!next-input-character;          !!!next-input-character;
2290    
2291          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2292    
2293          redo A;          redo A;
2294        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{next_char} == 0x002D) { # -
2295          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2296            !!!parse-error (type => 'dash in comment',
2297                            line => $self->{line_prev},
2298                            column => $self->{column_prev});
2299          $self->{current_token}->{data} .= '-'; # comment          $self->{current_token}->{data} .= '-'; # comment
2300          ## Stay in the state          ## Stay in the state
2301          !!!next-input-character;          !!!next-input-character;
2302          redo A;          redo A;
2303        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2304            !!!cp (153);
2305          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2306          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2307          ## reconsume          ## reconsume
2308    
2309          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{current_token}); # comment
2310    
2311          redo A;          redo A;
2312        } else {        } else {
2313          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2314          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2315          $self->{state} = 'comment';                          line => $self->{line_prev},
2316                            column => $self->{column_prev});
2317            $self->{current_token}->{data} .= '--' . chr ($self->{next_char}); # comment
2318            $self->{state} = COMMENT_STATE;
2319          !!!next-input-character;          !!!next-input-character;
2320          redo A;          redo A;
2321        }        }
2322      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2323        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
2324            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
2325            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
2326            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
2327            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
2328          $self->{state} = 'before DOCTYPE name';          !!!cp (155);
2329            $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2330          !!!next-input-character;          !!!next-input-character;
2331          redo A;          redo A;
2332        } else {        } else {
2333            !!!cp (156);
2334          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2335          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2336          ## reconsume          ## reconsume
2337          redo A;          redo A;
2338        }        }
2339      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2340        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
2341            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
2342            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
2343            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
2344            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
2345            !!!cp (157);
2346          ## Stay in the state          ## Stay in the state
2347          !!!next-input-character;          !!!next-input-character;
2348          redo A;          redo A;
2349        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2350            !!!cp (158);
2351          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2352          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2353          !!!next-input-character;          !!!next-input-character;
2354    
2355          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{current_token}); # DOCTYPE (quirks)
2356    
2357          redo A;          redo A;
2358        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2359            !!!cp (159);
2360          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2361          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2362          ## reconsume          ## reconsume
2363    
2364          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{current_token}); # DOCTYPE (quirks)
2365    
2366          redo A;          redo A;
2367        } else {        } else {
2368          $self->{current_token}          !!!cp (160);
2369              = {type => 'DOCTYPE',          $self->{current_token}->{name} = chr $self->{next_char};
2370                 name => chr ($self->{next_input_character}),          delete $self->{current_token}->{quirks};
                correct => 1};  
2371  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2372          $self->{state} = 'DOCTYPE name';          $self->{state} = DOCTYPE_NAME_STATE;
2373          !!!next-input-character;          !!!next-input-character;
2374          redo A;          redo A;
2375        }        }
2376      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2377  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2378        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
2379            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
2380            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
2381            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
2382            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
2383          $self->{state} = 'after DOCTYPE name';          !!!cp (161);
2384            $self->{state} = AFTER_DOCTYPE_NAME_STATE;
2385          !!!next-input-character;          !!!next-input-character;
2386          redo A;          redo A;
2387        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2388          $self->{state} = 'data';          !!!cp (162);
2389            $self->{state} = DATA_STATE;
2390          !!!next-input-character;          !!!next-input-character;
2391    
2392          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2393    
2394          redo A;          redo A;
2395        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2396            !!!cp (163);
2397          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2398          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2399          ## reconsume          ## reconsume
2400    
2401          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2402          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2403    
2404          redo A;          redo A;
2405        } else {        } else {
2406            !!!cp (164);
2407          $self->{current_token}->{name}          $self->{current_token}->{name}
2408            .= chr ($self->{next_input_character}); # DOCTYPE            .= chr ($self->{next_char}); # DOCTYPE
2409          ## Stay in the state          ## Stay in the state
2410          !!!next-input-character;          !!!next-input-character;
2411          redo A;          redo A;
2412        }        }
2413      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2414        if ($self->{next_input_character} == 0x0009 or # HT        if ($self->{next_char} == 0x0009 or # HT
2415            $self->{next_input_character} == 0x000A or # LF            $self->{next_char} == 0x000A or # LF
2416            $self->{next_input_character} == 0x000B or # VT            $self->{next_char} == 0x000B or # VT
2417            $self->{next_input_character} == 0x000C or # FF            $self->{next_char} == 0x000C or # FF
2418            $self->{next_input_character} == 0x0020) { # SP            $self->{next_char} == 0x0020) { # SP
2419            !!!cp (165);
2420          ## Stay in the state          ## Stay in the state
2421          !!!next-input-character;          !!!next-input-character;
2422          redo A;          redo A;
2423        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2424          $self->{state} = 'data';          !!!cp (166);
2425            $self->{state} = DATA_STATE;
2426          !!!next-input-character;          !!!next-input-character;
2427    
2428          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2429    
2430          redo A;          redo A;
2431        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2432            !!!cp (167);
2433          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2434          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2435          ## reconsume          ## reconsume
2436    
2437          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2438          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2439    
2440          redo A;          redo A;
2441        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{next_char} == 0x0050 or # P
2442                 $self->{next_input_character} == 0x0070) { # p                 $self->{next_char} == 0x0070) { # p
2443          !!!next-input-character;          !!!next-input-character;
2444          if ($self->{next_input_character} == 0x0055 or # U          if ($self->{next_char} == 0x0055 or # U
2445              $self->{next_input_character} == 0x0075) { # u              $self->{next_char} == 0x0075) { # u
2446            !!!next-input-character;            !!!next-input-character;
2447            if ($self->{next_input_character} == 0x0042 or # B            if ($self->{next_char} == 0x0042 or # B
2448                $self->{next_input_character} == 0x0062) { # b                $self->{next_char} == 0x0062) { # b
2449              !!!next-input-character;              !!!next-input-character;
2450              if ($self->{next_input_character} == 0x004C or # L              if ($self->{next_char} == 0x004C or # L
2451                  $self->{next_input_character} == 0x006C) { # l                  $self->{next_char} == 0x006C) { # l
2452                !!!next-input-character;                !!!next-input-character;
2453                if ($self->{next_input_character} == 0x0049 or # I                if ($self->{next_char} == 0x0049 or # I
2454                    $self->{next_input_character} == 0x0069) { # i                    $self->{next_char} == 0x0069) { # i
2455                  !!!next-input-character;                  !!!next-input-character;
2456                  if ($self->{next_input_character} == 0x0043 or # C                  if ($self->{next_char} == 0x0043 or # C
2457                      $self->{next_input_character} == 0x0063) { # c                      $self->{next_char} == 0x0063) { # c
2458                    $self->{state} = 'before DOCTYPE public identifier';                    !!!cp (168);
2459                      $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2460                    !!!next-input-character;                    !!!next-input-character;
2461                    redo A;                    redo A;
2462                    } else {
2463                      !!!cp (169);
2464                  }                  }
2465                  } else {
2466                    !!!cp (170);
2467                }                }
2468                } else {
2469                  !!!cp (171);
2470              }              }
2471              } else {
2472                !!!cp (172);
2473            }            }
2474            } else {
2475              !!!cp (173);
2476          }          }
2477    
2478          #          #
2479        } elsif ($self->{next_input_character} == 0x0053 or # S        } elsif ($self->{next_char} == 0x0053 or # S
2480                 $self->{next_input_character} == 0x0073) { # s                 $self->{next_char} == 0x0073) { # s
2481          !!!next-input-character;          !!!next-input-character;
2482          if ($self->{next_input_character} == 0x0059 or # Y          if ($self->{next_char} == 0x0059 or # Y
2483              $self->{next_input_character} == 0x0079) { # y              $self->{next_char} == 0x0079) { # y
2484            !!!next-input-character;            !!!next-input-character;
2485            if ($self->{next_input_character} == 0x0053 or # S            if ($self->{next_char} == 0x0053 or # S
2486                $self->{next_input_character} == 0x0073) { # s                $self->{next_char} == 0x0073) { # s
2487              !!!next-input-character;              !!!next-input-character;
2488              if ($self->{next_input_character} == 0x0054 or # T              if ($self->{next_char} == 0x0054 or # T
2489                  $self->{next_input_character} == 0x0074) { # t                  $self->{next_char} == 0x0074) { # t
2490                !!!next-input-character;                !!!next-input-character;
2491                if ($self->{next_input_character} == 0x0045 or # E                if ($self->{next_char} == 0x0045 or # E
2492                    $self->{next_input_character} == 0x0065) { # e                    $self->{next_char} == 0x0065) { # e
2493                  !!!next-input-character;                  !!!next-input-character;
2494                  if ($self->{next_input_character} == 0x004D or # M                  if ($self->{next_char} == 0x004D or # M
2495                      $self->{next_input_character} == 0x006D) { # m                      $self->{next_char} == 0x006D) { # m
2496                    $self->{state} = 'before DOCTYPE system identifier';                    !!!cp (174);
2497                      $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2498                    !!!next-input-character;                    !!!next-input-character;
2499                    redo A;                    redo A;
2500                    } else {
2501                      !!!cp (175);
2502                  }                  }
2503                  } else {
2504                    !!!cp (176);
2505                }                }
2506                } else {
2507                  !!!cp (177);
2508              }              }
2509              } else {
2510                !!!cp (178);
2511            }            }
2512            } else {
2513              !!!cp (179);
2514          }          }
2515    
2516          #          #
2517        } else {        } else {
2518            !!!cp (180);
2519          !!!next-input-character;          !!!next-input-character;
2520          #          #
2521        }        }
2522    
2523        !!!parse-error (type => 'string after DOCTYPE name');        !!!parse-error (type => 'string after DOCTYPE name');
2524        $self->{state} = 'bogus DOCTYPE';        $self->{current_token}->{quirks} = 1;
2525    
2526          $self->{state} = BOGUS_DOCTYPE_STATE;
2527        # next-input-character is already done        # next-input-character is already done
2528        redo A;        redo A;
2529      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2530        if ({        if ({
2531              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2532              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2533            }->{$self->{next_input_character}}) {            }->{$self->{next_char}}) {
2534            !!!cp (181);
2535          ## Stay in the state          ## Stay in the state
2536          !!!next-input-character;          !!!next-input-character;
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{next_char} eq 0x0022) { # "
2539            !!!cp (182);
2540          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
2541          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2542          !!!next-input-character;          !!!next-input-character;
2543          redo A;          redo A;
2544        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{next_char} eq 0x0027) { # '
2545            !!!cp (183);
2546          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          $self->{current_token}->{public_identifier} = ''; # DOCTYPE
2547          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2548          !!!next-input-character;          !!!next-input-character;
2549          redo A;          redo A;
2550        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{next_char} eq 0x003E) { # >
2551            !!!cp (184);
2552          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2553    
2554          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2555          !!!next-input-character;          !!!next-input-character;
2556    
2557          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2558          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2559    
2560          redo A;          redo A;
2561        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2562            !!!cp (185);
2563          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2564    
2565          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2566          ## reconsume          ## reconsume
2567    
2568          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2569          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2570    
2571          redo A;          redo A;
2572        } else {        } else {
2573            !!!cp (186);
2574          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2575          $self->{state} = 'bogus DOCTYPE';          $self->{current_token}->{quirks} = 1;
2576    
2577            $self->{state} = BOGUS_DOCTYPE_STATE;
2578          !!!next-input-character;          !!!next-input-character;
2579          redo A;          redo A;
2580        }        }
2581      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2582        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
2583          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (187);
2584            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2585            !!!next-input-character;
2586            redo A;
2587          } elsif ($self->{next_char} == 0x003E) { # >
2588            !!!cp (188);
2589            !!!parse-error (type => 'unclosed PUBLIC literal');
2590    
2591            $self->{state} = DATA_STATE;
2592          !!!next-input-character;          !!!next-input-character;
2593    
2594            $self->{current_token}->{quirks} = 1;
2595            !!!emit ($self->{current_token}); # DOCTYPE
2596    
2597          redo A;          redo A;
2598        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2599            !!!cp (189);
2600          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2601    
2602          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2603          ## reconsume          ## reconsume
2604    
2605          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2606          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2607    
2608          redo A;          redo A;
2609        } else {        } else {
2610            !!!cp (190);
2611          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{current_token}->{public_identifier} # DOCTYPE
2612              .= chr $self->{next_input_character};              .= chr $self->{next_char};
2613          ## Stay in the state          ## Stay in the state
2614          !!!next-input-character;          !!!next-input-character;
2615          redo A;          redo A;
2616        }        }
2617      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2618        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
2619          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2620            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2621            !!!next-input-character;
2622            redo A;
2623          } elsif ($self->{next_char} == 0x003E) { # >
2624            !!!cp (192);
2625            !!!parse-error (type => 'unclosed PUBLIC literal');
2626    
2627            $self->{state} = DATA_STATE;
2628          !!!next-input-character;          !!!next-input-character;
2629    
2630            $self->{current_token}->{quirks} = 1;
2631            !!!emit ($self->{current_token}); # DOCTYPE
2632    
2633          redo A;          redo A;
2634        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2635            !!!cp (193);
2636          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2637    
2638          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2639          ## reconsume          ## reconsume
2640    
2641          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2642          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2643    
2644          redo A;          redo A;
2645        } else {        } else {
2646            !!!cp (194);
2647          $self->{current_token}->{public_identifier} # DOCTYPE          $self->{current_token}->{public_identifier} # DOCTYPE
2648              .= chr $self->{next_input_character};              .= chr $self->{next_char};
2649          ## Stay in the state          ## Stay in the state
2650          !!!next-input-character;          !!!next-input-character;
2651          redo A;          redo A;
2652        }        }
2653      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2654        if ({        if ({
2655              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2656              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2657            }->{$self->{next_input_character}}) {            }->{$self->{next_char}}) {
2658            !!!cp (195);
2659          ## Stay in the state          ## Stay in the state
2660          !!!next-input-character;          !!!next-input-character;
2661          redo A;          redo A;
2662        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
2663            !!!cp (196);
2664          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2665          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2666          !!!next-input-character;          !!!next-input-character;
2667          redo A;          redo A;
2668        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
2669            !!!cp (197);
2670          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2671          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2672          !!!next-input-character;          !!!next-input-character;
2673          redo A;          redo A;
2674        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2675          $self->{state} = 'data';          !!!cp (198);
2676            $self->{state} = DATA_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678    
2679          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2680    
2681          redo A;          redo A;
2682        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2683            !!!cp (199);
2684          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2685    
2686          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2687          ## reconsume          ## reconsume
2688    
2689          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2690          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2691    
2692          redo A;          redo A;
2693        } else {        } else {
2694            !!!cp (200);
2695          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2696          $self->{state} = 'bogus DOCTYPE';          $self->{current_token}->{quirks} = 1;
2697    
2698            $self->{state} = BOGUS_DOCTYPE_STATE;
2699          !!!next-input-character;          !!!next-input-character;
2700          redo A;          redo A;
2701        }        }
2702      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2703        if ({        if ({
2704              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2705              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2706            }->{$self->{next_input_character}}) {            }->{$self->{next_char}}) {
2707            !!!cp (201);
2708          ## Stay in the state          ## Stay in the state
2709          !!!next-input-character;          !!!next-input-character;
2710          redo A;          redo A;
2711        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{next_char} == 0x0022) { # "
2712            !!!cp (202);
2713          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2714          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2715          !!!next-input-character;          !!!next-input-character;
2716          redo A;          redo A;
2717        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{next_char} == 0x0027) { # '
2718            !!!cp (203);
2719          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          $self->{current_token}->{system_identifier} = ''; # DOCTYPE
2720          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2721          !!!next-input-character;          !!!next-input-character;
2722          redo A;          redo A;
2723        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2724            !!!cp (204);
2725          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2726          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2727          !!!next-input-character;          !!!next-input-character;
2728    
2729          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2730          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2731    
2732          redo A;          redo A;
2733        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2734            !!!cp (205);
2735          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2736    
2737          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2738          ## reconsume          ## reconsume
2739    
2740          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2741          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2742    
2743          redo A;          redo A;
2744        } else {        } else {
2745            !!!cp (206);
2746          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2747          $self->{state} = 'bogus DOCTYPE';          $self->{current_token}->{quirks} = 1;
2748    
2749            $self->{state} = BOGUS_DOCTYPE_STATE;
2750          !!!next-input-character;          !!!next-input-character;
2751          redo A;          redo A;
2752        }        }
2753      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2754        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{next_char} == 0x0022) { # "
2755          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2756            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2757          !!!next-input-character;          !!!next-input-character;
2758          redo A;          redo A;
2759        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == 0x003E) { # >
2760            !!!cp (208);
2761            !!!parse-error (type => 'unclosed SYSTEM literal');
2762    
2763            $self->{state} = DATA_STATE;
2764            !!!next-input-character;
2765    
2766            $self->{current_token}->{quirks} = 1;
2767            !!!emit ($self->{current_token}); # DOCTYPE
2768    
2769            redo A;
2770          } elsif ($self->{next_char} == -1) {
2771            !!!cp (209);
2772          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2773    
2774          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2775          ## reconsume          ## reconsume
2776    
2777          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2778          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2779    
2780          redo A;          redo A;
2781        } else {        } else {
2782            !!!cp (210);
2783          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{current_token}->{system_identifier} # DOCTYPE
2784              .= chr $self->{next_input_character};              .= chr $self->{next_char};
2785          ## Stay in the state          ## Stay in the state
2786          !!!next-input-character;          !!!next-input-character;
2787          redo A;          redo A;
2788        }        }
2789      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2790        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{next_char} == 0x0027) { # '
2791          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2792            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2793            !!!next-input-character;
2794            redo A;
2795          } elsif ($self->{next_char} == 0x003E) { # >
2796            !!!cp (212);
2797            !!!parse-error (type => 'unclosed SYSTEM literal');
2798    
2799            $self->{state} = DATA_STATE;
2800          !!!next-input-character;          !!!next-input-character;
2801    
2802            $self->{current_token}->{quirks} = 1;
2803            !!!emit ($self->{current_token}); # DOCTYPE
2804    
2805          redo A;          redo A;
2806        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2807            !!!cp (213);
2808          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2809    
2810          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2811          ## reconsume          ## reconsume
2812    
2813          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2814          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2815    
2816          redo A;          redo A;
2817        } else {        } else {
2818            !!!cp (214);
2819          $self->{current_token}->{system_identifier} # DOCTYPE          $self->{current_token}->{system_identifier} # DOCTYPE
2820              .= chr $self->{next_input_character};              .= chr $self->{next_char};
2821          ## Stay in the state          ## Stay in the state
2822          !!!next-input-character;          !!!next-input-character;
2823          redo A;          redo A;
2824        }        }
2825      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2826        if ({        if ({
2827              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,
2828              #0x000D => 1, # HT, LF, VT, FF, SP, CR              #0x000D => 1, # HT, LF, VT, FF, SP, CR
2829            }->{$self->{next_input_character}}) {            }->{$self->{next_char}}) {
2830            !!!cp (215);
2831          ## Stay in the state          ## Stay in the state
2832          !!!next-input-character;          !!!next-input-character;
2833          redo A;          redo A;
2834        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{next_char} == 0x003E) { # >
2835          $self->{state} = 'data';          !!!cp (216);
2836            $self->{state} = DATA_STATE;
2837          !!!next-input-character;          !!!next-input-character;
2838    
2839          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2840    
2841          redo A;          redo A;
2842        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2843            !!!cp (217);
2844          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2845            $self->{state} = DATA_STATE;
         $self->{state} = 'data';  
2846          ## reconsume          ## reconsume
2847    
2848          delete $self->{current_token}->{correct};          $self->{current_token}->{quirks} = 1;
2849          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2850    
2851          redo A;          redo A;
2852        } else {        } else {
2853            !!!cp (218);
2854          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2855          $self->{state} = 'bogus DOCTYPE';          #$self->{current_token}->{quirks} = 1;
2856    
2857            $self->{state} = BOGUS_DOCTYPE_STATE;
2858          !!!next-input-character;          !!!next-input-character;
2859          redo A;          redo A;
2860        }        }
2861      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2862        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{next_char} == 0x003E) { # >
2863          $self->{state} = 'data';          !!!cp (219);
2864            $self->{state} = DATA_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866    
         delete $self->{current_token}->{correct};  
2867          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2868    
2869          redo A;          redo A;
2870        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{next_char} == -1) {
2871            !!!cp (220);
2872          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2873          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2874          ## reconsume          ## reconsume
2875    
         delete $self->{current_token}->{correct};  
2876          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{current_token}); # DOCTYPE
2877    
2878          redo A;          redo A;
2879        } else {        } else {
2880            !!!cp (221);
2881          ## Stay in the state          ## Stay in the state
2882          !!!next-input-character;          !!!next-input-character;
2883          redo A;          redo A;
2884        }        }
2885        } elsif ($self->{state} == CDATA_SECTION_STATE) {
2886          ## NOTE: "CDATA section state" in the state is jointly implemented
2887          ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
2888          ## and |CDATA_SECTION_MSE2_STATE|.
2889          
2890          if ($self->{next_char} == 0x005D) { # ]
2891            !!!cp (221.1);
2892            $self->{state} = CDATA_SECTION_MSE1_STATE;
2893            !!!next-input-character;
2894            redo A;
2895          } elsif ($self->{next_char} == -1) {
2896            $self->{state} = DATA_STATE;
2897            !!!next-input-character;
2898            if (length $self->{current_token}->{data}) { # character
2899              !!!cp (221.2);
2900              !!!emit ($self->{current_token}); # character
2901            } else {
2902              !!!cp (221.3);
2903              ## No token to emit. $self->{current_token} is discarded.
2904            }        
2905            redo A;
2906          } else {
2907            !!!cp (221.4);
2908            $self->{current_token}->{data} .= chr $self->{next_char};
2909            ## Stay in the state.
2910            !!!next-input-character;
2911            redo A;
2912          }
2913    
2914          ## ISSUE: "text tokens" in spec.
2915        } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
2916          if ($self->{next_char} == 0x005D) { # ]
2917            !!!cp (221.5);
2918            $self->{state} = CDATA_SECTION_MSE2_STATE;
2919            !!!next-input-character;
2920            redo A;
2921          } else {
2922            !!!cp (221.6);
2923            $self->{current_token}->{data} .= ']';
2924            $self->{state} = CDATA_SECTION_STATE;
2925            ## Reconsume.
2926            redo A;
2927          }
2928        } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
2929          if ($self->{next_char} == 0x003E) { # >
2930            $self->{state} = DATA_STATE;
2931            !!!next-input-character;
2932            if (length $self->{current_token}->{data}) { # character
2933              !!!cp (221.7);
2934              !!!emit ($self->{current_token}); # character
2935            } else {
2936              !!!cp (221.8);
2937              ## No token to emit. $self->{current_token} is discarded.
2938            }
2939            redo A;
2940          } elsif ($self->{next_char} == 0x005D) { # ]
2941            !!!cp (221.9); # character
2942            $self->{current_token}->{data} .= ']'; ## Add first "]" of "]]]".
2943            ## Stay in the state.
2944            !!!next-input-character;
2945            redo A;
2946          } else {
2947            !!!cp (221.11);
2948            $self->{current_token}->{data} .= ']]'; # character
2949            $self->{state} = CDATA_SECTION_STATE;
2950            ## Reconsume.
2951            redo A;
2952          }
2953      } else {      } else {
2954        die "$0: $self->{state}: Unknown state";        die "$0: $self->{state}: Unknown state";
2955      }      }
# Line 1605  sub _get_next_token ($) { Line 2958  sub _get_next_token ($) {
2958    die "$0: _get_next_token: unexpected case";    die "$0: _get_next_token: unexpected case";
2959  } # _get_next_token  } # _get_next_token
2960    
2961  sub _tokenize_attempt_to_consume_an_entity ($$) {  sub _tokenize_attempt_to_consume_an_entity ($$$) {
2962    my ($self, $in_attr) = @_;    my ($self, $in_attr, $additional) = @_;
2963    
2964      my ($l, $c) = ($self->{line_prev}, $self->{column_prev});
2965    
2966    if ({    if ({
2967         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,
2968         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR
2969        }->{$self->{next_input_character}}) {         $additional => 1,
2970          }->{$self->{next_char}}) {
2971        !!!cp (1001);
2972      ## Don't consume      ## Don't consume
2973      ## No error      ## No error
2974      return undef;      return undef;
2975    } elsif ($self->{next_input_character} == 0x0023) { # #    } elsif ($self->{next_char} == 0x0023) { # #
2976      !!!next-input-character;      !!!next-input-character;
2977      if ($self->{next_input_character} == 0x0078 or # x      if ($self->{next_char} == 0x0078 or # x
2978          $self->{next_input_character} == 0x0058) { # X          $self->{next_char} == 0x0058) { # X
2979        my $code;        my $code;
2980        X: {        X: {
2981          my $x_char = $self->{next_input_character};          my $x_char = $self->{next_char};
2982          !!!next-input-character;          !!!next-input-character;
2983          if (0x0030 <= $self->{next_input_character} and          if (0x0030 <= $self->{next_char} and
2984              $self->{next_input_character} <= 0x0039) { # 0..9              $self->{next_char} <= 0x0039) { # 0..9
2985              !!!cp (1002);
2986            $code ||= 0;            $code ||= 0;
2987            $code *= 0x10;            $code *= 0x10;
2988            $code += $self->{next_input_character} - 0x0030;            $code += $self->{next_char} - 0x0030;
2989            redo X;            redo X;
2990          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{next_char} and
2991                   $self->{next_input_character} <= 0x0066) { # a..f                   $self->{next_char} <= 0x0066) { # a..f
2992              !!!cp (1003);
2993            $code ||= 0;            $code ||= 0;
2994            $code *= 0x10;            $code *= 0x10;
2995            $code += $self->{next_input_character} - 0x0060 + 9;            $code += $self->{next_char} - 0x0060 + 9;
2996            redo X;            redo X;
2997          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{next_char} and
2998                   $self->{next_input_character} <= 0x0046) { # A..F                   $self->{next_char} <= 0x0046) { # A..F
2999              !!!cp (1004);
3000            $code ||= 0;            $code ||= 0;
3001            $code *= 0x10;            $code *= 0x10;
3002            $code += $self->{next_input_character} - 0x0040 + 9;            $code += $self->{next_char} - 0x0040 + 9;
3003            redo X;            redo X;
3004          } elsif (not defined $code) { # no hexadecimal digit          } elsif (not defined $code) { # no hexadecimal digit
3005            !!!parse-error (type => 'bare hcro');            !!!cp (1005);
3006            $self->{next_input_character} = 0x0023; # #            !!!parse-error (type => 'bare hcro', line => $l, column => $c);
3007            !!!back-next-input-character ($x_char);            !!!back-next-input-character ($x_char, $self->{next_char});
3008              $self->{next_char} = 0x0023; # #
3009            return undef;            return undef;
3010          } elsif ($self->{next_input_character} == 0x003B) { # ;          } elsif ($self->{next_char} == 0x003B) { # ;
3011              !!!cp (1006);
3012            !!!next-input-character;            !!!next-input-character;
3013          } else {          } else {
3014            !!!parse-error (type => 'no refc');            !!!cp (1007);
3015              !!!parse-error (type => 'no refc', line => $l, column => $c);
3016          }          }
3017    
3018          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
3019            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);            !!!cp (1008);
3020              !!!parse-error (type => 'invalid character reference',
3021                              text => (sprintf 'U+%04X', $code),
3022                              line => $l, column => $c);
3023            $code = 0xFFFD;            $code = 0xFFFD;
3024          } elsif ($code > 0x10FFFF) {          } elsif ($code > 0x10FFFF) {
3025            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);            !!!cp (1009);
3026              !!!parse-error (type => 'invalid character reference',
3027                              text => (sprintf 'U-%08X', $code),
3028                              line => $l, column => $c);
3029            $code = 0xFFFD;            $code = 0xFFFD;
3030          } elsif ($code == 0x000D) {          } elsif ($code == 0x000D) {
3031            !!!parse-error (type => 'CR character reference');            !!!cp (1010);
3032              !!!parse-error (type => 'CR character reference', line => $l, column => $c);
3033            $code = 0x000A;            $code = 0x000A;
3034          } elsif (0x80 <= $code and $code <= 0x9F) {          } elsif (0x80 <= $code and $code <= 0x9F) {
3035            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);            !!!cp (1011);
3036              !!!parse-error (type => 'C1 character reference', text => (sprintf 'U+%04X', $code), line => $l, column => $c);
3037            $code = $c1_entity_char->{$code};            $code = $c1_entity_char->{$code};
3038          }          }
3039    
3040          return {type => 'character', data => chr $code};          return {type => CHARACTER_TOKEN, data => chr $code,
3041                    has_reference => 1,
3042                    line => $l, column => $c,
3043                   };
3044        } # X        } # X
3045      } elsif (0x0030 <= $self->{next_input_character} and      } elsif (0x0030 <= $self->{next_char} and
3046               $self->{next_input_character} <= 0x0039) { # 0..9               $self->{next_char} <= 0x0039) { # 0..9
3047        my $code = $self->{next_input_character} - 0x0030;        my $code = $self->{next_char} - 0x0030;
3048        !!!next-input-character;        !!!next-input-character;
3049                
3050        while (0x0030 <= $self->{next_input_character} and        while (0x0030 <= $self->{next_char} and
3051                  $self->{next_input_character} <= 0x0039) { # 0..9                  $self->{next_char} <= 0x0039) { # 0..9
3052            !!!cp (1012);
3053          $code *= 10;          $code *= 10;
3054          $code += $self->{next_input_character} - 0x0030;          $code += $self->{next_char} - 0x0030;
3055                    
3056          !!!next-input-character;          !!!next-input-character;
3057        }        }
3058    
3059        if ($self->{next_input_character} == 0x003B) { # ;        if ($self->{next_char} == 0x003B) { # ;
3060            !!!cp (1013);
3061          !!!next-input-character;          !!!next-input-character;
3062        } else {        } else {
3063          !!!parse-error (type => 'no refc');          !!!cp (1014);
3064            !!!parse-error (type => 'no refc', line => $l, column => $c);
3065        }        }
3066    
3067        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {
3068          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (1015);
3069            !!!parse-error (type => 'invalid character reference',
3070                            text => (sprintf 'U+%04X', $code),
3071                            line => $l, column => $c);
3072          $code = 0xFFFD;          $code = 0xFFFD;
3073        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3074          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1016);
3075            !!!parse-error (type => 'invalid character reference',
3076                            text => (sprintf 'U-%08X', $code),
3077                            line => $l, column => $c);
3078          $code = 0xFFFD;          $code = 0xFFFD;
3079        } elsif ($code == 0x000D) {        } elsif ($code == 0x000D) {
3080          !!!parse-error (type => 'CR character reference');          !!!cp (1017);
3081            !!!parse-error (type => 'CR character reference',
3082                            line => $l, column => $c);
3083          $code = 0x000A;          $code = 0x000A;
3084        } elsif (0x80 <= $code and $code <= 0x9F) {        } elsif (0x80 <= $code and $code <= 0x9F) {
3085          !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          !!!cp (1018);
3086            !!!parse-error (type => 'C1 character reference',
3087                            text => (sprintf 'U+%04X', $code),
3088                            line => $l, column => $c);
3089          $code = $c1_entity_char->{$code};          $code = $c1_entity_char->{$code};
3090        }        }
3091                
3092        return {type => 'character', data => chr $code};        return {type => CHARACTER_TOKEN, data => chr $code, has_reference => 1,
3093                  line => $l, column => $c,
3094                 };
3095      } else {      } else {
3096        !!!parse-error (type => 'bare nero');        !!!cp (1019);
3097        !!!back-next-input-character ($self->{next_input_character});        !!!parse-error (type => 'bare nero', line => $l, column => $c);
3098        $self->{next_input_character} = 0x0023; # #        !!!back-next-input-character ($self->{next_char});
3099          $self->{next_char} = 0x0023; # #
3100        return undef;        return undef;
3101      }      }
3102    } elsif ((0x0041 <= $self->{next_input_character} and    } elsif ((0x0041 <= $self->{next_char} and
3103              $self->{next_input_character} <= 0x005A) or              $self->{next_char} <= 0x005A) or
3104             (0x0061 <= $self->{next_input_character} and             (0x0061 <= $self->{next_char} and
3105              $self->{next_input_character} <= 0x007A)) {              $self->{next_char} <= 0x007A)) {
3106      my $entity_name = chr $self->{next_input_character};      my $entity_name = chr $self->{next_char};
3107      !!!next-input-character;      !!!next-input-character;
3108    
3109      my $value = $entity_name;      my $value = $entity_name;
3110      my $match;      my $match = 0;
3111      require Whatpm::_NamedEntityList;      require Whatpm::_NamedEntityList;
3112      our $EntityChar;      our $EntityChar;
3113    
3114      while (length $entity_name < 10 and      while (length $entity_name < 30 and
3115             ## NOTE: Some number greater than the maximum length of entity name             ## NOTE: Some number greater than the maximum length of entity name
3116             ((0x0041 <= $self->{next_input_character} and # a             ((0x0041 <= $self->{next_char} and # a
3117               $self->{next_input_character} <= 0x005A) or # x               $self->{next_char} <= 0x005A) or # x
3118              (0x0061 <= $self->{next_input_character} and # a              (0x0061 <= $self->{next_char} and # a
3119               $self->{next_input_character} <= 0x007A) or # z               $self->{next_char} <= 0x007A) or # z
3120              (0x0030 <= $self->{next_input_character} and # 0              (0x0030 <= $self->{next_char} and # 0
3121               $self->{next_input_character} <= 0x0039) or # 9               $self->{next_char} <= 0x0039) or # 9
3122              $self->{next_input_character} == 0x003B)) { # ;              $self->{next_char} == 0x003B)) { # ;
3123        $entity_name .= chr $self->{next_input_character};        $entity_name .= chr $self->{next_char};
3124        if (defined $EntityChar->{$entity_name}) {        if (defined $EntityChar->{$entity_name}) {
3125          if ($self->{next_input_character} == 0x003B) { # ;          if ($self->{next_char} == 0x003B) { # ;
3126              !!!cp (1020);
3127            $value = $EntityChar->{$entity_name};            $value = $EntityChar->{$entity_name};
3128            $match = 1;            $match = 1;
3129            !!!next-input-character;            !!!next-input-character;
3130            last;            last;
3131          } elsif (not $in_attr) {          } else {
3132              !!!cp (1021);
3133            $value = $EntityChar->{$entity_name};            $value = $EntityChar->{$entity_name};
3134            $match = -1;            $match = -1;
3135          } else {            !!!next-input-character;
           $value .= chr $self->{next_input_character};  
3136          }          }
3137        } else {        } else {
3138          $value .= chr $self->{next_input_character};          !!!cp (1022);
3139            $value .= chr $self->{next_char};
3140            $match *= 2;
3141            !!!next-input-character;
3142        }        }
       !!!next-input-character;  
3143      }      }
3144            
3145      if ($match > 0) {      if ($match > 0) {
3146        return {type => 'character', data => $value};        !!!cp (1023);
3147          return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
3148                  line => $l, column => $c,
3149                 };
3150      } elsif ($match < 0) {      } elsif ($match < 0) {
3151        !!!parse-error (type => 'no refc');        !!!parse-error (type => 'no refc', line => $l, column => $c);
3152        return {type => 'character', data => $value};        if ($in_attr and $match < -1) {
3153            !!!cp (1024);
3154            return {type => CHARACTER_TOKEN, data => '&'.$entity_name,
3155                    line => $l, column => $c,
3156                   };
3157          } else {
3158            !!!cp (1025);
3159            return {type => CHARACTER_TOKEN, data => $value, has_reference => 1,
3160                    line => $l, column => $c,
3161                   };
3162          }
3163      } else {      } else {
3164        !!!parse-error (type => 'bare ero');        !!!cp (1026);
3165        ## NOTE: No characters are consumed in the spec.        !!!parse-error (type => 'bare ero', line => $l, column => $c);
3166        return {type => 'character', data => '&'.$value};        ## NOTE: "No characters are consumed" in the spec.
3167          return {type => CHARACTER_TOKEN, data => '&'.$value,
3168                  line => $l, column => $c,
3169                 };
3170      }      }
3171    } else {    } else {
3172        !!!cp (1027);
3173      ## no characters are consumed      ## no characters are consumed
3174      !!!parse-error (type => 'bare ero');      !!!parse-error (type => 'bare ero', line => $l, column => $c);
3175      return undef;      return undef;
3176    }    }
3177  } # _tokenize_attempt_to_consume_an_entity  } # _tokenize_attempt_to_consume_an_entity
# Line 1772  sub _initialize_tree_constructor ($) { Line 3183  sub _initialize_tree_constructor ($) {
3183    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3184    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3185    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3186      $self->{document}->set_user_data (manakai_source_line => 1);
3187      $self->{document}->set_user_data (manakai_source_column => 1);
3188  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3189    
3190  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1798  sub _construct_tree ($) { Line 3211  sub _construct_tree ($) {
3211        
3212    !!!next-token;    !!!next-token;
3213    
   $self->{insertion_mode} = 'before head';  
3214    undef $self->{form_element};    undef $self->{form_element};
3215    undef $self->{head_element};    undef $self->{head_element};
3216    $self->{open_elements} = [];    $self->{open_elements} = [];
3217    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3218    
3219      ## NOTE: The "initial" insertion mode.
3220    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3221    
3222      ## NOTE: The "before html" insertion mode.
3223    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3224      $self->{insertion_mode} = BEFORE_HEAD_IM;
3225    
3226      ## NOTE: The "before head" insertion mode and so on.
3227    $self->_tree_construction_main;    $self->_tree_construction_main;
3228  } # _construct_tree  } # _construct_tree
3229    
3230  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3231    my $self = shift;    my $self = shift;
3232    
3233      ## NOTE: "initial" insertion mode
3234    
3235    INITIAL: {    INITIAL: {
3236      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
3237        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3238        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
3239        ## language.        ## language.
3240        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3241        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3242        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3243        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
           defined $token->{public_identifier} or  
3244            defined $token->{system_identifier}) {            defined $token->{system_identifier}) {
3245          !!!parse-error (type => 'not HTML5');          !!!cp ('t1');
3246            !!!parse-error (type => 'not HTML5', token => $token);
3247        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3248          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3249          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3250          } elsif (defined $token->{public_identifier}) {
3251            if ($token->{public_identifier} eq 'XSLT-compat') {
3252              !!!cp ('t1.2');
3253              !!!parse-error (type => 'XSLT-compat', token => $token,
3254                              level => $self->{level}->{should});
3255            } else {
3256              !!!parse-error (type => 'not HTML5', token => $token);
3257            }
3258          } else {
3259            !!!cp ('t3');
3260            #
3261        }        }
3262                
3263        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3264          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3265          ## NOTE: Default value for both |public_id| and |system_id| attributes
3266          ## are empty strings, so that we don't set any value in missing cases.
3267        $doctype->public_id ($token->{public_identifier})        $doctype->public_id ($token->{public_identifier})
3268            if defined $token->{public_identifier};            if defined $token->{public_identifier};
3269        $doctype->system_id ($token->{system_identifier})        $doctype->system_id ($token->{system_identifier})
# Line 1838  sub _tree_construction_initial ($) { Line 3272  sub _tree_construction_initial ($) {
3272        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3273        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3274                
3275        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3276            !!!cp ('t4');
3277          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3278        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{public_identifier}) {
3279          my $pubid = $token->{public_identifier};          my $pubid = $token->{public_identifier};
3280          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3281          if ({          my $prefix = [
3282            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3283            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3284            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3285            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3286            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3287            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3288            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3289            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3290            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3291            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3292            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3293            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3294            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3295            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3296            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3297            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3298            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3299            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3300            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3301            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3302            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3303            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3304            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3305            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3306            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3307            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3308            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3309            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3310            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3311            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3312            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3313            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3314            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3315            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3316            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3317            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3318            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3319            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3320            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3321            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3322            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3323            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3324            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3325            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3326            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3327            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3328            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3329            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3330            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3331            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3332            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3333            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3334            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3335            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3336            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3337            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3338            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3339            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3340            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3341            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3342            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3343            "-//W3C//DTD W3 HTML//EN" => 1,            }
3344            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3345            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3346            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3347            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3348            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3349            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3350            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3351          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3352                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3353            if (defined $token->{system_identifier}) {            if (defined $token->{system_identifier}) {
3354                !!!cp ('t6');
3355              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3356            } else {            } else {
3357                !!!cp ('t7');
3358              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3359            }            }
3360          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3361                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3362              !!!cp ('t8');
3363            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3364            } else {
3365              !!!cp ('t9');
3366          }          }
3367          } else {
3368            !!!cp ('t10');
3369        }        }
3370        if (defined $token->{system_identifier}) {        if (defined $token->{system_identifier}) {
3371          my $sysid = $token->{system_identifier};          my $sysid = $token->{system_identifier};
3372          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3373          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") {
3374              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3375              ## marked as quirks.
3376            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3377              !!!cp ('t11');
3378            } else {
3379              !!!cp ('t12');
3380          }          }
3381          } else {
3382            !!!cp ('t13');
3383        }        }
3384                
3385        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3386        !!!next-token;        !!!next-token;
3387        return;        return;
3388      } elsif ({      } elsif ({
3389                'start tag' => 1,                START_TAG_TOKEN, 1,
3390                'end tag' => 1,                END_TAG_TOKEN, 1,
3391                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
3392               }->{$token->{type}}) {               }->{$token->{type}}) {
3393        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3394          !!!parse-error (type => 'no DOCTYPE', token => $token);
3395        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3396        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3397        ## reprocess        ## reprocess
3398          !!!ack-later;
3399        return;        return;
3400      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3401        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
3402          ## Ignore the token          ## Ignore the token
3403    
3404          unless (length $token->{data}) {          unless (length $token->{data}) {
3405            ## Stay in the phase            !!!cp ('t15');
3406              ## Stay in the insertion mode.
3407            !!!next-token;            !!!next-token;
3408            redo INITIAL;            redo INITIAL;
3409            } else {
3410              !!!cp ('t16');
3411          }          }
3412          } else {
3413            !!!cp ('t17');
3414        }        }
3415    
3416        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3417        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3418        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3419        ## reprocess        ## reprocess
3420        return;        return;
3421      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
3422          !!!cp ('t18');
3423        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3424        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3425                
3426        ## Stay in the phase.        ## Stay in the insertion mode.
3427        !!!next-token;        !!!next-token;
3428        redo INITIAL;        redo INITIAL;
3429      } else {      } else {
3430        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
3431      }      }
3432    } # INITIAL    } # INITIAL
3433    
3434      die "$0: _tree_construction_initial: This should be never reached";
3435  } # _tree_construction_initial  } # _tree_construction_initial
3436    
3437  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3438    my $self = shift;    my $self = shift;
3439    
3440      ## NOTE: "before html" insertion mode.
3441        
3442    B: {    B: {
3443        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3444          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3445            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3446          ## Ignore the token          ## Ignore the token
3447          ## Stay in the phase          ## Stay in the insertion mode.
3448          !!!next-token;          !!!next-token;
3449          redo B;          redo B;
3450        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3451            !!!cp ('t20');
3452          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3453          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3454          ## Stay in the phase          ## Stay in the insertion mode.
3455          !!!next-token;          !!!next-token;
3456          redo B;          redo B;
3457        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3458          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D
3459            ## Ignore the token.            ## Ignore the token.
3460    
3461            unless (length $token->{data}) {            unless (length $token->{data}) {
3462              ## Stay in the phase              !!!cp ('t21');
3463                ## Stay in the insertion mode.
3464              !!!next-token;              !!!next-token;
3465              redo B;              redo B;
3466              } else {
3467                !!!cp ('t22');
3468            }            }
3469            } else {
3470              !!!cp ('t23');
3471          }          }
3472    
3473            $self->{application_cache_selection}->(undef);
3474    
3475          #          #
3476          } elsif ($token->{type} == START_TAG_TOKEN) {
3477            if ($token->{tag_name} eq 'html') {
3478              my $root_element;
3479              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3480              $self->{document}->append_child ($root_element);
3481              push @{$self->{open_elements}},
3482                  [$root_element, $el_category->{html}];
3483    
3484              if ($token->{attributes}->{manifest}) {
3485                !!!cp ('t24');
3486                $self->{application_cache_selection}
3487                    ->($token->{attributes}->{manifest}->{value});
3488                ## ISSUE: Spec is unclear on relative references.
3489                ## According to Hixie (#whatwg 2008-03-19), it should be
3490                ## resolved against the base URI of the document in HTML
3491                ## or xml:base of the element in XHTML.
3492              } else {
3493                !!!cp ('t25');
3494                $self->{application_cache_selection}->(undef);
3495              }
3496    
3497              !!!nack ('t25c');
3498    
3499              !!!next-token;
3500              return; ## Go to the "before head" insertion mode.
3501            } else {
3502              !!!cp ('t25.1');
3503              #
3504            }
3505        } elsif ({        } elsif ({
3506                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3507                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3508                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3509          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3510          #          #
3511        } else {        } else {
3512          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3513        }        }
3514        my $root_element; !!!create-element ($root_element, 'html');  
3515        $self->{document}->append_child ($root_element);      my $root_element;
3516        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3517        #$phase = 'main';      $self->{document}->append_child ($root_element);
3518        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3519        #redo B;  
3520        return;      $self->{application_cache_selection}->(undef);
3521    
3522        ## NOTE: Reprocess the token.
3523        !!!ack-later;
3524        return; ## Go to the "before head" insertion mode.
3525    
3526        ## ISSUE: There is an issue in the spec
3527    } # B    } # B
3528    
3529      die "$0: _tree_construction_root_element: This should never be reached";
3530  } # _tree_construction_root_element  } # _tree_construction_root_element
3531    
3532  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2036  sub _reset_insertion_mode ($) { Line 3541  sub _reset_insertion_mode ($) {
3541            
3542      ## Step 3      ## Step 3
3543      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"!?  
3544        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3545          $last = 1;          $last = 1;
3546          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3547            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3548                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3549              #          } else {
3550            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3551          }          }
3552        }        }
3553              
3554        ## Step 4..13        ## Step 4..14
3555        my $new_mode = {        my $new_mode;
3556                        select => 'in select',        if ($node->[1] & FOREIGN_EL) {
3557                        td => 'in cell',          !!!cp ('t28.1');
3558                        th => 'in cell',          ## NOTE: Strictly spaking, the line below only applies to MathML and
3559                        tr => 'in row',          ## SVG elements.  Currently the HTML syntax supports only MathML and
3560                        tbody => 'in table body',          ## SVG elements as foreigners.
3561                        thead => 'in table head',          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3562                        tfoot => 'in table foot',        } elsif ($node->[1] & TABLE_CELL_EL) {
3563                        caption => 'in caption',          if ($last) {
3564                        colgroup => 'in column group',            !!!cp ('t28.2');
3565                        table => 'in table',            #
3566                        head => 'in body', # not in head!          } else {
3567                        body => 'in body',            !!!cp ('t28.3');
3568                        frameset => 'in frameset',            $new_mode = IN_CELL_IM;
3569                       }->{$node->[1]};          }
3570          } else {
3571            !!!cp ('t28.4');
3572            $new_mode = {
3573                          select => IN_SELECT_IM,
3574                          ## NOTE: |option| and |optgroup| do not set
3575                          ## insertion mode to "in select" by themselves.
3576                          tr => IN_ROW_IM,
3577                          tbody => IN_TABLE_BODY_IM,
3578                          thead => IN_TABLE_BODY_IM,
3579                          tfoot => IN_TABLE_BODY_IM,
3580                          caption => IN_CAPTION_IM,
3581                          colgroup => IN_COLUMN_GROUP_IM,
3582                          table => IN_TABLE_IM,
3583                          head => IN_BODY_IM, # not in head!
3584                          body => IN_BODY_IM,
3585                          frameset => IN_FRAMESET_IM,
3586                         }->{$node->[0]->manakai_local_name};
3587          }
3588        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3589                
3590        ## Step 14        ## Step 15
3591        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3592          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3593            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3594              $self->{insertion_mode} = BEFORE_HEAD_IM;
3595          } else {          } else {
3596            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3597              !!!cp ('t30');
3598              $self->{insertion_mode} = AFTER_HEAD_IM;
3599          }          }
3600          return;          return;
3601          } else {
3602            !!!cp ('t31');
3603        }        }
3604                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3605        ## Step 16        ## Step 16
3606          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3607          
3608          ## Step 17
3609        $i--;        $i--;
3610        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3611                
3612        ## Step 17        ## Step 18
3613        redo S3;        redo S3;
3614      } # S3      } # S3
3615    
3616      die "$0: _reset_insertion_mode: This line should never be reached";
3617  } # _reset_insertion_mode  } # _reset_insertion_mode
3618    
3619  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3620    my $self = shift;    my $self = shift;
3621    
   my $phase = 'main';  
   
3622    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3623    
3624    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2114  sub _tree_construction_main ($) { Line 3635  sub _tree_construction_main ($) {
3635      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3636      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3637        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3638            !!!cp ('t32');
3639          return;          return;
3640        }        }
3641      }      }
# Line 2128  sub _tree_construction_main ($) { Line 3650  sub _tree_construction_main ($) {
3650    
3651        ## Step 6        ## Step 6
3652        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3653            !!!cp ('t33_1');
3654          #          #
3655        } else {        } else {
3656          my $in_open_elements;          my $in_open_elements;
3657          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3658            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3659                !!!cp ('t33');
3660              $in_open_elements = 1;              $in_open_elements = 1;
3661              last OE;              last OE;
3662            }            }
3663          }          }
3664          if ($in_open_elements) {          if ($in_open_elements) {
3665              !!!cp ('t34');
3666            #            #
3667          } else {          } else {
3668              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3669              !!!cp ('t35');
3670            redo S4;            redo S4;
3671          }          }
3672        }        }
# Line 2162  sub _tree_construction_main ($) { Line 3689  sub _tree_construction_main ($) {
3689    
3690        ## Step 11        ## Step 11
3691        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3692            !!!cp ('t36');
3693          ## Step 7'          ## Step 7'
3694          $i++;          $i++;
3695          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3696                    
3697          redo S7;          redo S7;
3698        }        }
3699    
3700          !!!cp ('t37');
3701      } # S7      } # S7
3702    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3703    
3704    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3705      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3706        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3707            !!!cp ('t38');
3708          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3709          return;          return;
3710        }        }
3711      }      }
3712    
3713        !!!cp ('t39');
3714    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3715    
3716    my $parse_rcdata = sub ($$) {    my $insert;
3717      my ($content_model_flag, $insert) = @_;  
3718      my $parse_rcdata = sub ($) {
3719        my ($content_model_flag) = @_;
3720    
3721      ## Step 1      ## Step 1
3722      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3723      my $el;      my $el;
3724      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3725    
3726      ## Step 2      ## Step 2
3727      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3728    
3729      ## Step 3      ## Step 3
3730      $self->{content_model_flag} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3731      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3732    
3733      ## Step 4      ## Step 4
3734      my $text = '';      my $text = '';
3735        !!!nack ('t40.1');
3736      !!!next-token;      !!!next-token;
3737      while ($token->{type} eq 'character') { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3738          !!!cp ('t40');
3739        $text .= $token->{data};        $text .= $token->{data};
3740        !!!next-token;        !!!next-token;
3741      }      }
3742    
3743      ## Step 5      ## Step 5
3744      if (length $text) {      if (length $text) {
3745          !!!cp ('t41');
3746        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
3747        $el->append_child ($text);        $el->append_child ($text);
3748      }      }
3749    
3750      ## Step 6      ## Step 6
3751      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
3752    
3753      ## Step 7      ## Step 7
3754      if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
3755            $token->{tag_name} eq $start_tag_name) {
3756          !!!cp ('t42');
3757        ## Ignore the token        ## Ignore the token
3758      } else {      } else {
3759        !!!parse-error (type => 'in '.$content_model_flag.':#'.$token->{type});        ## NOTE: An end-of-file token.
3760          if ($content_model_flag == CDATA_CONTENT_MODEL) {
3761            !!!cp ('t43');
3762            !!!parse-error (type => 'in CDATA:#eof', token => $token);
3763          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
3764            !!!cp ('t44');
3765            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
3766          } else {
3767            die "$0: $content_model_flag in parse_rcdata";
3768          }
3769      }      }
3770      !!!next-token;      !!!next-token;
3771    }; # $parse_rcdata    }; # $parse_rcdata
3772    
3773    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
3774      my $script_el;      my $script_el;
3775      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
3776      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
3777    
3778      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
3779      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3780            
3781      my $text = '';      my $text = '';
3782        !!!nack ('t45.1');
3783      !!!next-token;      !!!next-token;
3784      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
3785          !!!cp ('t45');
3786        $text .= $token->{data};        $text .= $token->{data};
3787        !!!next-token;        !!!next-token;
3788      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
3789      if (length $text) {      if (length $text) {
3790          !!!cp ('t46');
3791        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
3792      }      }
3793                                
3794      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
3795    
3796      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
3797          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
3798          !!!cp ('t47');
3799        ## Ignore the token        ## Ignore the token
3800      } else {      } else {
3801        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
3802          !!!parse-error (type => 'in CDATA:#eof', token => $token);
3803        ## ISSUE: And ignore?        ## ISSUE: And ignore?
3804        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3805      }      }
3806            
3807      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
3808          !!!cp ('t49');
3809        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
3810      } else {      } else {
3811          !!!cp ('t50');
3812        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
3813        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
3814    
# Line 2267  sub _tree_construction_main ($) { Line 3822  sub _tree_construction_main ($) {
3822      !!!next-token;      !!!next-token;
3823    }; # $script_start_tag    }; # $script_start_tag
3824    
3825      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
3826      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
3827      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
3828    
3829    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
3830      my $tag_name = shift;      my $end_tag_token = shift;
3831        my $tag_name = $end_tag_token->{tag_name};
3832    
3833        ## NOTE: The adoption agency algorithm (AAA).
3834    
3835      FET: {      FET: {
3836        ## Step 1        ## Step 1
3837        my $formatting_element;        my $formatting_element;
3838        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
3839        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
3840          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3841              !!!cp ('t52');
3842              last AFE;
3843            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
3844                         eq $tag_name) {
3845              !!!cp ('t51');
3846            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
3847            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
3848            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
3849          }          }
3850        } # AFE        } # AFE
3851        unless (defined $formatting_element) {        unless (defined $formatting_element) {
3852          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
3853            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
3854          ## Ignore the token          ## Ignore the token
3855          !!!next-token;          !!!next-token;
3856          return;          return;
# Line 2296  sub _tree_construction_main ($) { Line 3862  sub _tree_construction_main ($) {
3862          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3863          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
3864            if ($in_scope) {            if ($in_scope) {
3865                !!!cp ('t54');
3866              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
3867              last INSCOPE;              last INSCOPE;
3868            } else { # in open elements but not in scope            } else { # in open elements but not in scope
3869              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
3870                !!!parse-error (type => 'unmatched end tag',
3871                                text => $token->{tag_name},
3872                                token => $end_tag_token);
3873              ## Ignore the token              ## Ignore the token
3874              !!!next-token;              !!!next-token;
3875              return;              return;
3876            }            }
3877          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
3878                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
3879            $in_scope = 0;            $in_scope = 0;
3880          }          }
3881        } # INSCOPE        } # INSCOPE
3882        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
3883          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
3884            !!!parse-error (type => 'unmatched end tag',
3885                            text => $token->{tag_name},
3886                            token => $end_tag_token);
3887          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
3888          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
3889          return;          return;
3890        }        }
3891        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
3892          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
3893            !!!parse-error (type => 'not closed',
3894                            text => $self->{open_elements}->[-1]->[0]
3895                                ->manakai_local_name,
3896                            token => $end_tag_token);
3897        }        }
3898                
3899        ## Step 2        ## Step 2
# Line 2326  sub _tree_construction_main ($) { Line 3901  sub _tree_construction_main ($) {
3901        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
3902        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
3903          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
3904          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
3905              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
3906              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
3907               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
3908              !!!cp ('t59');
3909            $furthest_block = $node;            $furthest_block = $node;
3910            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
3911          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
3912              !!!cp ('t60');
3913            last OE;            last OE;
3914          }          }
3915        } # OE        } # OE
3916                
3917        ## Step 3        ## Step 3
3918        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
3919            !!!cp ('t61');
3920          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
3921          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
3922          !!!next-token;          !!!next-token;
# Line 2351  sub _tree_construction_main ($) { Line 3929  sub _tree_construction_main ($) {
3929        ## Step 5        ## Step 5
3930        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
3931        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
3932            !!!cp ('t62');
3933          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
3934        }        }
3935                
# Line 2373  sub _tree_construction_main ($) { Line 3952  sub _tree_construction_main ($) {
3952          S7S2: {          S7S2: {
3953            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
3954              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
3955                  !!!cp ('t63');
3956                $node_i_in_active = $_;                $node_i_in_active = $_;
3957                last S7S2;                last S7S2;
3958              }              }
# Line 2386  sub _tree_construction_main ($) { Line 3966  sub _tree_construction_main ($) {
3966                    
3967          ## Step 4          ## Step 4
3968          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
3969              !!!cp ('t64');
3970            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
3971          }          }
3972                    
3973          ## Step 5          ## Step 5
3974          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
3975              !!!cp ('t65');
3976            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
3977            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
3978            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2408  sub _tree_construction_main ($) { Line 3990  sub _tree_construction_main ($) {
3990        } # S7          } # S7  
3991                
3992        ## Step 8        ## Step 8
3993        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
3994            my $foster_parent_element;
3995            my $next_sibling;
3996            OE: for (reverse 0..$#{$self->{open_elements}}) {
3997              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
3998                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
3999                                 if (defined $parent and $parent->node_type == 1) {
4000                                   !!!cp ('t65.1');
4001                                   $foster_parent_element = $parent;
4002                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4003                                 } else {
4004                                   !!!cp ('t65.2');
4005                                   $foster_parent_element
4006                                     = $self->{open_elements}->[$_ - 1]->[0];
4007                                 }
4008                                 last OE;
4009                               }
4010                             } # OE
4011                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4012                               unless defined $foster_parent_element;
4013            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4014            $open_tables->[-1]->[1] = 1; # tainted
4015          } else {
4016            !!!cp ('t65.3');
4017            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4018          }
4019                
4020        ## Step 9        ## Step 9
4021        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2425  sub _tree_construction_main ($) { Line 4032  sub _tree_construction_main ($) {
4032        my $i;        my $i;
4033        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4034          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4035              !!!cp ('t66');
4036            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4037            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4038          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4039              !!!cp ('t67');
4040            $i = $_;            $i = $_;
4041          }          }
4042        } # AFE        } # AFE
# Line 2437  sub _tree_construction_main ($) { Line 4046  sub _tree_construction_main ($) {
4046        undef $i;        undef $i;
4047        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4048          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4049              !!!cp ('t68');
4050            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4051            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4052          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4053              !!!cp ('t69');
4054            $i = $_;            $i = $_;
4055          }          }
4056        } # OE        } # OE
# Line 2450  sub _tree_construction_main ($) { Line 4061  sub _tree_construction_main ($) {
4061      } # FET      } # FET
4062    }; # $formatting_end_tag    }; # $formatting_end_tag
4063    
4064    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4065      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4066    }; # $insert_to_current    }; # $insert_to_current
4067    
4068    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4069                         my $child = shift;      my $child = shift;
4070                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4071                              table => 1, tbody => 1, tfoot => 1,        # MUST
4072                              thead => 1, tr => 1,        my $foster_parent_element;
4073                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4074                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4075                           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') {  
4076                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4077                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4078                                   !!!cp ('t70');
4079                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4080                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4081                               } else {                               } else {
4082                                   !!!cp ('t71');
4083                                 $foster_parent_element                                 $foster_parent_element
4084                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4085                               }                               }
# Line 2480  sub _tree_construction_main ($) { Line 4090  sub _tree_construction_main ($) {
4090                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4091                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4092                             ($child, $next_sibling);                             ($child, $next_sibling);
4093                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4094                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4095                         }        !!!cp ('t72');
4096          $self->{open_elements}->[-1]->[0]->append_child ($child);
4097        }
4098    }; # $insert_to_foster    }; # $insert_to_foster
4099    
4100    my $in_body = sub {    B: while (1) {
4101      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
4102      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
4103        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4104          ## NOTE: This is an "as if in head" code clone        ## Ignore the token
4105          $script_start_tag->($insert);        ## Stay in the phase
4106          return;        !!!next-token;
4107        } elsif ($token->{tag_name} eq 'style') {        next B;
4108          ## NOTE: This is an "as if in head" code clone      } elsif ($token->{type} == START_TAG_TOKEN and
4109          $parse_rcdata->('CDATA', $insert);               $token->{tag_name} eq 'html') {
4110          return;        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4111        } elsif ({          !!!cp ('t79');
4112                  base => 1, link => 1, meta => 1,          !!!parse-error (type => 'after html', text => 'html', token => $token);
4113                 }->{$token->{tag_name}}) {          $self->{insertion_mode} = AFTER_BODY_IM;
4114          ## NOTE: This is an "as if in head" code clone, only "-t" differs        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4115          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!cp ('t80');
4116          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          !!!parse-error (type => 'after html', text => 'html', token => $token);
4117          !!!next-token;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4118          ## TODO: Extracting |charset| from |meta|.        } else {
4119          return;          !!!cp ('t81');
4120        } elsif ($token->{tag_name} eq 'title') {        }
4121          !!!parse-error (type => 'in body:title');  
4122          ## NOTE: This is an "as if in head" code clone        !!!cp ('t82');
4123          $parse_rcdata->('RCDATA', sub {        !!!parse-error (type => 'not first start tag', token => $token);
4124            if (defined $self->{head_element}) {        my $top_el = $self->{open_elements}->[0]->[0];
4125              $self->{head_element}->append_child ($_[0]);        for my $attr_name (keys %{$token->{attributes}}) {
4126            } else {          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4127              $insert->($_[0]);            !!!cp ('t84');
4128            }            $top_el->set_attribute_ns
4129          });              (undef, [undef, $attr_name],
4130          return;               $token->{attributes}->{$attr_name}->{value});
       } 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});  
             }  
           }  
4131          }          }
4132          }
4133          !!!nack ('t84.1');
4134          !!!next-token;
4135          next B;
4136        } elsif ($token->{type} == COMMENT_TOKEN) {
4137          my $comment = $self->{document}->create_comment ($token->{data});
4138          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4139            !!!cp ('t85');
4140            $self->{document}->append_child ($comment);
4141          } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4142            !!!cp ('t86');
4143            $self->{open_elements}->[0]->[0]->append_child ($comment);
4144          } else {
4145            !!!cp ('t87');
4146            $self->{open_elements}->[-1]->[0]->append_child ($comment);
4147          }
4148          !!!next-token;
4149          next B;
4150        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4151          if ($token->{type} == CHARACTER_TOKEN) {
4152            !!!cp ('t87.1');
4153            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4154          !!!next-token;          !!!next-token;
4155          return;          next B;
4156        } elsif ({        } elsif ($token->{type} == START_TAG_TOKEN) {
4157                  address => 1, blockquote => 1, center => 1, dir => 1,          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4158                  div => 1, dl => 1, fieldset => 1, listing => 1,               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4159                  menu => 1, ol => 1, p => 1, ul => 1,              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4160                  pre => 1,              ($token->{tag_name} eq 'svg' and
4161                 }->{$token->{tag_name}}) {               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4162          ## has a p element in scope            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4163          INSCOPE: for (reverse @{$self->{open_elements}}) {            !!!cp ('t87.2');
4164            if ($_->[1] eq 'p') {            #
4165              !!!back-token;          } elsif ({
4166              $token = {type => 'end tag', tag_name => 'p'};                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4167              return;                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4168            } elsif ({                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4169                      table => 1, caption => 1, td => 1, th => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4170                      button => 1, marquee => 1, object => 1, html => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4171                     }->{$_->[1]}) {                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4172              last INSCOPE;                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4173            }                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4174          } # INSCOPE                   }->{$token->{tag_name}}) {
4175                        !!!cp ('t87.2');
4176          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            !!!parse-error (type => 'not closed',
4177          if ($token->{tag_name} eq 'pre') {                            text => $self->{open_elements}->[-1]->[0]
4178            !!!next-token;                                ->manakai_local_name,
4179            if ($token->{type} eq 'character') {                            token => $token);
4180              $token->{data} =~ s/^\x0A//;  
4181              unless (length $token->{data}) {            pop @{$self->{open_elements}}
4182                !!!next-token;                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4183              }  
4184            }            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4185          } else {            ## Reprocess.
4186            !!!next-token;            next B;
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
4187          } else {          } else {
4188            ## has a p element in scope            my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4189            INSCOPE: for (reverse @{$self->{open_elements}}) {            my $tag_name = $token->{tag_name};
4190              if ($_->[1] eq 'p') {            if ($nsuri eq $SVG_NS) {
4191                !!!back-token;              $tag_name = {
4192                $token = {type => 'end tag', tag_name => 'p'};                 altglyph => 'altGlyph',
4193                return;                 altglyphdef => 'altGlyphDef',
4194              } elsif ({                 altglyphitem => 'altGlyphItem',
4195                        table => 1, caption => 1, td => 1, th => 1,                 animatecolor => 'animateColor',
4196                        button => 1, marquee => 1, object => 1, html => 1,                 animatemotion => 'animateMotion',
4197                       }->{$_->[1]}) {                 animatetransform => 'animateTransform',
4198                last INSCOPE;                 clippath => 'clipPath',
4199              }                 feblend => 'feBlend',
4200            } # INSCOPE                 fecolormatrix => 'feColorMatrix',
4201                               fecomponenttransfer => 'feComponentTransfer',
4202            !!!insert-element-t ($token->{tag_name}, $token->{attributes});                 fecomposite => 'feComposite',
4203            $self->{form_element} = $self->{open_elements}->[-1]->[0];                 feconvolvematrix => 'feConvolveMatrix',
4204            !!!next-token;                 fediffuselighting => 'feDiffuseLighting',
4205            return;                 fedisplacementmap => 'feDisplacementMap',
4206          }                 fedistantlight => 'feDistantLight',
4207        } elsif ($token->{tag_name} eq 'li') {                 feflood => 'feFlood',
4208          ## has a p element in scope                 fefunca => 'feFuncA',
4209          INSCOPE: for (reverse @{$self->{open_elements}}) {                 fefuncb => 'feFuncB',
4210            if ($_->[1] eq 'p') {                 fefuncg => 'feFuncG',
4211              !!!back-token;                 fefuncr => 'feFuncR',
4212              $token = {type => 'end tag', tag_name => 'p'};                 fegaussianblur => 'feGaussianBlur',
4213              return;                 feimage => 'feImage',
4214            } elsif ({                 femerge => 'feMerge',
4215                      table => 1, caption => 1, td => 1, th => 1,                 femergenode => 'feMergeNode',
4216                      button => 1, marquee => 1, object => 1, html => 1,                 femorphology => 'feMorphology',
4217                     }->{$_->[1]}) {                 feoffset => 'feOffset',
4218              last INSCOPE;                 fepointlight => 'fePointLight',
4219                   fespecularlighting => 'feSpecularLighting',
4220                   fespotlight => 'feSpotLight',
4221                   fetile => 'feTile',
4222                   feturbulence => 'feTurbulence',
4223                   foreignobject => 'foreignObject',
4224                   glyphref => 'glyphRef',
4225                   lineargradient => 'linearGradient',
4226                   radialgradient => 'radialGradient',
4227                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4228                   textpath => 'textPath',  
4229                }->{$tag_name} || $tag_name;
4230            }            }
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             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 '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_flag} = 'PLAINTEXT';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## 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});  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'a') {  
         AFE: for my $i (reverse 0..$#$active_formatting_elements) {  
           my $node = $active_formatting_elements->[$i];  
           if ($node->[1] eq 'a') {  
             !!!parse-error (type => 'in a:a');  
               
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'a'};  
             $formatting_end_tag->($token->{tag_name});  
               
             AFE2: for (reverse 0..$#$active_formatting_elements) {  
               if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$active_formatting_elements, $_, 1;  
                 last AFE2;  
               }  
             } # AFE2  
             OE: for (reverse 0..$#{$self->{open_elements}}) {  
               if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {  
                 splice @{$self->{open_elements}}, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4231    
4232          !!!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];  
4233    
4234          !!!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);  
4235    
4236          ## 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', ''];  
4237    
4238          !!!next-token;            if ($self->{self_closing}) {
4239          return;              pop @{$self->{open_elements}};
4240        } elsif ($token->{tag_name} eq 'marquee' or              !!!ack ('t87.3');
                $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', $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';  
         }  
   
         ## NOTE: There is an "as if <br>" code clone.  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           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}};  
4241            } else {            } else {
4242              push @tokens, {type => 'character',              !!!cp ('t87.4');
                            data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD  
             ## TODO: make this configurable  
4243            }            }
4244            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_flag} = 'RCDATA';  
         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};  
4245            !!!next-token;            !!!next-token;
4246              next B;
4247          }          }
4248          if (length $text) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4249            $el->manakai_append_text ($text);          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4250          }          !!!cp ('t87.5');
4251                    #
4252          $self->{content_model_flag} = 'PCDATA';        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4253                    !!!cp ('t87.6');
4254          if ($token->{type} eq 'end tag' and          !!!parse-error (type => 'not closed',
4255              $token->{tag_name} eq $tag_name) {                          text => $self->{open_elements}->[-1]->[0]
4256            ## Ignore the token                              ->manakai_local_name,
4257          } else {                          token => $token);
4258            !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4259          }          pop @{$self->{open_elements}}
4260          !!!next-token;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4261          return;  
4262        } elsif ({          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4263                  iframe => 1,          ## Reprocess.
4264                  noembed => 1,          next B;
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         $parse_rcdata->('CDATA', $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.  
4265        } else {        } else {
4266          $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;  
4267        }        }
4268      } 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]);  
             }  
           }  
4269    
4270            $self->{insertion_mode} = 'after body';      if ($self->{insertion_mode} & HEAD_IMS) {
4271            !!!next-token;        if ($token->{type} == CHARACTER_TOKEN) {
4272            return;          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
4273          } else {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4274            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.2');
4275            ## Ignore the token              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4276            !!!next-token;            } else {
4277            return;              !!!cp ('t88.1');
4278          }              ## Ignore the token.
4279        } elsif ($token->{tag_name} eq 'html') {              !!!next-token;
4280          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {              next B;
           ## 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]);  
4281            }            }
4282            $self->{insertion_mode} = 'after body';            unless (length $token->{data}) {
4283            ## reprocess              !!!cp ('t88');
4284            return;              !!!next-token;
4285          } else {              next B;
           !!!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;  
4286            }            }
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         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);  
4287          }          }
4288          $clear_up_to_marker->()  
4289            if {          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4290              button => 1, marquee => 1, object => 1,            !!!cp ('t89');
4291            }->{$token->{tag_name}};            ## As if <head>
4292          !!!next-token;            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4293          return;            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4294        } elsif ($token->{tag_name} eq 'form') {            push @{$self->{open_elements}},
4295          ## has an element in scope                [$self->{head_element}, $el_category->{head}];
4296          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
4297            my $node = $self->{open_elements}->[$_];            ## Reprocess in the "in head" insertion mode...
           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;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
4298            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
4299    
4300          undef $self->{form_element};            ## Reprocess in the "after head" insertion mode...
4301          !!!next-token;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4302          return;            !!!cp ('t90');
4303        } elsif ({            ## As if </noscript>
4304                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,            pop @{$self->{open_elements}};
4305                 }->{$token->{tag_name}}) {            !!!parse-error (type => 'in noscript:#text', token => $token);
4306          ## has an element in scope            
4307          my $i;            ## Reprocess in the "in head" insertion mode...
4308          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            ## As if </head>
4309            my $node = $self->{open_elements}->[$_];            pop @{$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;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ($token->{tag_name} eq 'br') {  
         !!!parse-error (type => 'unmatched end tag:br');  
4310    
4311          ## As if <br>            ## Reprocess in the "after head" insertion mode...
4312          $reconstruct_active_formatting_elements->($insert_to_current);          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4313                      !!!cp ('t91');
4314          my $el;            pop @{$self->{open_elements}};
         !!!create-element ($el, 'br');  
         $insert->($el);  
           
         ## 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];  
4315    
4316          ## Step 2            ## Reprocess in the "after head" insertion mode...
4317          S2: {          } else {
4318            if ($node->[1] eq $token->{tag_name}) {            !!!cp ('t92');
4319              ## 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;  
4320    
4321            ## "after head" insertion mode
4322            ## As if <body>
4323            !!!insert-element ('body',, $token);
4324            $self->{insertion_mode} = IN_BODY_IM;
4325            ## reprocess
4326            next B;
4327          } elsif ($token->{type} == START_TAG_TOKEN) {
4328            if ($token->{tag_name} eq 'head') {
4329              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4330                !!!cp ('t93');
4331                !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4332                $self->{open_elements}->[-1]->[0]->append_child
4333                    ($self->{head_element});
4334                push @{$self->{open_elements}},
4335                    [$self->{head_element}, $el_category->{head}];
4336                $self->{insertion_mode} = IN_HEAD_IM;
4337                !!!nack ('t93.1');
4338              !!!next-token;              !!!next-token;
4339              last S2;              next B;
4340              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4341                !!!cp ('t93.2');
4342                !!!parse-error (type => 'after head', text => 'head',
4343                                token => $token);
4344                ## Ignore the token
4345                !!!nack ('t93.3');
4346                !!!next-token;
4347                next B;
4348            } else {            } else {
4349              ## Step 3              !!!cp ('t95');
4350              if (not $formatting_category->{$node->[1]} and              !!!parse-error (type => 'in head:head',
4351                  #not $phrasing_category->{$node->[1]} and                              token => $token); # or in head noscript
4352                  ($special_category->{$node->[1]} or              ## Ignore the token
4353                   $scoping_category->{$node->[1]})) {              !!!nack ('t95.1');
4354                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!next-token;
4355                ## Ignore the token              next B;
               !!!next-token;  
               last S2;  
             }  
4356            }            }
4357                      } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4358            ## Step 4            !!!cp ('t96');
4359            $node_i--;            ## As if <head>
4360            $node = $self->{open_elements}->[$node_i];            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4361                        $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4362            ## Step 5;            push @{$self->{open_elements}},
4363            redo S2;                [$self->{head_element}, $el_category->{head}];
         } # S2  
         return;  
       }  
     }  
   }; # $in_body  
4364    
4365    B: {            $self->{insertion_mode} = IN_HEAD_IM;
4366      if ($phase eq 'main') {            ## Reprocess in the "in head" insertion mode...
4367        if ($token->{type} eq 'DOCTYPE') {          } else {
4368          !!!parse-error (type => 'in html:#DOCTYPE');            !!!cp ('t97');
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
 ## ISSUE: "aa<html>" is not a parse error.  
 ## ISSUE: "<html>" in fragment is not a parse error.  
         unless ($token->{first_start_tag}) {  
           !!!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});  
           }  
         }  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
              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]);  
4369          }          }
4370    
4371          ## Stop parsing              if ($token->{tag_name} eq 'base') {
4372          last B;                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4373                    !!!cp ('t98');
4374                    ## As if </noscript>
4375                    pop @{$self->{open_elements}};
4376                    !!!parse-error (type => 'in noscript', text => 'base',
4377                                    token => $token);
4378                  
4379                    $self->{insertion_mode} = IN_HEAD_IM;
4380                    ## Reprocess in the "in head" insertion mode...
4381                  } else {
4382                    !!!cp ('t99');
4383                  }
4384    
4385          ## ISSUE: There is an issue in the spec.                ## NOTE: There is a "as if in head" code clone.
4386        } else {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4387          if ($self->{insertion_mode} eq 'before head') {                  !!!cp ('t100');
4388            if ($token->{type} eq 'character') {                  !!!parse-error (type => 'after head',
4389              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                                  text => $token->{tag_name}, token => $token);
4390                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                  push @{$self->{open_elements}},
4391                unless (length $token->{data}) {                      [$self->{head_element}, $el_category->{head}];
4392                  !!!next-token;                } else {
4393                  redo B;                  !!!cp ('t101');
4394                }                }
4395              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4396              ## As if <head>                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4397              !!!create-element ($self->{head_element}, 'head');                pop @{$self->{open_elements}} # <head>
4398              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4399              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                !!!nack ('t101.1');
             $self->{insertion_mode} = 'in head';  
             ## reprocess  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};  
             !!!create-element ($self->{head_element}, 'head', $attr);  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             if ($token->{tag_name} eq 'head') {  
               !!!next-token;  
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ({  
                  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.  
4400                !!!next-token;                !!!next-token;
4401                redo B;                next B;
4402              }              } elsif ($token->{tag_name} eq 'link') {
           } else {  
             die "$0: $token->{type}: Unknown type";  
           }  
         } elsif ($self->{insertion_mode} eq 'in head' or  
                  $self->{insertion_mode} eq 'in head noscript' or  
                  $self->{insertion_mode} eq 'after head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({base => ($self->{insertion_mode} eq 'in head' or  
                           $self->{insertion_mode} eq 'after head'),  
                  link => 1, meta => 1}->{$token->{tag_name}}) {  
4403                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4404                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4405                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t102');
4406                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4407                                    text => $token->{tag_name}, token => $token);
4408                    push @{$self->{open_elements}},
4409                        [$self->{head_element}, $el_category->{head}];
4410                  } else {
4411                    !!!cp ('t103');
4412                }                }
4413                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4414                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4415                ## TODO: Extracting |charset| from |meta|.                pop @{$self->{open_elements}} # <head>
4416                pop @{$self->{open_elements}}                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4417                    if $self->{insertion_mode} eq 'after head';                !!!ack ('t103.1');
4418                !!!next-token;                !!!next-token;
4419                redo B;                next B;
4420              } elsif ($token->{tag_name} eq 'title' and              } elsif ($token->{tag_name} eq 'meta') {
                      $self->{insertion_mode} eq 'in head') {  
4421                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4422                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4423                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t104');
4424                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4425                                    text => $token->{tag_name}, token => $token);
4426                    push @{$self->{open_elements}},
4427                        [$self->{head_element}, $el_category->{head}];
4428                  } else {
4429                    !!!cp ('t105');
4430                  }
4431                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4432                  my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4433    
4434                  unless ($self->{confident}) {
4435                    if ($token->{attributes}->{charset}) {
4436                      !!!cp ('t106');
4437                      ## NOTE: Whether the encoding is supported or not is handled
4438                      ## in the {change_encoding} callback.
4439                      $self->{change_encoding}
4440                          ->($self, $token->{attributes}->{charset}->{value},
4441                             $token);
4442                      
4443                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4444                          ->set_user_data (manakai_has_reference =>
4445                                               $token->{attributes}->{charset}
4446                                                   ->{has_reference});
4447                    } elsif ($token->{attributes}->{content}) {
4448                      if ($token->{attributes}->{content}->{value}
4449                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4450                              [\x09-\x0D\x20]*=
4451                              [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4452                              ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {
4453                        !!!cp ('t107');
4454                        ## NOTE: Whether the encoding is supported or not is handled
4455                        ## in the {change_encoding} callback.
4456                        $self->{change_encoding}
4457                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4458                               $token);
4459                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4460                            ->set_user_data (manakai_has_reference =>
4461                                                 $token->{attributes}->{content}
4462                                                       ->{has_reference});
4463                      } else {
4464                        !!!cp ('t108');
4465                      }
4466                    }
4467                  } else {
4468                    if ($token->{attributes}->{charset}) {
4469                      !!!cp ('t109');
4470                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4471                          ->set_user_data (manakai_has_reference =>
4472                                               $token->{attributes}->{charset}
4473                                                   ->{has_reference});
4474                    }
4475                    if ($token->{attributes}->{content}) {
4476                      !!!cp ('t110');
4477                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4478                          ->set_user_data (manakai_has_reference =>
4479                                               $token->{attributes}->{content}
4480                                                   ->{has_reference});
4481                    }
4482                  }
4483    
4484                  pop @{$self->{open_elements}} # <head>
4485                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4486                  !!!ack ('t110.1');
4487                  !!!next-token;
4488                  next B;
4489                } elsif ($token->{tag_name} eq 'title') {
4490                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4491                    !!!cp ('t111');
4492                    ## As if </noscript>
4493                    pop @{$self->{open_elements}};
4494                    !!!parse-error (type => 'in noscript', text => 'title',
4495                                    token => $token);
4496                  
4497                    $self->{insertion_mode} = IN_HEAD_IM;
4498                    ## Reprocess in the "in head" insertion mode...
4499                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4500                    !!!cp ('t112');
4501                    !!!parse-error (type => 'after head',
4502                                    text => $token->{tag_name}, token => $token);
4503                    push @{$self->{open_elements}},
4504                        [$self->{head_element}, $el_category->{head}];
4505                  } else {
4506                    !!!cp ('t113');
4507                }                }
4508    
4509                  ## NOTE: There is a "as if in head" code clone.
4510                my $parent = defined $self->{head_element} ? $self->{head_element}                my $parent = defined $self->{head_element} ? $self->{head_element}
4511                    : $self->{open_elements}->[-1]->[0];                    : $self->{open_elements}->[-1]->[0];
4512                $parse_rcdata->('RCDATA', sub { $parent->append_child ($_[0]) });                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4513                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4514                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4515                redo B;                next B;
4516              } elsif ($token->{tag_name} eq 'style') {              } elsif ($token->{tag_name} eq 'style' or
4517                         $token->{tag_name} eq 'noframes') {
4518                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4519                ## insertion mode 'in head')                ## insertion mode IN_HEAD_IM)
4520                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4521                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4522                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t114');
4523                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4524                                    text => $token->{tag_name}, token => $token);
4525                    push @{$self->{open_elements}},
4526                        [$self->{head_element}, $el_category->{head}];
4527                  } else {
4528                    !!!cp ('t115');
4529                }                }
4530                $parse_rcdata->('CDATA', $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4531                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4532                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4533                redo B;                next B;
4534              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4535                if ($self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4536                    !!!cp ('t116');
4537                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4538                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4539                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4540                    !!!nack ('t116.1');
4541                  !!!next-token;                  !!!next-token;
4542                  redo B;                  next B;
4543                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4544                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4545                    !!!parse-error (type => 'in noscript', text => 'noscript',
4546                                    token => $token);
4547                  ## Ignore the token                  ## Ignore the token
4548                  redo B;                  !!!nack ('t117.1');
4549                    !!!next-token;
4550                    next B;
4551                } else {                } else {
4552                    !!!cp ('t118');
4553                  #                  #
4554                }                }
4555              } elsif ($token->{tag_name} eq 'head' and              } elsif ($token->{tag_name} eq 'script') {
4556                       $self->{insertion_mode} ne 'after head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4557                !!!parse-error (type => 'in head:head'); # or in head noscript                  !!!cp ('t119');
4558                ## Ignore the token                  ## As if </noscript>
4559                !!!next-token;                  pop @{$self->{open_elements}};
4560                redo B;                  !!!parse-error (type => 'in noscript', text => 'script',
4561              } elsif ($self->{insertion_mode} ne 'in head noscript' and                                  token => $token);
4562                       $token->{tag_name} eq 'script') {                
4563                if ($self->{insertion_mode} eq 'after head') {                  $self->{insertion_mode} = IN_HEAD_IM;
4564                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  ## Reprocess in the "in head" insertion mode...
4565                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4566                    !!!cp ('t120');
4567                    !!!parse-error (type => 'after head',
4568                                    text => $token->{tag_name}, token => $token);
4569                    push @{$self->{open_elements}},
4570                        [$self->{head_element}, $el_category->{head}];
4571                  } else {
4572                    !!!cp ('t121');
4573                }                }
4574    
4575                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4576                $script_start_tag->($insert_to_current);                $script_start_tag->();
4577                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4578                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4579                redo B;                next B;
4580              } 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  
4581                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4582                !!!insert-element ('frameset', $token->{attributes});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4583                $self->{insertion_mode} = 'in frameset';                  !!!cp ('t122');
4584                    ## As if </noscript>
4585                    pop @{$self->{open_elements}};
4586                    !!!parse-error (type => 'in noscript',
4587                                    text => $token->{tag_name}, token => $token);
4588                    
4589                    ## Reprocess in the "in head" insertion mode...
4590                    ## As if </head>
4591                    pop @{$self->{open_elements}};
4592                    
4593                    ## Reprocess in the "after head" insertion mode...
4594                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4595                    !!!cp ('t124');
4596                    pop @{$self->{open_elements}};
4597                    
4598                    ## Reprocess in the "after head" insertion mode...
4599                  } else {
4600                    !!!cp ('t125');
4601                  }
4602    
4603                  ## "after head" insertion mode
4604                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4605                  if ($token->{tag_name} eq 'body') {
4606                    !!!cp ('t126');
4607                    $self->{insertion_mode} = IN_BODY_IM;
4608                  } elsif ($token->{tag_name} eq 'frameset') {
4609                    !!!cp ('t127');
4610                    $self->{insertion_mode} = IN_FRAMESET_IM;
4611                  } else {
4612                    die "$0: tag name: $self->{tag_name}";
4613                  }
4614                  !!!nack ('t127.1');
4615                !!!next-token;                !!!next-token;
4616                redo B;                next B;
4617              } else {              } else {
4618                  !!!cp ('t128');
4619                #                #
4620              }              }
4621            } elsif ($token->{type} eq 'end tag') {  
4622              if ($self->{insertion_mode} eq 'in head' and              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4623                  $token->{tag_name} eq 'head') {                !!!cp ('t129');
4624                  ## As if </noscript>
4625                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4626                $self->{insertion_mode} = 'after head';                !!!parse-error (type => 'in noscript:/',
4627                !!!next-token;                                text => $token->{tag_name}, token => $token);
4628                redo B;                
4629              } elsif ($self->{insertion_mode} eq 'in head noscript' and                ## Reprocess in the "in head" insertion mode...
4630                  $token->{tag_name} eq 'noscript') {                ## As if </head>
4631                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               $self->{insertion_mode} = 'in head';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'in head' and  
                      {  
                       body => 1, html => 1,  
                       p => 1, br => 1,  
                      }->{$token->{tag_name}}) {  
               #  
             } elsif ($self->{insertion_mode} eq 'in head noscript' and  
                      {  
                       p => 1, br => 1,  
                      }->{$token->{tag_name}}) {  
               #  
             } elsif ($self->{insertion_mode} ne 'after head') {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
4632    
4633            ## As if </head> or </noscript> or <body>                ## Reprocess in the "after head" insertion mode...
4634            if ($self->{insertion_mode} eq 'in head') {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4635              pop @{$self->{open_elements}};                !!!cp ('t130');
4636              $self->{insertion_mode} = 'after head';                ## As if </head>
4637            } elsif ($self->{insertion_mode} eq 'in head noscript') {                pop @{$self->{open_elements}};
             pop @{$self->{open_elements}};  
             !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));  
             $self->{insertion_mode} = 'in head';  
           } else { # 'after head'  
             !!!insert-element ('body');  
             $self->{insertion_mode} = 'in body';  
           }  
           ## reprocess  
           redo B;  
   
           ## ISSUE: An issue in the spec.  
         } elsif ($self->{insertion_mode} eq 'in body') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There is a code clone of "character in body".  
             $reconstruct_active_formatting_elements->($insert_to_current);  
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
4638    
4639              !!!next-token;                ## Reprocess in the "after head" insertion mode...
4640              redo B;              } else {
4641            } elsif ($token->{type} eq 'comment') {                !!!cp ('t131');
             ## NOTE: There is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } else {  
             $in_body->($insert_to_current);  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There are "character in table" code clones.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
4642              }              }
4643    
4644              !!!parse-error (type => 'in table:#character');              ## "after head" insertion mode
4645                ## As if <body>
4646                !!!insert-element ('body',, $token);
4647                $self->{insertion_mode} = IN_BODY_IM;
4648                ## reprocess
4649                !!!ack-later;
4650                next B;
4651              } elsif ($token->{type} == END_TAG_TOKEN) {
4652                if ($token->{tag_name} eq 'head') {
4653                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4654                    !!!cp ('t132');
4655                    ## As if <head>
4656                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4657                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4658                    push @{$self->{open_elements}},
4659                        [$self->{head_element}, $el_category->{head}];
4660    
4661              ## As if in body, but insert into foster parent element                  ## Reprocess in the "in head" insertion mode...
4662              ## ISSUE: Spec says that "whenever a node would be inserted                  pop @{$self->{open_elements}};
4663              ## into the current node" while characters might not be                  $self->{insertion_mode} = AFTER_HEAD_IM;
4664              ## result in a new Text node.                  !!!next-token;
4665              $reconstruct_active_formatting_elements->($insert_to_foster);                  next B;
4666                              } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4667              if ({                  !!!cp ('t133');
4668                   table => 1, tbody => 1, tfoot => 1,                  ## As if </noscript>
4669                   thead => 1, tr => 1,                  pop @{$self->{open_elements}};
4670                  }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'in noscript:/',
4671                # MUST                                  text => 'head', token => $token);
4672                my $foster_parent_element;                  
4673                my $next_sibling;                  ## Reprocess in the "in head" insertion mode...
4674                my $prev_sibling;                  pop @{$self->{open_elements}};
4675                OE: for (reverse 0..$#{$self->{open_elements}}) {                  $self->{insertion_mode} = AFTER_HEAD_IM;
4676                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  !!!next-token;
4677                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  next B;
4678                    if (defined $parent and $parent->node_type == 1) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4679                      $foster_parent_element = $parent;                  !!!cp ('t134');
4680                      $next_sibling = $self->{open_elements}->[$_]->[0];                  pop @{$self->{open_elements}};
4681                      $prev_sibling = $next_sibling->previous_sibling;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4682                    } else {                  !!!next-token;
4683                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                  next B;
4684                      $prev_sibling = $foster_parent_element->last_child;                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4685                    }                  !!!cp ('t134.1');
4686                    last OE;                  !!!parse-error (type => 'unmatched end tag', text => 'head',
4687                  }                                  token => $token);
4688                } # OE                  ## Ignore the token
4689                $foster_parent_element = $self->{open_elements}->[0]->[0] and                  !!!next-token;
4690                $prev_sibling = $foster_parent_element->last_child                  next B;
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
4691                } else {                } else {
4692                  $foster_parent_element->insert_before                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
4693                }                }
4694              } else {              } elsif ($token->{tag_name} eq 'noscript') {
4695                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4696              }                  !!!cp ('t136');
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({  
                  caption => 1,  
                  colgroup => 1,  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4697                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4698                    $self->{insertion_mode} = IN_HEAD_IM;
4699                    !!!next-token;
4700                    next B;
4701                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4702                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4703                    !!!cp ('t137');
4704                    !!!parse-error (type => 'unmatched end tag',
4705                                    text => 'noscript', token => $token);
4706                    ## Ignore the token ## ISSUE: An issue in the spec.
4707                    !!!next-token;
4708                    next B;
4709                  } else {
4710                    !!!cp ('t138');
4711                    #
4712                }                }
   
               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;  
4713              } elsif ({              } elsif ({
4714                        col => 1,                        body => 1, html => 1,
                       td => 1, th => 1, tr => 1,  
4715                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4716                ## Clear back to table context                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
4717                while ($self->{open_elements}->[-1]->[1] ne 'table' and                    $self->{insertion_mode} == IN_HEAD_IM or
4718                       $self->{open_elements}->[-1]->[1] ne 'html') {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4719                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t140');
4720                  pop @{$self->{open_elements}};                  !!!parse-error (type => 'unmatched end tag',
4721                                    text => $token->{tag_name}, token => $token);
4722                    ## Ignore the token
4723                    !!!next-token;
4724                    next B;
4725                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4726                    !!!cp ('t140.1');
4727                    !!!parse-error (type => 'unmatched end tag',
4728                                    text => $token->{tag_name}, token => $token);
4729                    ## Ignore the token
4730                    !!!next-token;
4731                    next B;
4732                  } else {
4733                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4734                }                }
4735                } elsif ($token->{tag_name} eq 'p') {
4736                  !!!cp ('t142');
4737                  !!!parse-error (type => 'unmatched end tag',
4738                                  text => $token->{tag_name}, token => $token);
4739                  ## Ignore the token
4740                  !!!next-token;
4741                  next B;
4742                } elsif ($token->{tag_name} eq 'br') {
4743                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4744                    !!!cp ('t142.2');
4745                    ## (before head) as if <head>, (in head) as if </head>
4746                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4747                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4748                    $self->{insertion_mode} = AFTER_HEAD_IM;
4749      
4750                    ## Reprocess in the "after head" insertion mode...
4751                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4752                    !!!cp ('t143.2');
4753                    ## As if </head>
4754                    pop @{$self->{open_elements}};
4755                    $self->{insertion_mode} = AFTER_HEAD_IM;
4756      
4757                    ## Reprocess in the "after head" insertion mode...
4758                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4759                    !!!cp ('t143.3');
4760                    ## ISSUE: Two parse errors for <head><noscript></br>
4761                    !!!parse-error (type => 'unmatched end tag',
4762                                    text => 'br', token => $token);
4763                    ## As if </noscript>
4764                    pop @{$self->{open_elements}};
4765                    $self->{insertion_mode} = IN_HEAD_IM;
4766    
4767                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                  ## Reprocess in the "in head" insertion mode...
4768                $self->{insertion_mode} = $token->{tag_name} eq 'col'                  ## As if </head>
4769                  ? 'in column group' : 'in table body';                  pop @{$self->{open_elements}};
4770                ## reprocess                  $self->{insertion_mode} = AFTER_HEAD_IM;
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: There are code clones for this "table in table"  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4771    
4772                ## As if </table>                  ## Reprocess in the "after head" insertion mode...
4773                ## have a table element in table scope                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4774                my $i;                  !!!cp ('t143.4');
4775                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  #
4776                  my $node = $self->{open_elements}->[$_];                } else {
4777                  if ($node->[1] eq 'table') {                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    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;  
4778                }                }
4779    
4780                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
4781                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'unmatched end tag',
4782                }                                text => 'br', token => $token);
4783                  ## Ignore the token
4784                  !!!next-token;
4785                  next B;
4786                } else {
4787                  !!!cp ('t145');
4788                  !!!parse-error (type => 'unmatched end tag',
4789                                  text => $token->{tag_name}, token => $token);
4790                  ## Ignore the token
4791                  !!!next-token;
4792                  next B;
4793                }
4794    
4795                splice @{$self->{open_elements}}, $i;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4796                  !!!cp ('t146');
4797                  ## As if </noscript>
4798                  pop @{$self->{open_elements}};
4799                  !!!parse-error (type => 'in noscript:/',
4800                                  text => $token->{tag_name}, token => $token);
4801                  
4802                  ## Reprocess in the "in head" insertion mode...
4803                  ## As if </head>
4804                  pop @{$self->{open_elements}};
4805    
4806                $self->_reset_insertion_mode;                ## Reprocess in the "after head" insertion mode...
4807                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4808                  !!!cp ('t147');
4809                  ## As if </head>
4810                  pop @{$self->{open_elements}};
4811    
4812                ## reprocess                ## Reprocess in the "after head" insertion mode...
4813                redo B;              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4814    ## ISSUE: This case cannot be reached?
4815                  !!!cp ('t148');
4816                  !!!parse-error (type => 'unmatched end tag',
4817                                  text => $token->{tag_name}, token => $token);
4818                  ## Ignore the token ## ISSUE: An issue in the spec.
4819                  !!!next-token;
4820                  next B;
4821              } else {              } else {
4822                #                !!!cp ('t149');
4823              }              }
           } 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;  
               }  
4824    
4825                if ($self->{open_elements}->[-1]->[1] ne 'table') {              ## "after head" insertion mode
4826                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## As if <body>
4827                }              !!!insert-element ('body',, $token);
4828                $self->{insertion_mode} = IN_BODY_IM;
4829                ## reprocess
4830                next B;
4831          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4832            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4833              !!!cp ('t149.1');
4834    
4835              ## NOTE: As if <head>
4836              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4837              $self->{open_elements}->[-1]->[0]->append_child
4838                  ($self->{head_element});
4839              #push @{$self->{open_elements}},
4840              #    [$self->{head_element}, $el_category->{head}];
4841              #$self->{insertion_mode} = IN_HEAD_IM;
4842              ## NOTE: Reprocess.
4843    
4844              ## NOTE: As if </head>
4845              #pop @{$self->{open_elements}};
4846              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4847              ## NOTE: Reprocess.
4848              
4849              #
4850            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4851              !!!cp ('t149.2');
4852    
4853                splice @{$self->{open_elements}}, $i;            ## NOTE: As if </head>
4854              pop @{$self->{open_elements}};
4855              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4856              ## NOTE: Reprocess.
4857    
4858                $self->_reset_insertion_mode;            #
4859            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4860              !!!cp ('t149.3');
4861    
4862                !!!next-token;            !!!parse-error (type => 'in noscript:#eof', token => $token);
4863                redo B;  
4864              } elsif ({            ## As if </noscript>
4865                        body => 1, caption => 1, col => 1, colgroup => 1,            pop @{$self->{open_elements}};
4866                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,            #$self->{insertion_mode} = IN_HEAD_IM;
4867                        thead => 1, tr => 1,            ## NOTE: Reprocess.
4868                       }->{$token->{tag_name}}) {  
4869                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: As if </head>
4870                ## Ignore the token            pop @{$self->{open_elements}};
4871                !!!next-token;            #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
4872                redo B;            ## NOTE: Reprocess.
4873              } else {  
4874                #            #
4875              }          } else {
4876            } else {            !!!cp ('t149.4');
4877              #            #
4878            }          }
4879    
4880            ## NOTE: As if <body>
4881            !!!insert-element ('body',, $token);
4882            $self->{insertion_mode} = IN_BODY_IM;
4883            ## NOTE: Reprocess.
4884            next B;
4885          } else {
4886            die "$0: $token->{type}: Unknown token type";
4887          }
4888    
4889            !!!parse-error (type => 'in table:'.$token->{tag_name});            ## ISSUE: An issue in the spec.
4890            $in_body->($insert_to_foster);      } elsif ($self->{insertion_mode} & BODY_IMS) {
4891            redo B;            if ($token->{type} == CHARACTER_TOKEN) {
4892          } elsif ($self->{insertion_mode} eq 'in caption') {              !!!cp ('t150');
4893            if ($token->{type} eq 'character') {              ## NOTE: There is a code clone of "character in body".
             ## NOTE: This is a code clone of "character in body".  
4894              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
4895                            
4896              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4897    
4898              !!!next-token;              !!!next-token;
4899              redo B;              next B;
4900            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} == START_TAG_TOKEN) {
             ## NOTE: This is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
4901              if ({              if ({
4902                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
4903                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
4904                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
4905                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
4906                    ## have an element in table scope
4907                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
4908                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
4909                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
4910                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
4911                  my $node = $self->{open_elements}->[$_];  
4912                  if ($node->[1] eq 'caption') {                      ## Close the cell
4913                    $i = $_;                      !!!back-token; # <x>
4914                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
4915                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
4916                            table => 1, html => 1,                                line => $token->{line},
4917                           }->{$node->[1]}) {                                column => $token->{column}};
4918                    last INSCOPE;                      next B;
4919                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4920                        !!!cp ('t152');
4921                        ## ISSUE: This case can never be reached, maybe.
4922                        last;
4923                      }
4924                  }                  }
4925                } # INSCOPE  
4926                unless (defined $i) {                  !!!cp ('t153');
4927                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
4928                        text => $token->{tag_name}, token => $token);
4929                  ## Ignore the token                  ## Ignore the token
4930                    !!!nack ('t153.1');
4931                  !!!next-token;                  !!!next-token;
4932                  redo B;                  next B;
4933                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
4934                                  !!!parse-error (type => 'not closed', text => 'caption',
4935                ## generate implied end tags                                  token => $token);
4936                if ({                  
4937                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
4938                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
4939                     tbody => 1, tfoot=> 1, thead => 1,                  my $i;
4940                    }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: {
4941                  !!!back-token; # <?>                    for (reverse 0..$#{$self->{open_elements}}) {
4942                  $token = {type => 'end tag', tag_name => 'caption'};                      my $node = $self->{open_elements}->[$_];
4943                  !!!back-token;                      if ($node->[1] & CAPTION_EL) {
4944                  $token = {type => 'end tag',                        !!!cp ('t155');
4945                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        $i = $_;
4946                  redo B;                        last INSCOPE;
4947                }                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
4948                          !!!cp ('t156');
4949                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        last;
4950                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      }
4951                }                    }
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
4952    
4953                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
4954                      !!!parse-error (type => 'start tag not allowed',
4955                                      text => $token->{tag_name}, token => $token);
4956                      ## Ignore the token
4957                      !!!nack ('t157.1');
4958                      !!!next-token;
4959                      next B;
4960                    } # INSCOPE
4961                    
4962                    ## generate implied end tags
4963                    while ($self->{open_elements}->[-1]->[1]
4964                               & END_TAG_OPTIONAL_EL) {
4965                      !!!cp ('t158');
4966                      pop @{$self->{open_elements}};
4967                    }
4968    
4969                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
4970                redo B;                    !!!cp ('t159');
4971                      !!!parse-error (type => 'not closed',
4972                                      text => $self->{open_elements}->[-1]->[0]
4973                                          ->manakai_local_name,
4974                                      token => $token);
4975                    } else {
4976                      !!!cp ('t160');
4977                    }
4978                    
4979                    splice @{$self->{open_elements}}, $i;
4980                    
4981                    $clear_up_to_marker->();
4982                    
4983                    $self->{insertion_mode} = IN_TABLE_IM;
4984                    
4985                    ## reprocess
4986                    !!!ack-later;
4987                    next B;
4988                  } else {
4989                    !!!cp ('t161');
4990                    #
4991                  }
4992              } else {              } else {
4993                  !!!cp ('t162');
4994                #                #
4995              }              }
4996            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
4997              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
4998                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
4999                my $i;                  ## have an element in table scope
5000                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
5001                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5002                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5003                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5004                    last INSCOPE;                      !!!cp ('t163');
5005                  } elsif ({                      $i = $_;
5006                            table => 1, html => 1,                      last INSCOPE;
5007                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5008                    last INSCOPE;                      !!!cp ('t164');
5009                        last INSCOPE;
5010                      }
5011                    } # INSCOPE
5012                      unless (defined $i) {
5013                        !!!cp ('t165');
5014                        !!!parse-error (type => 'unmatched end tag',
5015                                        text => $token->{tag_name},
5016                                        token => $token);
5017                        ## Ignore the token
5018                        !!!next-token;
5019                        next B;
5020                      }
5021                    
5022                    ## generate implied end tags
5023                    while ($self->{open_elements}->[-1]->[1]
5024                               & END_TAG_OPTIONAL_EL) {
5025                      !!!cp ('t166');
5026                      pop @{$self->{open_elements}};
5027                  }                  }
5028                } # INSCOPE  
5029                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5030                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5031                      !!!cp ('t167');
5032                      !!!parse-error (type => 'not closed',
5033                                      text => $self->{open_elements}->[-1]->[0]
5034                                          ->manakai_local_name,
5035                                      token => $token);
5036                    } else {
5037                      !!!cp ('t168');
5038                    }
5039                    
5040                    splice @{$self->{open_elements}}, $i;
5041                    
5042                    $clear_up_to_marker->();
5043                    
5044                    $self->{insertion_mode} = IN_ROW_IM;
5045                    
5046                    !!!next-token;
5047                    next B;
5048                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5049                    !!!cp ('t169');
5050                    !!!parse-error (type => 'unmatched end tag',
5051                                    text => $token->{tag_name}, token => $token);
5052                  ## Ignore the token                  ## Ignore the token
5053                  !!!next-token;                  !!!next-token;
5054                  redo B;                  next B;
5055                }                } else {
5056                                  !!!cp ('t170');
5057                ## 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;  
5058                }                }
5059                } elsif ($token->{tag_name} eq 'caption') {
5060                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5061                    ## have a table element in table scope
5062                    my $i;
5063                    INSCOPE: {
5064                      for (reverse 0..$#{$self->{open_elements}}) {
5065                        my $node = $self->{open_elements}->[$_];
5066                        if ($node->[1] & CAPTION_EL) {
5067                          !!!cp ('t171');
5068                          $i = $_;
5069                          last INSCOPE;
5070                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5071                          !!!cp ('t172');
5072                          last;
5073                        }
5074                      }
5075    
5076                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5077                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5078                                      text => $token->{tag_name}, token => $token);
5079                      ## Ignore the token
5080                      !!!next-token;
5081                      next B;
5082                    } # INSCOPE
5083                    
5084                    ## generate implied end tags
5085                    while ($self->{open_elements}->[-1]->[1]
5086                               & END_TAG_OPTIONAL_EL) {
5087                      !!!cp ('t174');
5088                      pop @{$self->{open_elements}};
5089                    }
5090                    
5091                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5092                      !!!cp ('t175');
5093                      !!!parse-error (type => 'not closed',
5094                                      text => $self->{open_elements}->[-1]->[0]
5095                                          ->manakai_local_name,
5096                                      token => $token);
5097                    } else {
5098                      !!!cp ('t176');
5099                    }
5100                    
5101                    splice @{$self->{open_elements}}, $i;
5102                    
5103                    $clear_up_to_marker->();
5104                    
5105                    $self->{insertion_mode} = IN_TABLE_IM;
5106                    
5107                    !!!next-token;
5108                    next B;
5109                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5110                    !!!cp ('t177');
5111                    !!!parse-error (type => 'unmatched end tag',
5112                                    text => $token->{tag_name}, token => $token);
5113                    ## Ignore the token
5114                    !!!next-token;
5115                    next B;
5116                  } else {
5117                    !!!cp ('t178');
5118                    #
5119                }                }
5120                } elsif ({
5121                          table => 1, tbody => 1, tfoot => 1,
5122                          thead => 1, tr => 1,
5123                         }->{$token->{tag_name}} and
5124                         $self->{insertion_mode} == IN_CELL_IM) {
5125                  ## have an element in table scope
5126                  my $i;
5127                  my $tn;
5128                  INSCOPE: {
5129                    for (reverse 0..$#{$self->{open_elements}}) {
5130                      my $node = $self->{open_elements}->[$_];
5131                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5132                        !!!cp ('t179');
5133                        $i = $_;
5134    
5135                        ## Close the cell
5136                        !!!back-token; # </x>
5137                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5138                                  line => $token->{line},
5139                                  column => $token->{column}};
5140                        next B;
5141                      } elsif ($node->[1] & TABLE_CELL_EL) {
5142                        !!!cp ('t180');
5143                        $tn = $node->[0]->manakai_local_name;
5144                        ## NOTE: There is exactly one |td| or |th| element
5145                        ## in scope in the stack of open elements by definition.
5146                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5147                        ## ISSUE: Can this be reached?
5148                        !!!cp ('t181');
5149                        last;
5150                      }
5151                    }
5152    
5153                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5154                    !!!parse-error (type => 'unmatched end tag',
5155                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5156                    ## Ignore the token
5157                $self->{insertion_mode} = 'in table';                  !!!next-token;
5158                    next B;
5159                !!!next-token;                } # INSCOPE
5160                redo B;              } elsif ($token->{tag_name} eq 'table' and
5161              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5162                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5163                                  token => $token);
5164    
5165                ## As if </caption>                ## As if </caption>
5166                ## have a table element in table scope                ## have a table element in table scope
5167                my $i;                my $i;
5168                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5169                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5170                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5171                      !!!cp ('t184');
5172                    $i = $_;                    $i = $_;
5173                    last INSCOPE;                    last INSCOPE;
5174                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5175                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5176                    last INSCOPE;                    last INSCOPE;
5177                  }                  }
5178                } # INSCOPE                } # INSCOPE
5179                unless (defined $i) {                unless (defined $i) {
5180                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5181                    !!!parse-error (type => 'unmatched end tag',
5182                                    text => 'caption', token => $token);
5183                  ## Ignore the token                  ## Ignore the token
5184                  !!!next-token;                  !!!next-token;
5185                  redo B;                  next B;
5186                }                }
5187                                
5188                ## generate implied end tags                ## generate implied end tags
5189                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5190                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5191                     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;  
5192                }                }
5193    
5194                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5195                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5196                    !!!parse-error (type => 'not closed',
5197                                    text => $self->{open_elements}->[-1]->[0]
5198                                        ->manakai_local_name,
5199                                    token => $token);
5200                  } else {
5201                    !!!cp ('t189');
5202                }                }
5203    
5204                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5205    
5206                $clear_up_to_marker->();                $clear_up_to_marker->();
5207    
5208                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5209    
5210                ## reprocess                ## reprocess
5211                redo B;                next B;
5212              } elsif ({              } elsif ({
5213                        body => 1, col => 1, colgroup => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
                       html => 1, tbody => 1, td => 1, tfoot => 1,  
                       th => 1, thead => 1, tr => 1,  
5214                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5215                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5216                ## Ignore the token                  !!!cp ('t190');
5217                redo B;                  !!!parse-error (type => 'unmatched end tag',
5218              } else {                                  text => $token->{tag_name}, token => $token);
               #  
             }  
           } else {  
             #  
           }  
                 
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in column group') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'col') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'colgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html') {  
                 !!!parse-error (type => 'unmatched end tag:colgroup');  
5219                  ## Ignore the token                  ## Ignore the token
5220                  !!!next-token;                  !!!next-token;
5221                  redo B;                  next B;
5222                } else {                } else {
5223                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
5224                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5225                }                }
5226              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5227                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
5228                          thead => 1, tr => 1,
5229                         }->{$token->{tag_name}} and
5230                         $self->{insertion_mode} == IN_CAPTION_IM) {
5231                  !!!cp ('t192');
5232                  !!!parse-error (type => 'unmatched end tag',
5233                                  text => $token->{tag_name}, token => $token);
5234                ## Ignore the token                ## Ignore the token
5235                !!!next-token;                !!!next-token;
5236                redo B;                next B;
5237              } else {              } else {
5238                #                !!!cp ('t193');
5239                  #
5240              }              }
5241            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5242              #          for my $entry (@{$self->{open_elements}}) {
5243              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5244                !!!cp ('t75');
5245                !!!parse-error (type => 'in body:#eof', token => $token);
5246                last;
5247            }            }
5248            }
5249    
5250            ## As if </colgroup>          ## Stop parsing.
5251            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
5252              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
5253              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5254          }
5255    
5256          $insert = $insert_to_current;
5257          #
5258        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5259          if ($token->{type} == CHARACTER_TOKEN) {
5260            if (not $open_tables->[-1]->[1] and # tainted
5261                $token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5262              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5263                  
5264              unless (length $token->{data}) {
5265                !!!cp ('t194');
5266              !!!next-token;              !!!next-token;
5267              redo B;              next B;
5268            } else {            } else {
5269              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
5270            }            }
5271          } 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;  
               }  
             }  
5272    
5273              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5274    
5275              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5276              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
5277              ## into the current node" while characters might not be              ## into the current node" while characters might not be
5278              ## result in a new Text node.              ## result in a new Text node.
5279              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5280                
5281              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]}) {  
5282                # MUST                # MUST
5283                my $foster_parent_element;                my $foster_parent_element;
5284                my $next_sibling;                my $next_sibling;
5285                my $prev_sibling;                my $prev_sibling;
5286                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5287                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5288                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5289                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5290                        !!!cp ('t196');
5291                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5292                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5293                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5294                    } else {                    } else {
5295                        !!!cp ('t197');
5296                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5297                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5298                    }                    }
# Line 4103  sub _tree_construction_main ($) { Line 5304  sub _tree_construction_main ($) {
5304                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5305                if (defined $prev_sibling and                if (defined $prev_sibling and
5306                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5307                    !!!cp ('t198');
5308                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5309                } else {                } else {
5310                    !!!cp ('t199');
5311                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5312                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5313                     $next_sibling);                     $next_sibling);
5314                }                }
5315              } else {            $open_tables->[-1]->[1] = 1; # tainted
5316                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5317              !!!cp ('t200');
5318              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5319            }
5320                
5321            !!!next-token;
5322            next B;
5323          } elsif ($token->{type} == START_TAG_TOKEN) {
5324            if ({
5325                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5326                 th => 1, td => 1,
5327                }->{$token->{tag_name}}) {
5328              if ($self->{insertion_mode} == IN_TABLE_IM) {
5329                ## Clear back to table context
5330                while (not ($self->{open_elements}->[-1]->[1]
5331                                & TABLE_SCOPING_EL)) {
5332                  !!!cp ('t201');
5333                  pop @{$self->{open_elements}};
5334              }              }
5335                            
5336              !!!next-token;              !!!insert-element ('tbody',, $token);
5337              redo B;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5338            } elsif ($token->{type} eq 'comment') {              ## reprocess in the "in table body" insertion mode...
5339              ## Copied from 'in table'            }
5340              my $comment = $self->{document}->create_comment ($token->{data});            
5341              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5342              !!!next-token;              unless ($token->{tag_name} eq 'tr') {
5343              redo B;                !!!cp ('t202');
5344            } elsif ($token->{type} eq 'start tag') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
5345              if ({              }
5346                   tr => 1,                  
5347                   th => 1, td => 1,              ## Clear back to table body context
5348                  }->{$token->{tag_name}}) {              while (not ($self->{open_elements}->[-1]->[1]
5349                unless ($token->{tag_name} eq 'tr') {                              & TABLE_ROWS_SCOPING_EL)) {
5350                  !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t203');
5351                  ## ISSUE: Can this case be reached?
5352                  pop @{$self->{open_elements}};
5353                }
5354                    
5355                    $self->{insertion_mode} = IN_ROW_IM;
5356                    if ($token->{tag_name} eq 'tr') {
5357                      !!!cp ('t204');
5358                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5359                      !!!nack ('t204');
5360                      !!!next-token;
5361                      next B;
5362                    } else {
5363                      !!!cp ('t205');
5364                      !!!insert-element ('tr',, $token);
5365                      ## reprocess in the "in row" insertion mode
5366                    }
5367                  } else {
5368                    !!!cp ('t206');
5369                }                }
5370    
5371                ## Clear back to table body context                ## Clear back to table row context
5372                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5373                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5374                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5375                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5376                }                }
5377                                
5378                $self->{insertion_mode} = 'in row';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5379                if ($token->{tag_name} eq 'tr') {                $self->{insertion_mode} = IN_CELL_IM;
5380                  !!!insert-element ($token->{tag_name}, $token->{attributes});  
5381                  !!!next-token;                push @$active_formatting_elements, ['#marker', ''];
5382                } else {                
5383                  !!!insert-element ('tr');                !!!nack ('t207.1');
5384                  ## reprocess                !!!next-token;
5385                }                next B;
               redo B;  
5386              } elsif ({              } elsif ({
5387                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5388                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5389                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5390                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5391                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
5392                my $i;                  ## As if </tr>
5393                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
5394                  my $node = $self->{open_elements}->[$_];                  my $i;
5395                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5396                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
5397                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
5398                    $i = $_;                      !!!cp ('t208');
5399                    last INSCOPE;                      $i = $_;
5400                  } elsif ({                      last INSCOPE;
5401                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5402                           }->{$node->[1]}) {                      !!!cp ('t209');
5403                    last INSCOPE;                      last INSCOPE;
5404                      }
5405                    } # INSCOPE
5406                    unless (defined $i) {
5407                      !!!cp ('t210');
5408    ## TODO: This type is wrong.
5409                      !!!parse-error (type => 'unmacthed end tag',
5410                                      text => $token->{tag_name}, token => $token);
5411                      ## Ignore the token
5412                      !!!nack ('t210.1');
5413                      !!!next-token;
5414                      next B;
5415                    }
5416                    
5417                    ## Clear back to table row context
5418                    while (not ($self->{open_elements}->[-1]->[1]
5419                                    & TABLE_ROW_SCOPING_EL)) {
5420                      !!!cp ('t211');
5421                      ## ISSUE: Can this case be reached?
5422                      pop @{$self->{open_elements}};
5423                    }
5424                    
5425                    pop @{$self->{open_elements}}; # tr
5426                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5427                    if ($token->{tag_name} eq 'tr') {
5428                      !!!cp ('t212');
5429                      ## reprocess
5430                      !!!ack-later;
5431                      next B;
5432                    } else {
5433                      !!!cp ('t213');
5434                      ## reprocess in the "in table body" insertion mode...
5435                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5436                }                }
5437    
5438                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5439                while (not {                  ## have an element in table scope
5440                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5441                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5442                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5443                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5444                        !!!cp ('t214');
5445                        $i = $_;
5446                        last INSCOPE;
5447                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5448                        !!!cp ('t215');
5449                        last INSCOPE;
5450                      }
5451                    } # INSCOPE
5452                    unless (defined $i) {
5453                      !!!cp ('t216');
5454    ## TODO: This erorr type is wrong.
5455                      !!!parse-error (type => 'unmatched end tag',
5456                                      text => $token->{tag_name}, token => $token);
5457                      ## Ignore the token
5458                      !!!nack ('t216.1');
5459                      !!!next-token;
5460                      next B;
5461                    }
5462    
5463                    ## Clear back to table body context
5464                    while (not ($self->{open_elements}->[-1]->[1]
5465                                    & TABLE_ROWS_SCOPING_EL)) {
5466                      !!!cp ('t217');
5467                      ## ISSUE: Can this state be reached?
5468                      pop @{$self->{open_elements}};
5469                    }
5470                    
5471                    ## As if <{current node}>
5472                    ## have an element in table scope
5473                    ## true by definition
5474                    
5475                    ## Clear back to table body context
5476                    ## nop by definition
5477                    
5478                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5479                    $self->{insertion_mode} = IN_TABLE_IM;
5480                    ## reprocess in "in table" insertion mode...
5481                  } else {
5482                    !!!cp ('t218');
5483                }                }
5484    
5485                ## As if <{current node}>                if ($token->{tag_name} eq 'col') {
5486                ## have an element in table scope                  ## Clear back to table context
5487                ## true by definition                  while (not ($self->{open_elements}->[-1]->[1]
5488                                    & TABLE_SCOPING_EL)) {
5489                ## Clear back to table body context                    !!!cp ('t219');
5490                ## nop by definition                    ## ISSUE: Can this state be reached?
5491                      pop @{$self->{open_elements}};
5492                pop @{$self->{open_elements}};                  }
5493                $self->{insertion_mode} = 'in table';                  
5494                ## reprocess                  !!!insert-element ('colgroup',, $token);
5495                redo B;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5496                    ## reprocess
5497                    !!!ack-later;
5498                    next B;
5499                  } elsif ({
5500                            caption => 1,
5501                            colgroup => 1,
5502                            tbody => 1, tfoot => 1, thead => 1,
5503                           }->{$token->{tag_name}}) {
5504                    ## Clear back to table context
5505                    while (not ($self->{open_elements}->[-1]->[1]
5506                                    & TABLE_SCOPING_EL)) {
5507                      !!!cp ('t220');
5508                      ## ISSUE: Can this state be reached?
5509                      pop @{$self->{open_elements}};
5510                    }
5511                    
5512                    push @$active_formatting_elements, ['#marker', '']
5513                        if $token->{tag_name} eq 'caption';
5514                    
5515                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5516                    $self->{insertion_mode} = {
5517                                               caption => IN_CAPTION_IM,
5518                                               colgroup => IN_COLUMN_GROUP_IM,
5519                                               tbody => IN_TABLE_BODY_IM,
5520                                               tfoot => IN_TABLE_BODY_IM,
5521                                               thead => IN_TABLE_BODY_IM,
5522                                              }->{$token->{tag_name}};
5523                    !!!next-token;
5524                    !!!nack ('t220.1');
5525                    next B;
5526                  } else {
5527                    die "$0: in table: <>: $token->{tag_name}";
5528                  }
5529              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5530                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5531                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5532                                      ->manakai_local_name,
5533                                  token => $token);
5534    
5535                ## As if </table>                ## As if </table>
5536                ## have a table element in table scope                ## have a table element in table scope
5537                my $i;                my $i;
5538                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5539                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5540                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5541                      !!!cp ('t221');
5542                    $i = $_;                    $i = $_;
5543                    last INSCOPE;                    last INSCOPE;
5544                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5545                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5546                    last INSCOPE;                    last INSCOPE;
5547                  }                  }
5548                } # INSCOPE                } # INSCOPE
5549                unless (defined $i) {                unless (defined $i) {
5550                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5551    ## TODO: The following is wrong, maybe.
5552                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5553                                    token => $token);
5554                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5555                    !!!nack ('t223.1');
5556                  !!!next-token;                  !!!next-token;
5557                  redo B;                  next B;
5558                }                }
5559                                
5560    ## TODO: Followings are removed from the latest spec.
5561                ## generate implied end tags                ## generate implied end tags
5562                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5563                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5564                     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;  
5565                }                }
5566    
5567                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5568                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5569                    ## NOTE: |<table><tr><table>|
5570                    !!!parse-error (type => 'not closed',
5571                                    text => $self->{open_elements}->[-1]->[0]
5572                                        ->manakai_local_name,
5573                                    token => $token);
5574                  } else {
5575                    !!!cp ('t226');
5576                }                }
5577    
5578                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5579                  pop @{$open_tables};
5580    
5581                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5582    
5583                ## reprocess            ## reprocess
5584                redo B;            !!!ack-later;
5585              } else {            next B;
5586                #          } elsif ($token->{tag_name} eq 'style') {
5587              }            if (not $open_tables->[-1]->[1]) { # tainted
5588            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5589              if ({              ## NOTE: This is a "as if in head" code clone.
5590                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5591                  }->{$token->{tag_name}}) {              next B;
5592                ## have an element in table scope            } else {
5593                my $i;              !!!cp ('t227.7');
5594                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              #
5595                  my $node = $self->{open_elements}->[$_];            }
5596                  if ($node->[1] eq $token->{tag_name}) {          } elsif ($token->{tag_name} eq 'script') {
5597                    $i = $_;            if (not $open_tables->[-1]->[1]) { # tainted
5598                    last INSCOPE;              !!!cp ('t227.6');
5599                  } elsif ({              ## NOTE: This is a "as if in head" code clone.
5600                            table => 1, html => 1,              $script_start_tag->();
5601                           }->{$node->[1]}) {              next B;
5602                    last INSCOPE;            } else {
5603                  }              !!!cp ('t227.5');
5604                } # INSCOPE              #
5605                unless (defined $i) {            }
5606                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'input') {
5607                  ## Ignore the token            if (not $open_tables->[-1]->[1]) { # tainted
5608                  !!!next-token;              if ($token->{attributes}->{type}) { ## TODO: case
5609                  redo B;                my $type = lc $token->{attributes}->{type}->{value};
5610                }                if ($type eq 'hidden') {
5611                    !!!cp ('t227.3');
5612                    !!!parse-error (type => 'in table',
5613                                    text => $token->{tag_name}, token => $token);
5614    
5615                ## 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}};  
               }  
5616    
5617                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;  
               }  
5618    
               ## 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]);  
5619                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
5620    
5621                ## Clear back to table body context                  !!!next-token;
5622                ## nop by definition                  !!!ack ('t227.2.1');
5623                    next B;
5624                pop @{$self->{open_elements}};                } else {
5625                $self->{insertion_mode} = 'in table';                  !!!cp ('t227.2');
5626                ## reprocess                  #
5627                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;  
5628              } else {              } else {
5629                  !!!cp ('t227.1');
5630                #                #
5631              }              }
5632            } else {            } else {
5633                !!!cp ('t227.4');
5634              #              #
5635            }            }
5636                      } else {
5637            ## As if in table            !!!cp ('t227');
5638            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5639            $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;  
               }  
             }  
5640    
5641              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table', text => $token->{tag_name},
5642                            token => $token);
5643    
5644              ## As if in body, but insert into foster parent element          $insert = $insert_to_foster;
5645              ## ISSUE: Spec says that "whenever a node would be inserted          #
5646              ## into the current node" while characters might not be        } elsif ($token->{type} == END_TAG_TOKEN) {
5647              ## result in a new Text node.              if ($token->{tag_name} eq 'tr' and
5648              $reconstruct_active_formatting_elements->($insert_to_foster);                  $self->{insertion_mode} == IN_ROW_IM) {
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
   
               push @$active_formatting_elements, ['#marker', ''];  
                 
               !!!next-token;  
               redo B;  
             } elsif ({  
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
5649                ## have an element in table scope                ## have an element in table scope
5650                my $i;                my $i;
5651                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5652                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5653                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5654                      !!!cp ('t228');
5655                    $i = $_;                    $i = $_;
5656                    last INSCOPE;                    last INSCOPE;
5657                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5658                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5659                    last INSCOPE;                    last INSCOPE;
5660                  }                  }
5661                } # INSCOPE                } # INSCOPE
5662                unless (defined $i) {                unless (defined $i) {
5663                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
5664                    !!!parse-error (type => 'unmatched end tag',
5665                                    text => $token->{tag_name}, token => $token);
5666                  ## Ignore the token                  ## Ignore the token
5667                    !!!nack ('t230.1');
5668                  !!!next-token;                  !!!next-token;
5669                  redo B;                  next B;
5670                  } else {
5671                    !!!cp ('t232');
5672                }                }
5673    
5674                ## Clear back to table row context                ## Clear back to table row context
5675                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5676                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5677                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
5678                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
5679                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5680                }                }
5681    
5682                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5683                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5684                ## reprocess                !!!next-token;
5685                redo B;                !!!nack ('t231.1');
5686                  next B;
5687              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5688                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
5689                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
5690                    ## have an element in table scope
5691                ## As if </table>                  my $i;
5692                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5693                my $i;                    my $node = $self->{open_elements}->[$_];
5694                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
5695                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
5696                  if ($node->[1] eq 'table') {                      $i = $_;
5697                    $i = $_;                      last INSCOPE;
5698                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5699                  } elsif ({                      !!!cp ('t234');
5700                            table => 1, html => 1,                      last INSCOPE;
5701                           }->{$node->[1]}) {                    }
5702                    last INSCOPE;                  } # INSCOPE
5703                    unless (defined $i) {
5704                      !!!cp ('t235');
5705    ## TODO: The following is wrong.
5706                      !!!parse-error (type => 'unmatched end tag',
5707                                      text => $token->{type}, token => $token);
5708                      ## Ignore the token
5709                      !!!nack ('t236.1');
5710                      !!!next-token;
5711                      next B;
5712                  }                  }
5713                } # INSCOPE                  
5714                unless (defined $i) {                  ## Clear back to table row context
5715                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
5716                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
5717                  !!!next-token;                    !!!cp ('t236');
5718                  redo B;  ## ISSUE: Can this state be reached?
5719                }                    pop @{$self->{open_elements}};
                 
               ## 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 'tr') {  
               ## 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;  
5720                  }                  }
5721                } # INSCOPE                  
5722                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
5723                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5724                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
5725                  !!!next-token;                }
5726                  redo B;  
5727                }                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5728                    ## have an element in table scope
5729                ## Clear back to table row context                  my $i;
5730                while (not {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5731                  tr => 1, html => 1,                    my $node = $self->{open_elements}->[$_];
5732                }->{$self->{open_elements}->[-1]->[1]}) {                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
5733                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      !!!cp ('t237');
5734                        $i = $_;
5735                        last INSCOPE;
5736                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5737                        !!!cp ('t238');
5738                        last INSCOPE;
5739                      }
5740                    } # INSCOPE
5741                    unless (defined $i) {
5742                      !!!cp ('t239');
5743                      !!!parse-error (type => 'unmatched end tag',
5744                                      text => $token->{tag_name}, token => $token);
5745                      ## Ignore the token
5746                      !!!nack ('t239.1');
5747                      !!!next-token;
5748                      next B;
5749                    }
5750                    
5751                    ## Clear back to table body context
5752                    while (not ($self->{open_elements}->[-1]->[1]
5753                                    & TABLE_ROWS_SCOPING_EL)) {
5754                      !!!cp ('t240');
5755                      pop @{$self->{open_elements}};
5756                    }
5757                    
5758                    ## As if <{current node}>
5759                    ## have an element in table scope
5760                    ## true by definition
5761                    
5762                    ## Clear back to table body context
5763                    ## nop by definition
5764                    
5765                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5766                    $self->{insertion_mode} = IN_TABLE_IM;
5767                    ## reprocess in the "in table" insertion mode...
5768                }                }
5769    
5770                pop @{$self->{open_elements}}; # tr                ## NOTE: </table> in the "in table" insertion mode.
5771                $self->{insertion_mode} = 'in table body';                ## When you edit the code fragment below, please ensure that
5772                !!!next-token;                ## the code for <table> in the "in table" insertion mode
5773                redo B;                ## is synced with it.
5774              } elsif ($token->{tag_name} eq 'table') {  
5775                ## As if </tr>                ## have a table element in table scope
               ## have an element in table scope  
5776                my $i;                my $i;
5777                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5778                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5779                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_EL) {
5780                      !!!cp ('t241');
5781                    $i = $_;                    $i = $_;
5782                    last INSCOPE;                    last INSCOPE;
5783                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5784                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
5785                    last INSCOPE;                    last INSCOPE;
5786                  }                  }
5787                } # INSCOPE                } # INSCOPE
5788                unless (defined $i) {                unless (defined $i) {
5789                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t243');
5790                    !!!parse-error (type => 'unmatched end tag',
5791                                    text => $token->{tag_name}, token => $token);
5792                  ## Ignore the token                  ## Ignore the token
5793                    !!!nack ('t243.1');
5794                  !!!next-token;                  !!!next-token;
5795                  redo B;                  next B;
               }  
   
               ## 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}};  
5796                }                }
5797                    
5798                pop @{$self->{open_elements}}; # tr                splice @{$self->{open_elements}}, $i;
5799                $self->{insertion_mode} = 'in table body';                pop @{$open_tables};
5800                ## reprocess                
5801                redo B;                $self->_reset_insertion_mode;
5802                  
5803                  !!!next-token;
5804                  next B;
5805              } elsif ({              } elsif ({
5806                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5807                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}} and
5808                ## have an element in table scope                       $self->{insertion_mode} & ROW_IMS) {
5809                my $i;                if ($self->{insertion_mode} == IN_ROW_IM) {
5810                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
5811                  my $node = $self->{open_elements}->[$_];                  my $i;
5812                  if ($node->[1] eq $token->{tag_name}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5813                    $i = $_;                    my $node = $self->{open_elements}->[$_];
5814                    last INSCOPE;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5815                  } elsif ({                      !!!cp ('t247');
5816                            table => 1, html => 1,                      $i = $_;
5817                           }->{$node->[1]}) {                      last INSCOPE;
5818                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5819                        !!!cp ('t248');
5820                        last INSCOPE;
5821                      }
5822                    } # INSCOPE
5823                      unless (defined $i) {
5824                        !!!cp ('t249');
5825                        !!!parse-error (type => 'unmatched end tag',
5826                                        text => $token->{tag_name}, token => $token);
5827                        ## Ignore the token
5828                        !!!nack ('t249.1');
5829                        !!!next-token;
5830                        next B;
5831                      }
5832                    
5833                    ## As if </tr>
5834                    ## have an element in table scope
5835                    my $i;
5836                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5837                      my $node = $self->{open_elements}->[$_];
5838                      if ($node->[1] & TABLE_ROW_EL) {
5839                        !!!cp ('t250');
5840                        $i = $_;
5841                        last INSCOPE;
5842                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5843                        !!!cp ('t251');
5844                        last INSCOPE;
5845                      }
5846                    } # INSCOPE
5847                      unless (defined $i) {
5848                        !!!cp ('t252');
5849                        !!!parse-error (type => 'unmatched end tag',
5850                                        text => 'tr', token => $token);
5851                        ## Ignore the token
5852                        !!!nack ('t252.1');
5853                        !!!next-token;
5854                        next B;
5855                      }
5856                    
5857                    ## Clear back to table row context
5858                    while (not ($self->{open_elements}->[-1]->[1]
5859                                    & TABLE_ROW_SCOPING_EL)) {
5860                      !!!cp ('t253');
5861    ## ISSUE: Can this case be reached?
5862                      pop @{$self->{open_elements}};
5863                  }                  }
5864                } # INSCOPE                  
5865                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
5866                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5867                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
                 !!!next-token;  
                 redo B;  
5868                }                }
5869    
               ## As if </tr>  
5870                ## have an element in table scope                ## have an element in table scope
5871                my $i;                my $i;
5872                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5873                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5874                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5875                      !!!cp ('t254');
5876                    $i = $_;                    $i = $_;
5877                    last INSCOPE;                    last INSCOPE;
5878                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5879                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
5880                    last INSCOPE;                    last INSCOPE;
5881                  }                  }
5882                } # INSCOPE                } # INSCOPE
5883                unless (defined $i) {                unless (defined $i) {
5884                  !!!parse-error (type => 'unmatched end tag:tr');                  !!!cp ('t256');
5885                    !!!parse-error (type => 'unmatched end tag',
5886                                    text => $token->{tag_name}, token => $token);
5887                  ## Ignore the token                  ## Ignore the token
5888                    !!!nack ('t256.1');
5889                  !!!next-token;                  !!!next-token;
5890                  redo B;                  next B;
5891                }                }
5892    
5893                ## Clear back to table row context                ## Clear back to table body context
5894                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5895                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
5896                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
5897                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
5898                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5899                }                }
5900    
5901                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
5902                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
5903                ## reprocess                !!!nack ('t257.1');
5904                redo B;                !!!next-token;
5905                  next B;
5906              } elsif ({              } elsif ({
5907                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
5908                        colgroup => 1, html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
5909                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5910                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
5911                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5912                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
5913                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
5914                !!!next-token;                            text => $token->{tag_name}, token => $token);
5915                redo B;            ## Ignore the token
5916              } else {            !!!nack ('t258.1');
5917                #             !!!next-token;
5918              }            next B;
5919            } else {          } else {
5920              #            !!!cp ('t259');
5921            }            !!!parse-error (type => 'in table:/',
5922                              text => $token->{tag_name}, token => $token);
5923    
5924            ## As if in table            $insert = $insert_to_foster;
5925            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5926            $in_body->($insert_to_foster);          }
5927            redo B;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5928          } elsif ($self->{insertion_mode} eq 'in cell') {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
5929            if ($token->{type} eq 'character') {                  @{$self->{open_elements}} == 1) { # redundant, maybe
5930              ## NOTE: This is a code clone of "character in body".            !!!parse-error (type => 'in body:#eof', token => $token);
5931              $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t259.1');
5932                          #
5933              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5934              !!!cp ('t259.2');
5935              #
5936            }
5937    
5938              !!!next-token;          ## Stop parsing
5939              redo B;          last B;
5940            } elsif ($token->{type} eq 'comment') {        } else {
5941              ## NOTE: This is a code clone of "comment in body".          die "$0: $token->{type}: Unknown token type";
5942              my $comment = $self->{document}->create_comment ($token->{data});        }
5943              $self->{open_elements}->[-1]->[0]->append_child ($comment);      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
5944              !!!next-token;            if ($token->{type} == CHARACTER_TOKEN) {
5945              redo B;              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
5946            } elsif ($token->{type} eq 'start tag') {                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5947              if ({                unless (length $token->{data}) {
5948                   caption => 1, col => 1, colgroup => 1,                  !!!cp ('t260');
                  tbody => 1, td => 1, tfoot => 1, th => 1,  
                  thead => 1, tr => 1,  
                 }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $tn) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
5949                  !!!next-token;                  !!!next-token;
5950                  redo B;                  next B;
5951                }                }
5952                }
5953                ## Close the cell              
5954                !!!back-token; # <?>              !!!cp ('t261');
5955                $token = {type => 'end tag', tag_name => $tn};              #
5956                redo B;            } elsif ($token->{type} == START_TAG_TOKEN) {
5957              } else {              if ($token->{tag_name} eq 'col') {
5958                  !!!cp ('t262');
5959                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5960                  pop @{$self->{open_elements}};
5961                  !!!ack ('t262.1');
5962                  !!!next-token;
5963                  next B;
5964                } else {
5965                  !!!cp ('t263');
5966                #                #
5967              }              }
5968            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5969              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'colgroup') {
5970                ## have an element in table scope                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
5971                my $i;                  !!!cp ('t264');
5972                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  !!!parse-error (type => 'unmatched end tag',
5973                  my $node = $self->{open_elements}->[$_];                                  text => 'colgroup', token => $token);
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5974                  ## Ignore the token                  ## Ignore the token
5975                  !!!next-token;                  !!!next-token;
5976                  redo B;                  next B;
5977                }                } else {
5978                                  !!!cp ('t265');
5979                ## generate implied end tags                  pop @{$self->{open_elements}}; # colgroup
5980                if ({                  $self->{insertion_mode} = IN_TABLE_IM;
5981                     dd => 1, dt => 1, li => 1, p => 1,                  !!!next-token;
5982                     td => ($token->{tag_name} eq 'th'),                  next B;            
                    th => ($token->{tag_name} eq 'td'),  
                    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 $token->{tag_name}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5983                }                }
5984                } elsif ($token->{tag_name} eq 'col') {
5985                splice @{$self->{open_elements}}, $i;                !!!cp ('t266');
5986                  !!!parse-error (type => 'unmatched end tag',
5987                $clear_up_to_marker->();                                text => 'col', token => $token);
   
               $self->{insertion_mode} = 'in row';  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
5988                ## Ignore the token                ## Ignore the token
5989                !!!next-token;                !!!next-token;
5990                redo B;                next B;
             } elsif ({  
                       table => 1, tbody => 1, tfoot => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $i;  
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   ## NOTE: There is exactly one |td| or |th| element  
                   ## in scope in the stack of open elements by definition.  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
5991              } else {              } else {
5992                #                !!!cp ('t267');
5993                  #
5994              }              }
5995          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5996            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
5997                @{$self->{open_elements}} == 1) { # redundant, maybe
5998              !!!cp ('t270.2');
5999              ## Stop parsing.
6000              last B;
6001            } else {
6002              ## NOTE: As if </colgroup>.
6003              !!!cp ('t270.1');
6004              pop @{$self->{open_elements}}; # colgroup
6005              $self->{insertion_mode} = IN_TABLE_IM;
6006              ## Reprocess.
6007              next B;
6008            }
6009          } else {
6010            die "$0: $token->{type}: Unknown token type";
6011          }
6012    
6013              ## As if </colgroup>
6014              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6015                !!!cp ('t269');
6016    ## TODO: Wrong error type?
6017                !!!parse-error (type => 'unmatched end tag',
6018                                text => 'colgroup', token => $token);
6019                ## Ignore the token
6020                !!!nack ('t269.1');
6021                !!!next-token;
6022                next B;
6023            } else {            } else {
6024              #              !!!cp ('t270');
6025                pop @{$self->{open_elements}}; # colgroup
6026                $self->{insertion_mode} = IN_TABLE_IM;
6027                !!!ack-later;
6028                ## reprocess
6029                next B;
6030              }
6031        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6032          if ($token->{type} == CHARACTER_TOKEN) {
6033            !!!cp ('t271');
6034            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6035            !!!next-token;
6036            next B;
6037          } elsif ($token->{type} == START_TAG_TOKEN) {
6038            if ($token->{tag_name} eq 'option') {
6039              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6040                !!!cp ('t272');
6041                ## As if </option>
6042                pop @{$self->{open_elements}};
6043              } else {
6044                !!!cp ('t273');
6045            }            }
             
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 ## As if </option>  
                 pop @{$self->{open_elements}};  
               }  
6046    
6047                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6048                !!!next-token;            !!!nack ('t273.1');
6049                redo B;            !!!next-token;
6050              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6051                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6052                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6053                  pop @{$self->{open_elements}};              !!!cp ('t274');
6054                }              ## As if </option>
6055                pop @{$self->{open_elements}};
6056              } else {
6057                !!!cp ('t275');
6058              }
6059    
6060                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6061                  ## As if </optgroup>              !!!cp ('t276');
6062                  pop @{$self->{open_elements}};              ## As if </optgroup>
6063                }              pop @{$self->{open_elements}};
6064              } else {
6065                !!!cp ('t277');
6066              }
6067    
6068                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6069                !!!next-token;            !!!nack ('t277.1');
6070                redo B;            !!!next-token;
6071              } elsif ($token->{tag_name} eq 'select') {            next B;
6072                !!!parse-error (type => 'not closed:select');          } elsif ({
6073                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6074                ## have an element in table scope                   }->{$token->{tag_name}} or
6075                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6076                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6077                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6078                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6079                    $i = $_;                     tr => 1, td => 1, th => 1,
6080                    last INSCOPE;                    }->{$token->{tag_name}})) {
6081                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6082                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6083                           }->{$node->[1]}) {                            token => $token);
6084                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6085                  }            ## as if there were </select> (otherwise).
6086                } # INSCOPE            ## have an element in table scope
6087                unless (defined $i) {            my $i;
6088                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6089                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6090                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6091                  redo B;                !!!cp ('t278');
6092                }                $i = $_;
6093                  last INSCOPE;
6094                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6095                  !!!cp ('t279');
6096                  last INSCOPE;
6097                }
6098              } # INSCOPE
6099              unless (defined $i) {
6100                !!!cp ('t280');
6101                !!!parse-error (type => 'unmatched end tag',
6102                                text => 'select', token => $token);
6103                ## Ignore the token
6104                !!!nack ('t280.1');
6105                !!!next-token;
6106                next B;
6107              }
6108                                
6109                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6110              splice @{$self->{open_elements}}, $i;
6111    
6112                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6113    
6114                !!!next-token;            if ($token->{tag_name} eq 'select') {
6115                redo B;              !!!nack ('t281.2');
6116              } else {              !!!next-token;
6117                #              next B;
6118              } else {
6119                !!!cp ('t281.1');
6120                !!!ack-later;
6121                ## Reprocess the token.
6122                next B;
6123              }
6124            } else {
6125              !!!cp ('t282');
6126              !!!parse-error (type => 'in select',
6127                              text => $token->{tag_name}, token => $token);
6128              ## Ignore the token
6129              !!!nack ('t282.1');
6130              !!!next-token;
6131              next B;
6132            }
6133          } elsif ($token->{type} == END_TAG_TOKEN) {
6134            if ($token->{tag_name} eq 'optgroup') {
6135              if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6136                  $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6137                !!!cp ('t283');
6138                ## As if </option>
6139                splice @{$self->{open_elements}}, -2;
6140              } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6141                !!!cp ('t284');
6142                pop @{$self->{open_elements}};
6143              } else {
6144                !!!cp ('t285');
6145                !!!parse-error (type => 'unmatched end tag',
6146                                text => $token->{tag_name}, token => $token);
6147                ## Ignore the token
6148              }
6149              !!!nack ('t285.1');
6150              !!!next-token;
6151              next B;
6152            } elsif ($token->{tag_name} eq 'option') {
6153              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6154                !!!cp ('t286');
6155                pop @{$self->{open_elements}};
6156              } else {
6157                !!!cp ('t287');
6158                !!!parse-error (type => 'unmatched end tag',
6159                                text => $token->{tag_name}, token => $token);
6160                ## Ignore the token
6161              }
6162              !!!nack ('t287.1');
6163              !!!next-token;
6164              next B;
6165            } elsif ($token->{tag_name} eq 'select') {
6166              ## have an element in table scope
6167              my $i;
6168              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6169                my $node = $self->{open_elements}->[$_];
6170                if ($node->[1] & SELECT_EL) {
6171                  !!!cp ('t288');
6172                  $i = $_;
6173                  last INSCOPE;
6174                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6175                  !!!cp ('t289');
6176                  last INSCOPE;
6177              }              }
6178            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6179              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
6180                if ($self->{open_elements}->[-1]->[1] eq 'option' and              !!!cp ('t290');
6181                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag',
6182                  ## As if </option>                              text => $token->{tag_name}, token => $token);
6183                  splice @{$self->{open_elements}}, -2;              ## Ignore the token
6184                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              !!!nack ('t290.1');
6185                  pop @{$self->{open_elements}};              !!!next-token;
6186                } else {              next B;
6187                  !!!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;  
               }  
6188                                
6189                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6190              splice @{$self->{open_elements}}, $i;
6191    
6192                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6193    
6194                !!!next-token;            !!!nack ('t291.1');
6195                redo B;            !!!next-token;
6196              } elsif ({            next B;
6197                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6198                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6199                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6200                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6201                                   }->{$token->{tag_name}}) {
6202                ## have an element in table scope  ## TODO: The following is wrong?
6203                my $i;            !!!parse-error (type => 'unmatched end tag',
6204                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;  
               }  
6205                                
6206                ## As if </select>            ## have an element in table scope
6207                ## have an element in table scope            my $i;
6208                undef $i;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6209                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              my $node = $self->{open_elements}->[$_];
6210                  my $node = $self->{open_elements}->[$_];              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6211                  if ($node->[1] eq 'select') {                !!!cp ('t292');
6212                    $i = $_;                $i = $_;
6213                    last INSCOPE;                last INSCOPE;
6214                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6215                            table => 1, html => 1,                !!!cp ('t293');
6216                           }->{$node->[1]}) {                last INSCOPE;
6217                    last INSCOPE;              }
6218                  }            } # INSCOPE
6219                } # INSCOPE            unless (defined $i) {
6220                unless (defined $i) {              !!!cp ('t294');
6221                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
6222                  ## Ignore the </select> token              !!!nack ('t294.1');
6223                  !!!next-token; ## TODO: ok?              !!!next-token;
6224                  redo B;              next B;
6225                }            }
6226                                
6227                splice @{$self->{open_elements}}, $i;            ## As if </select>
6228              ## have an element in table scope
6229                $self->_reset_insertion_mode;            undef $i;
6230              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6231                ## reprocess              my $node = $self->{open_elements}->[$_];
6232                redo B;              if ($node->[1] & SELECT_EL) {
6233              } else {                !!!cp ('t295');
6234                #                $i = $_;
6235                  last INSCOPE;
6236                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6237    ## ISSUE: Can this state be reached?
6238                  !!!cp ('t296');
6239                  last INSCOPE;
6240              }              }
6241            } else {            } # INSCOPE
6242              #            unless (defined $i) {
6243                !!!cp ('t297');
6244    ## TODO: The following error type is correct?
6245                !!!parse-error (type => 'unmatched end tag',
6246                                text => 'select', token => $token);
6247                ## Ignore the </select> token
6248                !!!nack ('t297.1');
6249                !!!next-token; ## TODO: ok?
6250                next B;
6251            }            }
6252                  
6253              !!!cp ('t298');
6254              splice @{$self->{open_elements}}, $i;
6255    
6256              $self->_reset_insertion_mode;
6257    
6258            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!ack-later;
6259              ## reprocess
6260              next B;
6261            } else {
6262              !!!cp ('t299');
6263              !!!parse-error (type => 'in select:/',
6264                              text => $token->{tag_name}, token => $token);
6265            ## Ignore the token            ## Ignore the token
6266              !!!nack ('t299.3');
6267            !!!next-token;            !!!next-token;
6268            redo B;            next B;
6269          } elsif ($self->{insertion_mode} eq 'after body') {          }
6270            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6271              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6272                ## As if in body                  @{$self->{open_elements}} == 1) { # redundant, maybe
6273                $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t299.1');
6274              !!!parse-error (type => 'in body:#eof', token => $token);
6275            } else {
6276              !!!cp ('t299.2');
6277            }
6278    
6279            ## Stop parsing.
6280            last B;
6281          } else {
6282            die "$0: $token->{type}: Unknown token type";
6283          }
6284        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6285          if ($token->{type} == CHARACTER_TOKEN) {
6286            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
6287              my $data = $1;
6288              ## As if in body
6289              $reconstruct_active_formatting_elements->($insert_to_current);
6290                                
6291                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6292              
6293              unless (length $token->{data}) {
6294                !!!cp ('t300');
6295                !!!next-token;
6296                next B;
6297              }
6298            }
6299            
6300            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6301              !!!cp ('t301');
6302              !!!parse-error (type => 'after html:#text', token => $token);
6303    
6304                unless (length $token->{data}) {            ## Reprocess in the "after body" insertion mode.
6305                  !!!next-token;          } else {
6306                  redo B;            !!!cp ('t302');
6307                }          }
6308              }          
6309                        ## "after body" insertion mode
6310              #          !!!parse-error (type => 'after body:#text', token => $token);
6311              !!!parse-error (type => 'after body:#'.$token->{type});  
6312            } elsif ($token->{type} eq 'comment') {          $self->{insertion_mode} = IN_BODY_IM;
6313              my $comment = $self->{document}->create_comment ($token->{data});          ## reprocess
6314              $self->{open_elements}->[0]->[0]->append_child ($comment);          next B;
6315          } elsif ($token->{type} == START_TAG_TOKEN) {
6316            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6317              !!!cp ('t303');
6318              !!!parse-error (type => 'after html',
6319                              text => $token->{tag_name}, token => $token);
6320              
6321              ## Reprocess in the "after body" insertion mode.
6322            } else {
6323              !!!cp ('t304');
6324            }
6325    
6326            ## "after body" insertion mode
6327            !!!parse-error (type => 'after body',
6328                            text => $token->{tag_name}, token => $token);
6329    
6330            $self->{insertion_mode} = IN_BODY_IM;
6331            !!!ack-later;
6332            ## reprocess
6333            next B;
6334          } elsif ($token->{type} == END_TAG_TOKEN) {
6335            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6336              !!!cp ('t305');
6337              !!!parse-error (type => 'after html:/',
6338                              text => $token->{tag_name}, token => $token);
6339              
6340              $self->{insertion_mode} = AFTER_BODY_IM;
6341              ## Reprocess in the "after body" insertion mode.
6342            } else {
6343              !!!cp ('t306');
6344            }
6345    
6346            ## "after body" insertion mode
6347            if ($token->{tag_name} eq 'html') {
6348              if (defined $self->{inner_html_node}) {
6349                !!!cp ('t307');
6350                !!!parse-error (type => 'unmatched end tag',
6351                                text => 'html', token => $token);
6352                ## Ignore the token
6353              !!!next-token;              !!!next-token;
6354              redo B;              next B;
           } elsif ($token->{type} eq 'start tag') {  
             !!!parse-error (type => 'after body:'.$token->{tag_name});  
             #  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'html') {  
               if (defined $self->{inner_html_node}) {  
                 !!!parse-error (type => 'unmatched end tag:html');  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               } else {  
                 $phase = 'trailing end';  
                 !!!next-token;  
                 redo B;  
               }  
             } else {  
               !!!parse-error (type => 'after body:/'.$token->{tag_name});  
             }  
6355            } else {            } else {
6356              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t308');
6357                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6358                !!!next-token;
6359                next B;
6360            }            }
6361            } else {
6362              !!!cp ('t309');
6363              !!!parse-error (type => 'after body:/',
6364                              text => $token->{tag_name}, token => $token);
6365    
6366            $self->{insertion_mode} = 'in body';            $self->{insertion_mode} = IN_BODY_IM;
6367            ## reprocess            ## reprocess
6368            redo B;            next B;
6369          } elsif ($self->{insertion_mode} eq 'in frameset') {          }
6370            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6371              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t309.2');
6372                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          ## Stop parsing
6373            last B;
6374                unless (length $token->{data}) {        } else {
6375                  !!!next-token;          die "$0: $token->{type}: Unknown token type";
6376                  redo B;        }
6377                }      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6378              }        if ($token->{type} == CHARACTER_TOKEN) {
6379            if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {
6380              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6381              
6382              unless (length $token->{data}) {
6383                !!!cp ('t310');
6384                !!!next-token;
6385                next B;
6386              }
6387            }
6388            
6389            if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {
6390              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6391                !!!cp ('t311');
6392                !!!parse-error (type => 'in frameset:#text', token => $token);
6393              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6394                !!!cp ('t312');
6395                !!!parse-error (type => 'after frameset:#text', token => $token);
6396              } else { # "after after frameset"
6397                !!!cp ('t313');
6398                !!!parse-error (type => 'after html:#text', token => $token);
6399              }
6400              
6401              ## Ignore the token.
6402              if (length $token->{data}) {
6403                !!!cp ('t314');
6404                ## reprocess the rest of characters
6405              } else {
6406                !!!cp ('t315');
6407                !!!next-token;
6408              }
6409              next B;
6410            }
6411            
6412            die qq[$0: Character "$token->{data}"];
6413          } elsif ($token->{type} == START_TAG_TOKEN) {
6414            if ($token->{tag_name} eq 'frameset' and
6415                $self->{insertion_mode} == IN_FRAMESET_IM) {
6416              !!!cp ('t318');
6417              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6418              !!!nack ('t318.1');
6419              !!!next-token;
6420              next B;
6421            } elsif ($token->{tag_name} eq 'frame' and
6422                     $self->{insertion_mode} == IN_FRAMESET_IM) {
6423              !!!cp ('t319');
6424              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6425              pop @{$self->{open_elements}};
6426              !!!ack ('t319.1');
6427              !!!next-token;
6428              next B;
6429            } elsif ($token->{tag_name} eq 'noframes') {
6430              !!!cp ('t320');
6431              ## NOTE: As if in head.
6432              $parse_rcdata->(CDATA_CONTENT_MODEL);
6433              next B;
6434    
6435              #            ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6436            } elsif ($token->{type} eq 'comment') {            ## has no parse error.
6437              my $comment = $self->{document}->create_comment ($token->{data});          } else {
6438              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6439                !!!cp ('t321');
6440                !!!parse-error (type => 'in frameset',
6441                                text => $token->{tag_name}, token => $token);
6442              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6443                !!!cp ('t322');
6444                !!!parse-error (type => 'after frameset',
6445                                text => $token->{tag_name}, token => $token);
6446              } else { # "after after frameset"
6447                !!!cp ('t322.2');
6448                !!!parse-error (type => 'after after frameset',
6449                                text => $token->{tag_name}, token => $token);
6450              }
6451              ## Ignore the token
6452              !!!nack ('t322.1');
6453              !!!next-token;
6454              next B;
6455            }
6456          } elsif ($token->{type} == END_TAG_TOKEN) {
6457            if ($token->{tag_name} eq 'frameset' and
6458                $self->{insertion_mode} == IN_FRAMESET_IM) {
6459              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6460                  @{$self->{open_elements}} == 1) {
6461                !!!cp ('t325');
6462                !!!parse-error (type => 'unmatched end tag',
6463                                text => $token->{tag_name}, token => $token);
6464                ## Ignore the token
6465              !!!next-token;              !!!next-token;
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'frameset') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'frame') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'noframes') {  
               $in_body->($insert_to_current);  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'frameset') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html' and  
                   @{$self->{open_elements}} == 1) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
               } else {  
                 pop @{$self->{open_elements}};  
                 !!!next-token;  
               }  
                 
               ## if not inner_html and  
               if ($self->{open_elements}->[-1]->[1] ne 'frameset') {  
                 $self->{insertion_mode} = 'after frameset';  
               }  
               redo B;  
             } else {  
               #  
             }  
6466            } else {            } else {
6467              #              !!!cp ('t326');
6468                pop @{$self->{open_elements}};
6469                !!!next-token;
6470            }            }
6471              
6472            if (defined $token->{tag_name}) {            if (not defined $self->{inner_html_node} and
6473              !!!parse-error (type => 'in frameset:'.($token->{type} eq 'end tag' ? '/' : '').$token->{tag_name});                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6474                !!!cp ('t327');
6475                $self->{insertion_mode} = AFTER_FRAMESET_IM;
6476            } else {            } else {
6477              !!!parse-error (type => 'in frameset:#'.$token->{type});              !!!cp ('t328');
6478              }
6479              next B;
6480            } elsif ($token->{tag_name} eq 'html' and
6481                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6482              !!!cp ('t329');
6483              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6484              !!!next-token;
6485              next B;
6486            } else {
6487              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6488                !!!cp ('t330');
6489                !!!parse-error (type => 'in frameset:/',
6490                                text => $token->{tag_name}, token => $token);
6491              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6492                !!!cp ('t330.1');
6493                !!!parse-error (type => 'after frameset:/',
6494                                text => $token->{tag_name}, token => $token);
6495              } else { # "after after html"
6496                !!!cp ('t331');
6497                !!!parse-error (type => 'after after frameset:/',
6498                                text => $token->{tag_name}, token => $token);
6499            }            }
6500            ## Ignore the token            ## Ignore the token
6501            !!!next-token;            !!!next-token;
6502            redo B;            next B;
6503          } elsif ($self->{insertion_mode} eq 'after frameset') {          }
6504            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6505              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6506                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                  @{$self->{open_elements}} == 1) { # redundant, maybe
6507              !!!cp ('t331.1');
6508              !!!parse-error (type => 'in body:#eof', token => $token);
6509            } else {
6510              !!!cp ('t331.2');
6511            }
6512            
6513            ## Stop parsing
6514            last B;
6515          } else {
6516            die "$0: $token->{type}: Unknown token type";
6517          }
6518    
6519                unless (length $token->{data}) {        ## ISSUE: An issue in spec here
6520                  !!!next-token;      } else {
6521                  redo B;        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6522                }      }
6523    
6524        ## "in body" insertion mode
6525        if ($token->{type} == START_TAG_TOKEN) {
6526          if ($token->{tag_name} eq 'script') {
6527            !!!cp ('t332');
6528            ## NOTE: This is an "as if in head" code clone
6529            $script_start_tag->();
6530            next B;
6531          } elsif ($token->{tag_name} eq 'style') {
6532            !!!cp ('t333');
6533            ## NOTE: This is an "as if in head" code clone
6534            $parse_rcdata->(CDATA_CONTENT_MODEL);
6535            next B;
6536          } elsif ({
6537                    base => 1, link => 1,
6538                   }->{$token->{tag_name}}) {
6539            !!!cp ('t334');
6540            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6541            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6542            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6543            !!!ack ('t334.1');
6544            !!!next-token;
6545            next B;
6546          } elsif ($token->{tag_name} eq 'meta') {
6547            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6548            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6549            my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6550    
6551            unless ($self->{confident}) {
6552              if ($token->{attributes}->{charset}) {
6553                !!!cp ('t335');
6554                ## NOTE: Whether the encoding is supported or not is handled
6555                ## in the {change_encoding} callback.
6556                $self->{change_encoding}
6557                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6558                
6559                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6560                    ->set_user_data (manakai_has_reference =>
6561                                         $token->{attributes}->{charset}
6562                                             ->{has_reference});
6563              } elsif ($token->{attributes}->{content}) {
6564                if ($token->{attributes}->{content}->{value}
6565                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6566                        [\x09-\x0D\x20]*=
6567                        [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6568                        ([^"'\x09-\x0D\x20][^\x09-\x0D\x20\x3B]*))/x) {
6569                  !!!cp ('t336');
6570                  ## NOTE: Whether the encoding is supported or not is handled
6571                  ## in the {change_encoding} callback.
6572                  $self->{change_encoding}
6573                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6574                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6575                      ->set_user_data (manakai_has_reference =>
6576                                           $token->{attributes}->{content}
6577                                                 ->{has_reference});
6578              }              }
6579              }
6580            } else {
6581              if ($token->{attributes}->{charset}) {
6582                !!!cp ('t337');
6583                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6584                    ->set_user_data (manakai_has_reference =>
6585                                         $token->{attributes}->{charset}
6586                                             ->{has_reference});
6587              }
6588              if ($token->{attributes}->{content}) {
6589                !!!cp ('t338');
6590                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6591                    ->set_user_data (manakai_has_reference =>
6592                                         $token->{attributes}->{content}
6593                                             ->{has_reference});
6594              }
6595            }
6596    
6597              #          !!!ack ('t338.1');
6598            } elsif ($token->{type} eq 'comment') {          !!!next-token;
6599              my $comment = $self->{document}->create_comment ($token->{data});          next B;
6600              $self->{open_elements}->[-1]->[0]->append_child ($comment);        } elsif ($token->{tag_name} eq 'title') {
6601              !!!next-token;          !!!cp ('t341');
6602              redo B;          ## NOTE: This is an "as if in head" code clone
6603            } elsif ($token->{type} eq 'start tag') {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6604              if ($token->{tag_name} eq 'noframes') {          next B;
6605                $in_body->($insert_to_current);        } elsif ($token->{tag_name} eq 'body') {
6606                redo B;          !!!parse-error (type => 'in body', text => 'body', token => $token);
6607              } else {                
6608                #          if (@{$self->{open_elements}} == 1 or
6609                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6610              !!!cp ('t342');
6611              ## Ignore the token
6612            } else {
6613              my $body_el = $self->{open_elements}->[1]->[0];
6614              for my $attr_name (keys %{$token->{attributes}}) {
6615                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6616                  !!!cp ('t343');
6617                  $body_el->set_attribute_ns
6618                    (undef, [undef, $attr_name],
6619                     $token->{attributes}->{$attr_name}->{value});
6620              }              }
6621            } elsif ($token->{type} eq 'end tag') {            }
6622              if ($token->{tag_name} eq 'html') {          }
6623                $phase = 'trailing end';          !!!nack ('t343.1');
6624            !!!next-token;
6625            next B;
6626          } elsif ({
6627                    address => 1, blockquote => 1, center => 1, dir => 1,
6628                    div => 1, dl => 1, fieldset => 1,
6629                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6630                    menu => 1, ol => 1, p => 1, ul => 1,
6631                    pre => 1, listing => 1,
6632                    form => 1,
6633                    table => 1,
6634                    hr => 1,
6635                   }->{$token->{tag_name}}) {
6636            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6637              !!!cp ('t350');
6638              !!!parse-error (type => 'in form:form', token => $token);
6639              ## Ignore the token
6640              !!!nack ('t350.1');
6641              !!!next-token;
6642              next B;
6643            }
6644    
6645            ## has a p element in scope
6646            INSCOPE: for (reverse @{$self->{open_elements}}) {
6647              if ($_->[1] & P_EL) {
6648                !!!cp ('t344');
6649                !!!back-token; # <form>
6650                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6651                          line => $token->{line}, column => $token->{column}};
6652                next B;
6653              } elsif ($_->[1] & SCOPING_EL) {
6654                !!!cp ('t345');
6655                last INSCOPE;
6656              }
6657            } # INSCOPE
6658              
6659            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6660            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6661              !!!nack ('t346.1');
6662              !!!next-token;
6663              if ($token->{type} == CHARACTER_TOKEN) {
6664                $token->{data} =~ s/^\x0A//;
6665                unless (length $token->{data}) {
6666                  !!!cp ('t346');
6667                !!!next-token;                !!!next-token;
               redo B;  
6668              } else {              } else {
6669                #                !!!cp ('t349');
6670              }              }
6671            } else {            } else {
6672              #              !!!cp ('t348');
6673            }            }
6674            } elsif ($token->{tag_name} eq 'form') {
6675              !!!cp ('t347.1');
6676              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6677    
6678              !!!nack ('t347.2');
6679              !!!next-token;
6680            } elsif ($token->{tag_name} eq 'table') {
6681              !!!cp ('t382');
6682              push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6683                        
6684            if (defined $token->{tag_name}) {            $self->{insertion_mode} = IN_TABLE_IM;
6685              !!!parse-error (type => 'after frameset:'.($token->{tag_name} eq 'end tag' ? '/' : '').$token->{tag_name});  
6686              !!!nack ('t382.1');
6687              !!!next-token;
6688            } elsif ($token->{tag_name} eq 'hr') {
6689              !!!cp ('t386');
6690              pop @{$self->{open_elements}};
6691            
6692              !!!nack ('t386.1');
6693              !!!next-token;
6694            } else {
6695              !!!nack ('t347.1');
6696              !!!next-token;
6697            }
6698            next B;
6699          } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6700            ## has a p element in scope
6701            INSCOPE: for (reverse @{$self->{open_elements}}) {
6702              if ($_->[1] & P_EL) {
6703                !!!cp ('t353');
6704                !!!back-token; # <x>
6705                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6706                          line => $token->{line}, column => $token->{column}};
6707                next B;
6708              } elsif ($_->[1] & SCOPING_EL) {
6709                !!!cp ('t354');
6710                last INSCOPE;
6711              }
6712            } # INSCOPE
6713              
6714            ## Step 1
6715            my $i = -1;
6716            my $node = $self->{open_elements}->[$i];
6717            my $li_or_dtdd = {li => {li => 1},
6718                              dt => {dt => 1, dd => 1},
6719                              dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
6720            LI: {
6721              ## Step 2
6722              if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
6723                if ($i != -1) {
6724                  !!!cp ('t355');
6725                  !!!parse-error (type => 'not closed',
6726                                  text => $self->{open_elements}->[-1]->[0]
6727                                      ->manakai_local_name,
6728                                  token => $token);
6729                } else {
6730                  !!!cp ('t356');
6731                }
6732                splice @{$self->{open_elements}}, $i;
6733                last LI;
6734            } else {            } else {
6735              !!!parse-error (type => 'after frameset:#'.$token->{type});              !!!cp ('t357');
6736              }
6737              
6738              ## Step 3
6739              if (not ($node->[1] & FORMATTING_EL) and
6740                  #not $phrasing_category->{$node->[1]} and
6741                  ($node->[1] & SPECIAL_EL or
6742                   $node->[1] & SCOPING_EL) and
6743                  not ($node->[1] & ADDRESS_EL) and
6744                  not ($node->[1] & DIV_EL)) {
6745                !!!cp ('t358');
6746                last LI;
6747              }
6748              
6749              !!!cp ('t359');
6750              ## Step 4
6751              $i--;
6752              $node = $self->{open_elements}->[$i];
6753              redo LI;
6754            } # LI
6755              
6756            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6757            !!!nack ('t359.1');
6758            !!!next-token;
6759            next B;
6760          } elsif ($token->{tag_name} eq 'plaintext') {
6761            ## has a p element in scope
6762            INSCOPE: for (reverse @{$self->{open_elements}}) {
6763              if ($_->[1] & P_EL) {
6764                !!!cp ('t367');
6765                !!!back-token; # <plaintext>
6766                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6767                          line => $token->{line}, column => $token->{column}};
6768                next B;
6769              } elsif ($_->[1] & SCOPING_EL) {
6770                !!!cp ('t368');
6771                last INSCOPE;
6772            }            }
6773            } # INSCOPE
6774              
6775            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6776              
6777            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
6778              
6779            !!!nack ('t368.1');
6780            !!!next-token;
6781            next B;
6782          } elsif ($token->{tag_name} eq 'a') {
6783            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
6784              my $node = $active_formatting_elements->[$i];
6785              if ($node->[1] & A_EL) {
6786                !!!cp ('t371');
6787                !!!parse-error (type => 'in a:a', token => $token);
6788                
6789                !!!back-token; # <a>
6790                $token = {type => END_TAG_TOKEN, tag_name => 'a',
6791                          line => $token->{line}, column => $token->{column}};
6792                $formatting_end_tag->($token);
6793                
6794                AFE2: for (reverse 0..$#$active_formatting_elements) {
6795                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
6796                    !!!cp ('t372');
6797                    splice @$active_formatting_elements, $_, 1;
6798                    last AFE2;
6799                  }
6800                } # AFE2
6801                OE: for (reverse 0..$#{$self->{open_elements}}) {
6802                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
6803                    !!!cp ('t373');
6804                    splice @{$self->{open_elements}}, $_, 1;
6805                    last OE;
6806                  }
6807                } # OE
6808                last AFE;
6809              } elsif ($node->[0] eq '#marker') {
6810                !!!cp ('t374');
6811                last AFE;
6812              }
6813            } # AFE
6814              
6815            $reconstruct_active_formatting_elements->($insert_to_current);
6816    
6817            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6818            push @$active_formatting_elements, $self->{open_elements}->[-1];
6819    
6820            !!!nack ('t374.1');
6821            !!!next-token;
6822            next B;
6823          } elsif ($token->{tag_name} eq 'nobr') {
6824            $reconstruct_active_formatting_elements->($insert_to_current);
6825    
6826            ## has a |nobr| element in scope
6827            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6828              my $node = $self->{open_elements}->[$_];
6829              if ($node->[1] & NOBR_EL) {
6830                !!!cp ('t376');
6831                !!!parse-error (type => 'in nobr:nobr', token => $token);
6832                !!!back-token; # <nobr>
6833                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
6834                          line => $token->{line}, column => $token->{column}};
6835                next B;
6836              } elsif ($node->[1] & SCOPING_EL) {
6837                !!!cp ('t377');
6838                last INSCOPE;
6839              }
6840            } # INSCOPE
6841            
6842            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6843            push @$active_formatting_elements, $self->{open_elements}->[-1];
6844            
6845            !!!nack ('t377.1');
6846            !!!next-token;
6847            next B;
6848          } elsif ($token->{tag_name} eq 'button') {
6849            ## has a button element in scope
6850            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6851              my $node = $self->{open_elements}->[$_];
6852              if ($node->[1] & BUTTON_EL) {
6853                !!!cp ('t378');
6854                !!!parse-error (type => 'in button:button', token => $token);
6855                !!!back-token; # <button>
6856                $token = {type => END_TAG_TOKEN, tag_name => 'button',
6857                          line => $token->{line}, column => $token->{column}};
6858                next B;
6859              } elsif ($node->[1] & SCOPING_EL) {
6860                !!!cp ('t379');
6861                last INSCOPE;
6862              }
6863            } # INSCOPE
6864              
6865            $reconstruct_active_formatting_elements->($insert_to_current);
6866              
6867            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6868    
6869            ## TODO: associate with $self->{form_element} if defined
6870    
6871            push @$active_formatting_elements, ['#marker', ''];
6872    
6873            !!!nack ('t379.1');
6874            !!!next-token;
6875            next B;
6876          } elsif ({
6877                    xmp => 1,
6878                    iframe => 1,
6879                    noembed => 1,
6880                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
6881                    noscript => 0, ## TODO: 1 if scripting is enabled
6882                   }->{$token->{tag_name}}) {
6883            if ($token->{tag_name} eq 'xmp') {
6884              !!!cp ('t381');
6885              $reconstruct_active_formatting_elements->($insert_to_current);
6886            } else {
6887              !!!cp ('t399');
6888            }
6889            ## NOTE: There is an "as if in body" code clone.
6890            $parse_rcdata->(CDATA_CONTENT_MODEL);
6891            next B;
6892          } elsif ($token->{tag_name} eq 'isindex') {
6893            !!!parse-error (type => 'isindex', token => $token);
6894            
6895            if (defined $self->{form_element}) {
6896              !!!cp ('t389');
6897            ## Ignore the token            ## Ignore the token
6898              !!!nack ('t389'); ## NOTE: Not acknowledged.
6899            !!!next-token;            !!!next-token;
6900            redo B;            next B;
6901            } else {
6902              !!!ack ('t391.1');
6903    
6904            ## ISSUE: An issue in spec there            my $at = $token->{attributes};
6905              my $form_attrs;
6906              $form_attrs->{action} = $at->{action} if $at->{action};
6907              my $prompt_attr = $at->{prompt};
6908              $at->{name} = {name => 'name', value => 'isindex'};
6909              delete $at->{action};
6910              delete $at->{prompt};
6911              my @tokens = (
6912                            {type => START_TAG_TOKEN, tag_name => 'form',
6913                             attributes => $form_attrs,
6914                             line => $token->{line}, column => $token->{column}},
6915                            {type => START_TAG_TOKEN, tag_name => 'hr',
6916                             line => $token->{line}, column => $token->{column}},
6917                            {type => START_TAG_TOKEN, tag_name => 'p',
6918                             line => $token->{line}, column => $token->{column}},
6919                            {type => START_TAG_TOKEN, tag_name => 'label',
6920                             line => $token->{line}, column => $token->{column}},
6921                           );
6922              if ($prompt_attr) {
6923                !!!cp ('t390');
6924                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
6925                               #line => $token->{line}, column => $token->{column},
6926                              };
6927              } else {
6928                !!!cp ('t391');
6929                push @tokens, {type => CHARACTER_TOKEN,
6930                               data => 'This is a searchable index. Insert your search keywords here: ',
6931                               #line => $token->{line}, column => $token->{column},
6932                              }; # SHOULD
6933                ## TODO: make this configurable
6934              }
6935              push @tokens,
6936                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
6937                             line => $token->{line}, column => $token->{column}},
6938                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
6939                            {type => END_TAG_TOKEN, tag_name => 'label',
6940                             line => $token->{line}, column => $token->{column}},
6941                            {type => END_TAG_TOKEN, tag_name => 'p',
6942                             line => $token->{line}, column => $token->{column}},
6943                            {type => START_TAG_TOKEN, tag_name => 'hr',
6944                             line => $token->{line}, column => $token->{column}},
6945                            {type => END_TAG_TOKEN, tag_name => 'form',
6946                             line => $token->{line}, column => $token->{column}};
6947              !!!back-token (@tokens);
6948              !!!next-token;
6949              next B;
6950            }
6951          } elsif ($token->{tag_name} eq 'textarea') {
6952            my $tag_name = $token->{tag_name};
6953            my $el;
6954            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
6955            
6956            ## TODO: $self->{form_element} if defined
6957            $self->{content_model} = RCDATA_CONTENT_MODEL;
6958            delete $self->{escape}; # MUST
6959            
6960            $insert->($el);
6961            
6962            my $text = '';
6963            !!!nack ('t392.1');
6964            !!!next-token;
6965            if ($token->{type} == CHARACTER_TOKEN) {
6966              $token->{data} =~ s/^\x0A//;
6967              unless (length $token->{data}) {
6968                !!!cp ('t392');
6969                !!!next-token;
6970              } else {
6971                !!!cp ('t393');
6972              }
6973          } else {          } else {
6974            die "$0: $self->{insertion_mode}: Unknown insertion mode";            !!!cp ('t394');
6975            }
6976            while ($token->{type} == CHARACTER_TOKEN) {
6977              !!!cp ('t395');
6978              $text .= $token->{data};
6979              !!!next-token;
6980            }
6981            if (length $text) {
6982              !!!cp ('t396');
6983              $el->manakai_append_text ($text);
6984            }
6985            
6986            $self->{content_model} = PCDATA_CONTENT_MODEL;
6987            
6988            if ($token->{type} == END_TAG_TOKEN and
6989                $token->{tag_name} eq $tag_name) {
6990              !!!cp ('t397');
6991              ## Ignore the token
6992            } else {
6993              !!!cp ('t398');
6994              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
6995          }          }
       }  
     } elsif ($phase eq 'trailing end') {  
       ## states in the main stage is preserved yet # MUST  
         
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'after html:#DOCTYPE');  
         ## Ignore the token  
6996          !!!next-token;          !!!next-token;
6997          redo B;          next B;
6998        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{tag_name} eq 'rt' or
6999          my $comment = $self->{document}->create_comment ($token->{data});                 $token->{tag_name} eq 'rp') {
7000          $self->{document}->append_child ($comment);          ## has a |ruby| element in scope
7001            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7002              my $node = $self->{open_elements}->[$_];
7003              if ($node->[1] & RUBY_EL) {
7004                !!!cp ('t398.1');
7005                ## generate implied end tags
7006                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7007                  !!!cp ('t398.2');
7008                  pop @{$self->{open_elements}};
7009                }
7010                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7011                  !!!cp ('t398.3');
7012                  !!!parse-error (type => 'not closed',
7013                                  text => $self->{open_elements}->[-1]->[0]
7014                                      ->manakai_local_name,
7015                                  token => $token);
7016                  pop @{$self->{open_elements}}
7017                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7018                }
7019                last INSCOPE;
7020              } elsif ($node->[1] & SCOPING_EL) {
7021                !!!cp ('t398.4');
7022                last INSCOPE;
7023              }
7024            } # INSCOPE
7025    
7026            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7027    
7028            !!!nack ('t398.5');
7029          !!!next-token;          !!!next-token;
7030          redo B;          redo B;
7031        } elsif ($token->{type} eq 'character') {        } elsif ($token->{tag_name} eq 'math' or
7032          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                 $token->{tag_name} eq 'svg') {
7033            my $data = $1;          $reconstruct_active_formatting_elements->($insert_to_current);
7034            ## As if in the main phase.  
7035            ## NOTE: The insertion mode in the main phase          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7036            ## just before the phase has been changed to the trailing  
7037            ## end phase is either "after body" or "after frameset".          ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7038            $reconstruct_active_formatting_elements->($insert_to_current)  
7039              if $phase eq 'main';          ## "adjust foreign attributes" - done in insert-element-f
7040            
7041            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7042            
7043            if ($self->{self_closing}) {
7044              pop @{$self->{open_elements}};
7045              !!!ack ('t398.1');
7046            } else {
7047              !!!cp ('t398.2');
7048              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7049              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7050              ## mode, "in body" (not "in foreign content") secondary insertion
7051              ## mode, maybe.
7052            }
7053    
7054            !!!next-token;
7055            next B;
7056          } elsif ({
7057                    caption => 1, col => 1, colgroup => 1, frame => 1,
7058                    frameset => 1, head => 1, option => 1, optgroup => 1,
7059                    tbody => 1, td => 1, tfoot => 1, th => 1,
7060                    thead => 1, tr => 1,
7061                   }->{$token->{tag_name}}) {
7062            !!!cp ('t401');
7063            !!!parse-error (type => 'in body',
7064                            text => $token->{tag_name}, token => $token);
7065            ## Ignore the token
7066            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7067            !!!next-token;
7068            next B;
7069            
7070            ## ISSUE: An issue on HTML5 new elements in the spec.
7071          } else {
7072            if ($token->{tag_name} eq 'image') {
7073              !!!cp ('t384');
7074              !!!parse-error (type => 'image', token => $token);
7075              $token->{tag_name} = 'img';
7076            } else {
7077              !!!cp ('t385');
7078            }
7079    
7080            ## NOTE: There is an "as if <br>" code clone.
7081            $reconstruct_active_formatting_elements->($insert_to_current);
7082            
7083            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7084    
7085            if ({
7086                 applet => 1, marquee => 1, object => 1,
7087                }->{$token->{tag_name}}) {
7088              !!!cp ('t380');
7089              push @$active_formatting_elements, ['#marker', ''];
7090              !!!nack ('t380.1');
7091            } elsif ({
7092                      b => 1, big => 1, em => 1, font => 1, i => 1,
7093                      s => 1, small => 1, strile => 1,
7094                      strong => 1, tt => 1, u => 1,
7095                     }->{$token->{tag_name}}) {
7096              !!!cp ('t375');
7097              push @$active_formatting_elements, $self->{open_elements}->[-1];
7098              !!!nack ('t375.1');
7099            } elsif ($token->{tag_name} eq 'input') {
7100              !!!cp ('t388');
7101              ## TODO: associate with $self->{form_element} if defined
7102              pop @{$self->{open_elements}};
7103              !!!ack ('t388.2');
7104            } elsif ({
7105                      area => 1, basefont => 1, bgsound => 1, br => 1,
7106                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
7107                      #image => 1,
7108                     }->{$token->{tag_name}}) {
7109              !!!cp ('t388.1');
7110              pop @{$self->{open_elements}};
7111              !!!ack ('t388.3');
7112            } elsif ($token->{tag_name} eq 'select') {
7113              ## TODO: associate with $self->{form_element} if defined
7114            
7115              if ($self->{insertion_mode} & TABLE_IMS or
7116                  $self->{insertion_mode} & BODY_TABLE_IMS or
7117                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7118                !!!cp ('t400.1');
7119                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7120              } else {
7121                !!!cp ('t400.2');
7122                $self->{insertion_mode} = IN_SELECT_IM;
7123              }
7124              !!!nack ('t400.3');
7125            } else {
7126              !!!nack ('t402');
7127            }
7128            
7129            !!!next-token;
7130            next B;
7131          }
7132        } elsif ($token->{type} == END_TAG_TOKEN) {
7133          if ($token->{tag_name} eq 'body') {
7134            ## has a |body| element in scope
7135            my $i;
7136            INSCOPE: {
7137              for (reverse @{$self->{open_elements}}) {
7138                if ($_->[1] & BODY_EL) {
7139                  !!!cp ('t405');
7140                  $i = $_;
7141                  last INSCOPE;
7142                } elsif ($_->[1] & SCOPING_EL) {
7143                  !!!cp ('t405.1');
7144                  last;
7145                }
7146              }
7147    
7148              !!!parse-error (type => 'start tag not allowed',
7149                              text => $token->{tag_name}, token => $token);
7150              ## NOTE: Ignore the token.
7151              !!!next-token;
7152              next B;
7153            } # INSCOPE
7154    
7155            for (@{$self->{open_elements}}) {
7156              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7157                !!!cp ('t403');
7158                !!!parse-error (type => 'not closed',
7159                                text => $_->[0]->manakai_local_name,
7160                                token => $token);
7161                last;
7162              } else {
7163                !!!cp ('t404');
7164              }
7165            }
7166    
7167            $self->{insertion_mode} = AFTER_BODY_IM;
7168            !!!next-token;
7169            next B;
7170          } elsif ($token->{tag_name} eq 'html') {
7171            ## TODO: Update this code.  It seems that the code below is not
7172            ## up-to-date, though it has same effect as speced.
7173            if (@{$self->{open_elements}} > 1 and
7174                $self->{open_elements}->[1]->[1] & BODY_EL) {
7175              ## ISSUE: There is an issue in the spec.
7176              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7177                !!!cp ('t406');
7178                !!!parse-error (type => 'not closed',
7179                                text => $self->{open_elements}->[1]->[0]
7180                                    ->manakai_local_name,
7181                                token => $token);
7182              } else {
7183                !!!cp ('t407');
7184              }
7185              $self->{insertion_mode} = AFTER_BODY_IM;
7186              ## reprocess
7187              next B;
7188            } else {
7189              !!!cp ('t408');
7190              !!!parse-error (type => 'unmatched end tag',
7191                              text => $token->{tag_name}, token => $token);
7192              ## Ignore the token
7193              !!!next-token;
7194              next B;
7195            }
7196          } elsif ({
7197                    address => 1, blockquote => 1, center => 1, dir => 1,
7198                    div => 1, dl => 1, fieldset => 1, listing => 1,
7199                    menu => 1, ol => 1, pre => 1, ul => 1,
7200                    dd => 1, dt => 1, li => 1,
7201                    applet => 1, button => 1, marquee => 1, object => 1,
7202                   }->{$token->{tag_name}}) {
7203            ## has an element in scope
7204            my $i;
7205            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7206              my $node = $self->{open_elements}->[$_];
7207              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7208                !!!cp ('t410');
7209                $i = $_;
7210                last INSCOPE;
7211              } elsif ($node->[1] & SCOPING_EL) {
7212                !!!cp ('t411');
7213                last INSCOPE;
7214              }
7215            } # INSCOPE
7216    
7217            unless (defined $i) { # has an element in scope
7218              !!!cp ('t413');
7219              !!!parse-error (type => 'unmatched end tag',
7220                              text => $token->{tag_name}, token => $token);
7221              ## NOTE: Ignore the token.
7222            } else {
7223              ## Step 1. generate implied end tags
7224              while ({
7225                      ## END_TAG_OPTIONAL_EL
7226                      dd => ($token->{tag_name} ne 'dd'),
7227                      dt => ($token->{tag_name} ne 'dt'),
7228                      li => ($token->{tag_name} ne 'li'),
7229                      p => 1,
7230                      rt => 1,
7231                      rp => 1,
7232                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7233                !!!cp ('t409');
7234                pop @{$self->{open_elements}};
7235              }
7236    
7237              ## Step 2.
7238              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7239                      ne $token->{tag_name}) {
7240                !!!cp ('t412');
7241                !!!parse-error (type => 'not closed',
7242                                text => $self->{open_elements}->[-1]->[0]
7243                                    ->manakai_local_name,
7244                                token => $token);
7245              } else {
7246                !!!cp ('t414');
7247              }
7248    
7249              ## Step 3.
7250              splice @{$self->{open_elements}}, $i;
7251    
7252              ## Step 4.
7253              $clear_up_to_marker->()
7254                  if {
7255                    applet => 1, button => 1, marquee => 1, object => 1,
7256                  }->{$token->{tag_name}};
7257            }
7258            !!!next-token;
7259            next B;
7260          } elsif ($token->{tag_name} eq 'form') {
7261            undef $self->{form_element};
7262    
7263            ## has an element in scope
7264            my $i;
7265            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7266              my $node = $self->{open_elements}->[$_];
7267              if ($node->[1] & FORM_EL) {
7268                !!!cp ('t418');
7269                $i = $_;
7270                last INSCOPE;
7271              } elsif ($node->[1] & SCOPING_EL) {
7272                !!!cp ('t419');
7273                last INSCOPE;
7274              }
7275            } # INSCOPE
7276    
7277            unless (defined $i) { # has an element in scope
7278              !!!cp ('t421');
7279              !!!parse-error (type => 'unmatched end tag',
7280                              text => $token->{tag_name}, token => $token);
7281              ## NOTE: Ignore the token.
7282            } else {
7283              ## Step 1. generate implied end tags
7284              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7285                !!!cp ('t417');
7286                pop @{$self->{open_elements}};
7287              }
7288                        
7289            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 2.
7290              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7291                      ne $token->{tag_name}) {
7292                !!!cp ('t417.1');
7293                !!!parse-error (type => 'not closed',
7294                                text => $self->{open_elements}->[-1]->[0]
7295                                    ->manakai_local_name,
7296                                token => $token);
7297              } else {
7298                !!!cp ('t420');
7299              }  
7300                        
7301            unless (length $token->{data}) {            ## Step 3.
7302              !!!next-token;            splice @{$self->{open_elements}}, $i;
7303              redo B;          }
7304    
7305            !!!next-token;
7306            next B;
7307          } elsif ({
7308                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7309                   }->{$token->{tag_name}}) {
7310            ## has an element in scope
7311            my $i;
7312            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7313              my $node = $self->{open_elements}->[$_];
7314              if ($node->[1] & HEADING_EL) {
7315                !!!cp ('t423');
7316                $i = $_;
7317                last INSCOPE;
7318              } elsif ($node->[1] & SCOPING_EL) {
7319                !!!cp ('t424');
7320                last INSCOPE;
7321              }
7322            } # INSCOPE
7323    
7324            unless (defined $i) { # has an element in scope
7325              !!!cp ('t425.1');
7326              !!!parse-error (type => 'unmatched end tag',
7327                              text => $token->{tag_name}, token => $token);
7328              ## NOTE: Ignore the token.
7329            } else {
7330              ## Step 1. generate implied end tags
7331              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7332                !!!cp ('t422');
7333                pop @{$self->{open_elements}};
7334            }            }
7335              
7336              ## Step 2.
7337              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7338                      ne $token->{tag_name}) {
7339                !!!cp ('t425');
7340                !!!parse-error (type => 'unmatched end tag',
7341                                text => $token->{tag_name}, token => $token);
7342              } else {
7343                !!!cp ('t426');
7344              }
7345    
7346              ## Step 3.
7347              splice @{$self->{open_elements}}, $i;
7348          }          }
7349            
7350            !!!next-token;
7351            next B;
7352          } elsif ($token->{tag_name} eq 'p') {
7353            ## has an element in scope
7354            my $i;
7355            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7356              my $node = $self->{open_elements}->[$_];
7357              if ($node->[1] & P_EL) {
7358                !!!cp ('t410.1');
7359                $i = $_;
7360                last INSCOPE;
7361              } elsif ($node->[1] & SCOPING_EL) {
7362                !!!cp ('t411.1');
7363                last INSCOPE;
7364              }
7365            } # INSCOPE
7366    
7367          !!!parse-error (type => 'after html:#character');          if (defined $i) {
7368          $phase = 'main';            if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7369          ## reprocess                    ne $token->{tag_name}) {
7370          redo B;              !!!cp ('t412.1');
7371        } elsif ($token->{type} eq 'start tag' or              !!!parse-error (type => 'not closed',
7372                 $token->{type} eq 'end tag') {                              text => $self->{open_elements}->[-1]->[0]
7373          !!!parse-error (type => 'after html:'.($token->{type} eq 'end tag' ? '/' : '').$token->{tag_name});                                  ->manakai_local_name,
7374          $phase = 'main';                              token => $token);
7375          ## reprocess            } else {
7376          redo B;              !!!cp ('t414.1');
7377        } elsif ($token->{type} eq 'end-of-file') {            }
7378          ## Stop parsing  
7379          last B;            splice @{$self->{open_elements}}, $i;
7380            } else {
7381              !!!cp ('t413.1');
7382              !!!parse-error (type => 'unmatched end tag',
7383                              text => $token->{tag_name}, token => $token);
7384    
7385              !!!cp ('t415.1');
7386              ## As if <p>, then reprocess the current token
7387              my $el;
7388              !!!create-element ($el, $HTML_NS, 'p',, $token);
7389              $insert->($el);
7390              ## NOTE: Not inserted into |$self->{open_elements}|.
7391            }
7392    
7393            !!!next-token;
7394            next B;
7395          } elsif ({
7396                    a => 1,
7397                    b => 1, big => 1, em => 1, font => 1, i => 1,
7398                    nobr => 1, s => 1, small => 1, strile => 1,
7399                    strong => 1, tt => 1, u => 1,
7400                   }->{$token->{tag_name}}) {
7401            !!!cp ('t427');
7402            $formatting_end_tag->($token);
7403            next B;
7404          } elsif ($token->{tag_name} eq 'br') {
7405            !!!cp ('t428');
7406            !!!parse-error (type => 'unmatched end tag',
7407                            text => 'br', token => $token);
7408    
7409            ## As if <br>
7410            $reconstruct_active_formatting_elements->($insert_to_current);
7411            
7412            my $el;
7413            !!!create-element ($el, $HTML_NS, 'br',, $token);
7414            $insert->($el);
7415            
7416            ## Ignore the token.
7417            !!!next-token;
7418            next B;
7419          } elsif ({
7420                    caption => 1, col => 1, colgroup => 1, frame => 1,
7421                    frameset => 1, head => 1, option => 1, optgroup => 1,
7422                    tbody => 1, td => 1, tfoot => 1, th => 1,
7423                    thead => 1, tr => 1,
7424                    area => 1, basefont => 1, bgsound => 1,
7425                    embed => 1, hr => 1, iframe => 1, image => 1,
7426                    img => 1, input => 1, isindex => 1, noembed => 1,
7427                    noframes => 1, param => 1, select => 1, spacer => 1,
7428                    table => 1, textarea => 1, wbr => 1,
7429                    noscript => 0, ## TODO: if scripting is enabled
7430                   }->{$token->{tag_name}}) {
7431            !!!cp ('t429');
7432            !!!parse-error (type => 'unmatched end tag',
7433                            text => $token->{tag_name}, token => $token);
7434            ## Ignore the token
7435            !!!next-token;
7436            next B;
7437            
7438            ## ISSUE: Issue on HTML5 new elements in spec
7439            
7440        } else {        } else {
7441          die "$0: $token->{type}: Unknown token";          ## Step 1
7442            my $node_i = -1;
7443            my $node = $self->{open_elements}->[$node_i];
7444    
7445            ## Step 2
7446            S2: {
7447              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7448                ## Step 1
7449                ## generate implied end tags
7450                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7451                  !!!cp ('t430');
7452                  ## NOTE: |<ruby><rt></ruby>|.
7453                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7454                  ## which seems wrong.
7455                  pop @{$self->{open_elements}};
7456                  $node_i++;
7457                }
7458            
7459                ## Step 2
7460                if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7461                        ne $token->{tag_name}) {
7462                  !!!cp ('t431');
7463                  ## NOTE: <x><y></x>
7464                  !!!parse-error (type => 'not closed',
7465                                  text => $self->{open_elements}->[-1]->[0]
7466                                      ->manakai_local_name,
7467                                  token => $token);
7468                } else {
7469                  !!!cp ('t432');
7470                }
7471                
7472                ## Step 3
7473                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7474    
7475                !!!next-token;
7476                last S2;
7477              } else {
7478                ## Step 3
7479                if (not ($node->[1] & FORMATTING_EL) and
7480                    #not $phrasing_category->{$node->[1]} and
7481                    ($node->[1] & SPECIAL_EL or
7482                     $node->[1] & SCOPING_EL)) {
7483                  !!!cp ('t433');
7484                  !!!parse-error (type => 'unmatched end tag',
7485                                  text => $token->{tag_name}, token => $token);
7486                  ## Ignore the token
7487                  !!!next-token;
7488                  last S2;
7489                }
7490    
7491                !!!cp ('t434');
7492              }
7493              
7494              ## Step 4
7495              $node_i--;
7496              $node = $self->{open_elements}->[$node_i];
7497              
7498              ## Step 5;
7499              redo S2;
7500            } # S2
7501            next B;
7502        }        }
7503      }      }
7504        next B;
7505      } continue { # B
7506        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7507          ## NOTE: The code below is executed in cases where it does not have
7508          ## to be, but it it is harmless even in those cases.
7509          ## has an element in scope
7510          INSCOPE: {
7511            for (reverse 0..$#{$self->{open_elements}}) {
7512              my $node = $self->{open_elements}->[$_];
7513              if ($node->[1] & FOREIGN_EL) {
7514                last INSCOPE;
7515              } elsif ($node->[1] & SCOPING_EL) {
7516                last;
7517              }
7518            }
7519            
7520            ## NOTE: No foreign element in scope.
7521            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7522          } # INSCOPE
7523        }
7524    } # B    } # B
7525    
7526    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 5197  sub _tree_construction_main ($) { Line 7528  sub _tree_construction_main ($) {
7528    ## TODO: script stuffs    ## TODO: script stuffs
7529  } # _tree_construct_main  } # _tree_construct_main
7530    
7531  sub set_inner_html ($$$) {  sub set_inner_html ($$$;$) {
7532    my $class = shift;    my $class = shift;
7533    my $node = shift;    my $node = shift;
7534    my $s = \$_[0];    my $s = \$_[0];
7535    my $onerror = $_[1];    my $onerror = $_[1];
7536      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7537    
7538      ## ISSUE: Should {confident} be true?
7539    
7540    my $nt = $node->node_type;    my $nt = $node->node_type;
7541    if ($nt == 9) {    if ($nt == 9) {
# Line 5218  sub set_inner_html ($$$) { Line 7552  sub set_inner_html ($$$) {
7552      }      }
7553    
7554      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7555      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($$s => $node, $onerror, $get_wrapper);
7556    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7557      ## TODO: If non-html element      ## TODO: If non-html element
7558    
7559      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7560    
7561    ## TODO: Support for $get_wrapper
7562    
7563      ## Step 1 # MUST      ## Step 1 # MUST
7564      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7565      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5231  sub set_inner_html ($$$) { Line 7567  sub set_inner_html ($$$) {
7567      my $p = $class->new;      my $p = $class->new;
7568      $p->{document} = $doc;      $p->{document} = $doc;
7569    
7570      ## Step 9 # MUST      ## Step 8 # MUST
7571      my $i = 0;      my $i = 0;
7572      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
7573      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
7574      $p->{set_next_input_character} = sub {      $p->{set_next_char} = sub {
7575        my $self = shift;        my $self = shift;
7576    
7577        pop @{$self->{prev_input_character}};        pop @{$self->{prev_char}};
7578        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        unshift @{$self->{prev_char}}, $self->{next_char};
7579    
7580          $self->{next_char} = -1 and return if $i >= length $$s;
7581          $self->{next_char} = ord substr $$s, $i++, 1;
7582    
7583          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7584          $p->{column}++;
7585    
7586        $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($self->{next_char} == 0x000A) { # LF
7587        $self->{next_input_character} = ord substr $$s, $i++, 1;          $p->{line}++;
7588        $column++;          $p->{column} = 0;
7589            !!!cp ('i1');
7590        if ($self->{next_input_character} == 0x000A) { # LF        } elsif ($self->{next_char} == 0x000D) { # CR
         $line++;  
         $column = 0;  
       } elsif ($self->{next_input_character} == 0x000D) { # CR  
7591          $i++ if substr ($$s, $i, 1) eq "\x0A";          $i++ if substr ($$s, $i, 1) eq "\x0A";
7592          $self->{next_input_character} = 0x000A; # LF # MUST          $self->{next_char} = 0x000A; # LF # MUST
7593          $line++;          $p->{line}++;
7594          $column = 0;          $p->{column} = 0;
7595        } elsif ($self->{next_input_character} > 0x10FFFF) {          !!!cp ('i2');
7596          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        } elsif ($self->{next_char} > 0x10FFFF) {
7597        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7598            !!!cp ('i3');
7599          } elsif ($self->{next_char} == 0x0000) { # NULL
7600            !!!cp ('i4');
7601          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7602          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_char} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7603          } elsif ($self->{next_char} <= 0x0008 or
7604                   (0x000E <= $self->{next_char} and
7605                    $self->{next_char} <= 0x001F) or
7606                   (0x007F <= $self->{next_char} and
7607                    $self->{next_char} <= 0x009F) or
7608                   (0xD800 <= $self->{next_char} and
7609                    $self->{next_char} <= 0xDFFF) or
7610                   (0xFDD0 <= $self->{next_char} and
7611                    $self->{next_char} <= 0xFDDF) or
7612                   {
7613                    0xFFFE => 1, 0xFFFF => 1, 0x1FFFE => 1, 0x1FFFF => 1,
7614                    0x2FFFE => 1, 0x2FFFF => 1, 0x3FFFE => 1, 0x3FFFF => 1,
7615                    0x4FFFE => 1, 0x4FFFF => 1, 0x5FFFE => 1, 0x5FFFF => 1,
7616                    0x6FFFE => 1, 0x6FFFF => 1, 0x7FFFE => 1, 0x7FFFF => 1,
7617                    0x8FFFE => 1, 0x8FFFF => 1, 0x9FFFE => 1, 0x9FFFF => 1,
7618                    0xAFFFE => 1, 0xAFFFF => 1, 0xBFFFE => 1, 0xBFFFF => 1,
7619                    0xCFFFE => 1, 0xCFFFF => 1, 0xDFFFE => 1, 0xDFFFF => 1,
7620                    0xEFFFE => 1, 0xEFFFF => 1, 0xFFFFE => 1, 0xFFFFF => 1,
7621                    0x10FFFE => 1, 0x10FFFF => 1,
7622                   }->{$self->{next_char}}) {
7623            !!!cp ('i4.1');
7624            if ($self->{next_char} < 0x10000) {
7625              !!!parse-error (type => 'control char',
7626                              text => (sprintf 'U+%04X', $self->{next_char}));
7627            } else {
7628              !!!parse-error (type => 'control char',
7629                              text => (sprintf 'U-%08X', $self->{next_char}));
7630            }
7631        }        }
7632      };      };
7633      $p->{prev_input_character} = [-1, -1, -1];      $p->{prev_char} = [-1, -1, -1];
7634      $p->{next_input_character} = -1;      $p->{next_char} = -1;
7635            
7636      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7637        my (%opt) = @_;        my (%opt) = @_;
7638        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
7639          my $column = $opt{column};
7640          if (defined $opt{token} and defined $opt{token}->{line}) {
7641            $line = $opt{token}->{line};
7642            $column = $opt{token}->{column};
7643          }
7644          warn "Parse error ($opt{type}) at line $line column $column\n";
7645      };      };
7646      $p->{parse_error} = sub {      $p->{parse_error} = sub {
7647        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7648      };      };
7649            
7650      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7651      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7652    
7653      ## Step 2      ## Step 2
7654      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
7655      $p->{content_model_flag} = {      $p->{content_model} = {
7656        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
7657        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
7658        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
7659        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
7660        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
7661        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
7662        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
7663        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
7664        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
7665        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
7666      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
7667         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
7668            unless defined $p->{content_model};
7669            ## ISSUE: What is "the name of the element"? local name?
7670    
7671      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
7672          ## TODO: Foreign element OK?
7673    
7674      ## Step 4      ## Step 3
7675      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
7676        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
7677    
7678      ## Step 5 # MUST      ## Step 4 # MUST
7679      $doc->append_child ($root);      $doc->append_child ($root);
7680    
7681      ## Step 6 # MUST      ## Step 5 # MUST
7682      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
7683    
7684      undef $p->{head_element};      undef $p->{head_element};
7685    
7686      ## Step 7 # MUST      ## Step 6 # MUST
7687      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
7688    
7689      ## Step 8 # MUST      ## Step 7 # MUST
7690      my $anode = $node;      my $anode = $node;
7691      AN: while (defined $anode) {      AN: while (defined $anode) {
7692        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
7693          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
7694          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
7695            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
7696                !!!cp ('i5');
7697              $p->{form_element} = $anode;              $p->{form_element} = $anode;
7698              last AN;              last AN;
7699            }            }
# Line 5322  sub set_inner_html ($$$) { Line 7702  sub set_inner_html ($$$) {
7702        $anode = $anode->parent_node;        $anode = $anode->parent_node;
7703      } # AN      } # AN
7704            
7705      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
7706      {      {
7707        my $self = $p;        my $self = $p;
7708        !!!next-token;        !!!next-token;
7709      }      }
7710      $p->_tree_construction_main;      $p->_tree_construction_main;
7711    
7712      ## Step 11 # MUST      ## Step 10 # MUST
7713      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
7714      for (@cn) {      for (@cn) {
7715        $node->remove_child ($_);        $node->remove_child ($_);
7716      }      }
7717      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
7718    
7719      ## Step 12 # MUST      ## Step 11 # MUST
7720      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
7721      for (@cn) {      for (@cn) {
7722        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5346  sub set_inner_html ($$$) { Line 7725  sub set_inner_html ($$$) {
7725      ## ISSUE: mutation events?      ## ISSUE: mutation events?
7726    
7727      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
7728    
7729        delete $p->{parse_error}; # delete loop
7730    } else {    } else {
7731      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";
7732    }    }
# Line 5353  sub set_inner_html ($$$) { Line 7734  sub set_inner_html ($$$) {
7734    
7735  } # tree construction stage  } # tree construction stage
7736    
7737  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
7738    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  
7739    
7740  1;  1;
7741  # $Date$  # $Date$

Legend:
Removed from v.1.31  
changed lines
  Added in v.1.165

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24