/[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.25 by wakaba, Sun Jun 24 05:12:11 2007 UTC revision 1.191 by wakaba, Mon Sep 22 06:04:29 2008 UTC
# Line 1  Line 1 
1  package Whatpm::HTML;  package Whatpm::HTML;
2  use strict;  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4    use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  my $permitted_slash_tag_name = {  require IO::Handle;
23    base => 1,  
24    link => 1,  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    meta => 1,  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26    hr => 1,  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    br => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    img=> 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    embed => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    param => 1,  
31    area => 1,  sub A_EL () { 0b1 }
32    col => 1,  sub ADDRESS_EL () { 0b10 }
33    input => 1,  sub BODY_EL () { 0b100 }
34    sub BUTTON_EL () { 0b1000 }
35    sub CAPTION_EL () { 0b10000 }
36    sub DD_EL () { 0b100000 }
37    sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL
70    ## is used in "generate implied end tags" implementation (search for the
71    ## function mae).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      P_EL |
77      RUBY_COMPONENT_EL
78    }
79    
80    ## NOTE: Used in </body> and EOF algorithms.
81    sub ALL_END_TAG_OPTIONAL_EL () {
82      DD_EL |
83      DT_EL |
84      LI_EL |
85      P_EL |
86    
87      BODY_EL |
88      HTML_EL |
89      TABLE_CELL_EL |
90      TABLE_ROW_EL |
91      TABLE_ROW_GROUP_EL
92    }
93    
94    sub SCOPING_EL () {
95      BUTTON_EL |
96      CAPTION_EL |
97      HTML_EL |
98      TABLE_EL |
99      TABLE_CELL_EL |
100      MISC_SCOPING_EL
101    }
102    
103    sub TABLE_SCOPING_EL () {
104      HTML_EL |
105      TABLE_EL
106    }
107    
108    sub TABLE_ROWS_SCOPING_EL () {
109      HTML_EL |
110      TABLE_ROW_GROUP_EL
111    }
112    
113    sub TABLE_ROW_SCOPING_EL () {
114      HTML_EL |
115      TABLE_ROW_EL
116    }
117    
118    sub SPECIAL_EL () {
119      ADDRESS_EL |
120      BODY_EL |
121      DIV_EL |
122    
123      DD_EL |
124      DT_EL |
125      LI_EL |
126      P_EL |
127    
128      FORM_EL |
129      FRAMESET_EL |
130      HEADING_EL |
131      OPTION_EL |
132      OPTGROUP_EL |
133      SELECT_EL |
134      TABLE_ROW_EL |
135      TABLE_ROW_GROUP_EL |
136      MISC_SPECIAL_EL
137    }
138    
139    my $el_category = {
140      a => A_EL | FORMATTING_EL,
141      address => ADDRESS_EL,
142      applet => MISC_SCOPING_EL,
143      area => MISC_SPECIAL_EL,
144      b => FORMATTING_EL,
145      base => MISC_SPECIAL_EL,
146      basefont => MISC_SPECIAL_EL,
147      bgsound => MISC_SPECIAL_EL,
148      big => FORMATTING_EL,
149      blockquote => MISC_SPECIAL_EL,
150      body => BODY_EL,
151      br => MISC_SPECIAL_EL,
152      button => BUTTON_EL,
153      caption => CAPTION_EL,
154      center => MISC_SPECIAL_EL,
155      col => MISC_SPECIAL_EL,
156      colgroup => MISC_SPECIAL_EL,
157      dd => DD_EL,
158      dir => MISC_SPECIAL_EL,
159      div => DIV_EL,
160      dl => MISC_SPECIAL_EL,
161      dt => DT_EL,
162      em => FORMATTING_EL,
163      embed => MISC_SPECIAL_EL,
164      fieldset => MISC_SPECIAL_EL,
165      font => FORMATTING_EL,
166      form => FORM_EL,
167      frame => MISC_SPECIAL_EL,
168      frameset => FRAMESET_EL,
169      h1 => HEADING_EL,
170      h2 => HEADING_EL,
171      h3 => HEADING_EL,
172      h4 => HEADING_EL,
173      h5 => HEADING_EL,
174      h6 => HEADING_EL,
175      head => MISC_SPECIAL_EL,
176      hr => MISC_SPECIAL_EL,
177      html => HTML_EL,
178      i => FORMATTING_EL,
179      iframe => MISC_SPECIAL_EL,
180      img => MISC_SPECIAL_EL,
181      input => MISC_SPECIAL_EL,
182      isindex => MISC_SPECIAL_EL,
183      li => LI_EL,
184      link => MISC_SPECIAL_EL,
185      listing => MISC_SPECIAL_EL,
186      marquee => MISC_SCOPING_EL,
187      menu => MISC_SPECIAL_EL,
188      meta => MISC_SPECIAL_EL,
189      nobr => NOBR_EL | FORMATTING_EL,
190      noembed => MISC_SPECIAL_EL,
191      noframes => MISC_SPECIAL_EL,
192      noscript => MISC_SPECIAL_EL,
193      object => MISC_SCOPING_EL,
194      ol => MISC_SPECIAL_EL,
195      optgroup => OPTGROUP_EL,
196      option => OPTION_EL,
197      p => P_EL,
198      param => MISC_SPECIAL_EL,
199      plaintext => MISC_SPECIAL_EL,
200      pre => MISC_SPECIAL_EL,
201      rp => RUBY_COMPONENT_EL,
202      rt => RUBY_COMPONENT_EL,
203      ruby => RUBY_EL,
204      s => FORMATTING_EL,
205      script => MISC_SPECIAL_EL,
206      select => SELECT_EL,
207      small => FORMATTING_EL,
208      spacer => MISC_SPECIAL_EL,
209      strike => FORMATTING_EL,
210      strong => FORMATTING_EL,
211      style => MISC_SPECIAL_EL,
212      table => TABLE_EL,
213      tbody => TABLE_ROW_GROUP_EL,
214      td => TABLE_CELL_EL,
215      textarea => MISC_SPECIAL_EL,
216      tfoot => TABLE_ROW_GROUP_EL,
217      th => TABLE_CELL_EL,
218      thead => TABLE_ROW_GROUP_EL,
219      title => MISC_SPECIAL_EL,
220      tr => TABLE_ROW_EL,
221      tt => FORMATTING_EL,
222      u => FORMATTING_EL,
223      ul => MISC_SPECIAL_EL,
224      wbr => MISC_SPECIAL_EL,
225    };
226    
227    my $el_category_f = {
228      $MML_NS => {
229        'annotation-xml' => MML_AXML_EL,
230        mi => FOREIGN_FLOW_CONTENT_EL,
231        mo => FOREIGN_FLOW_CONTENT_EL,
232        mn => FOREIGN_FLOW_CONTENT_EL,
233        ms => FOREIGN_FLOW_CONTENT_EL,
234        mtext => FOREIGN_FLOW_CONTENT_EL,
235      },
236      $SVG_NS => {
237        foreignObject => FOREIGN_FLOW_CONTENT_EL,
238        desc => FOREIGN_FLOW_CONTENT_EL,
239        title => FOREIGN_FLOW_CONTENT_EL,
240      },
241      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
242    };
243    
244    my $svg_attr_name = {
245      attributename => 'attributeName',
246      attributetype => 'attributeType',
247      basefrequency => 'baseFrequency',
248      baseprofile => 'baseProfile',
249      calcmode => 'calcMode',
250      clippathunits => 'clipPathUnits',
251      contentscripttype => 'contentScriptType',
252      contentstyletype => 'contentStyleType',
253      diffuseconstant => 'diffuseConstant',
254      edgemode => 'edgeMode',
255      externalresourcesrequired => 'externalResourcesRequired',
256      filterres => 'filterRes',
257      filterunits => 'filterUnits',
258      glyphref => 'glyphRef',
259      gradienttransform => 'gradientTransform',
260      gradientunits => 'gradientUnits',
261      kernelmatrix => 'kernelMatrix',
262      kernelunitlength => 'kernelUnitLength',
263      keypoints => 'keyPoints',
264      keysplines => 'keySplines',
265      keytimes => 'keyTimes',
266      lengthadjust => 'lengthAdjust',
267      limitingconeangle => 'limitingConeAngle',
268      markerheight => 'markerHeight',
269      markerunits => 'markerUnits',
270      markerwidth => 'markerWidth',
271      maskcontentunits => 'maskContentUnits',
272      maskunits => 'maskUnits',
273      numoctaves => 'numOctaves',
274      pathlength => 'pathLength',
275      patterncontentunits => 'patternContentUnits',
276      patterntransform => 'patternTransform',
277      patternunits => 'patternUnits',
278      pointsatx => 'pointsAtX',
279      pointsaty => 'pointsAtY',
280      pointsatz => 'pointsAtZ',
281      preservealpha => 'preserveAlpha',
282      preserveaspectratio => 'preserveAspectRatio',
283      primitiveunits => 'primitiveUnits',
284      refx => 'refX',
285      refy => 'refY',
286      repeatcount => 'repeatCount',
287      repeatdur => 'repeatDur',
288      requiredextensions => 'requiredExtensions',
289      requiredfeatures => 'requiredFeatures',
290      specularconstant => 'specularConstant',
291      specularexponent => 'specularExponent',
292      spreadmethod => 'spreadMethod',
293      startoffset => 'startOffset',
294      stddeviation => 'stdDeviation',
295      stitchtiles => 'stitchTiles',
296      surfacescale => 'surfaceScale',
297      systemlanguage => 'systemLanguage',
298      tablevalues => 'tableValues',
299      targetx => 'targetX',
300      targety => 'targetY',
301      textlength => 'textLength',
302      viewbox => 'viewBox',
303      viewtarget => 'viewTarget',
304      xchannelselector => 'xChannelSelector',
305      ychannelselector => 'yChannelSelector',
306      zoomandpan => 'zoomAndPan',
307  };  };
308    
309  my $c1_entity_char = {  my $foreign_attr_xname = {
310      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
311      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
312      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
313      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
314      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
315      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
316      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
317      'xml:base' => [$XML_NS, ['xml', 'base']],
318      'xml:lang' => [$XML_NS, ['xml', 'lang']],
319      'xml:space' => [$XML_NS, ['xml', 'space']],
320      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
321      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
322    };
323    
324    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
325    
326    my $charref_map = {
327      0x0D => 0x000A,
328    0x80 => 0x20AC,    0x80 => 0x20AC,
329    0x81 => 0xFFFD,    0x81 => 0xFFFD,
330    0x82 => 0x201A,    0x82 => 0x201A,
# Line 54  my $c1_entity_char = { Line 357  my $c1_entity_char = {
357    0x9D => 0xFFFD,    0x9D => 0xFFFD,
358    0x9E => 0x017E,    0x9E => 0x017E,
359    0x9F => 0x0178,    0x9F => 0x0178,
360  }; # $c1_entity_char  }; # $charref_map
361    $charref_map->{$_} = 0xFFFD
362        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
363            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
364            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
365            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
366            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
367            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
368            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
369    
370  my $special_category = {  sub parse_byte_string ($$$$;$) {
371    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,    my $self = shift;
372    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,    my $charset_name = shift;
373    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
374    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
375    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,  } # parse_byte_string
376    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,  
377    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,  sub parse_byte_stream ($$$$;$$) {
378    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
379    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,    my $self = ref $_[0] ? shift : shift->new;
380    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,    my $charset_name = shift;
381  };    my $byte_stream = $_[0];
382  my $scoping_category = {  
383    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $onerror = $_[2] || sub {
384    table => 1, td => 1, th => 1,      my (%opt) = @_;
385  };      warn "Parse error ($opt{type})\n";
386  my $formatting_category = {    };
387    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,    $self->{parse_error} = $onerror; # updated later by parse_char_string
388    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
389  };    my $get_wrapper = $_[3] || sub ($) {
390  # $phrasing_category: all other elements      return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
391      };
392    
393      ## HTML5 encoding sniffing algorithm
394      require Message::Charset::Info;
395      my $charset;
396      my $buffer;
397      my ($char_stream, $e_status);
398    
399      SNIFFING: {
400        ## NOTE: By setting |allow_fallback| option true when the
401        ## |get_decode_handle| method is invoked, we ignore what the HTML5
402        ## spec requires, i.e. unsupported encoding should be ignored.
403          ## TODO: We should not do this unless the parser is invoked
404          ## in the conformance checking mode, in which this behavior
405          ## would be useful.
406    
407        ## Step 1
408        if (defined $charset_name) {
409          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
410              ## TODO: Is this ok?  Transfer protocol's parameter should be
411              ## interpreted in its semantics?
412    
413          ($char_stream, $e_status) = $charset->get_decode_handle
414              ($byte_stream, allow_error_reporting => 1,
415               allow_fallback => 1);
416          if ($char_stream) {
417            $self->{confident} = 1;
418            last SNIFFING;
419          } else {
420            !!!parse-error (type => 'charset:not supported',
421                            layer => 'encode',
422                            line => 1, column => 1,
423                            value => $charset_name,
424                            level => $self->{level}->{uncertain});
425          }
426        }
427    
428        ## Step 2
429        my $byte_buffer = '';
430        for (1..1024) {
431          my $char = $byte_stream->getc;
432          last unless defined $char;
433          $byte_buffer .= $char;
434        } ## TODO: timeout
435    
436        ## Step 3
437        if ($byte_buffer =~ /^\xFE\xFF/) {
438          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
439          ($char_stream, $e_status) = $charset->get_decode_handle
440              ($byte_stream, allow_error_reporting => 1,
441               allow_fallback => 1, byte_buffer => \$byte_buffer);
442          $self->{confident} = 1;
443          last SNIFFING;
444        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
445          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
446          ($char_stream, $e_status) = $charset->get_decode_handle
447              ($byte_stream, allow_error_reporting => 1,
448               allow_fallback => 1, byte_buffer => \$byte_buffer);
449          $self->{confident} = 1;
450          last SNIFFING;
451        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
452          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
453          ($char_stream, $e_status) = $charset->get_decode_handle
454              ($byte_stream, allow_error_reporting => 1,
455               allow_fallback => 1, byte_buffer => \$byte_buffer);
456          $self->{confident} = 1;
457          last SNIFFING;
458        }
459    
460        ## Step 4
461        ## TODO: <meta charset>
462    
463        ## Step 5
464        ## TODO: from history
465    
466        ## Step 6
467        require Whatpm::Charset::UniversalCharDet;
468        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
469            ($byte_buffer);
470        if (defined $charset_name) {
471          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
472    
473          ## ISSUE: Unsupported encoding is not ignored according to the spec.
474          require Whatpm::Charset::DecodeHandle;
475          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
476              ($byte_stream);
477          ($char_stream, $e_status) = $charset->get_decode_handle
478              ($buffer, allow_error_reporting => 1,
479               allow_fallback => 1, byte_buffer => \$byte_buffer);
480          if ($char_stream) {
481            $buffer->{buffer} = $byte_buffer;
482            !!!parse-error (type => 'sniffing:chardet',
483                            text => $charset_name,
484                            level => $self->{level}->{info},
485                            layer => 'encode',
486                            line => 1, column => 1);
487            $self->{confident} = 0;
488            last SNIFFING;
489          }
490        }
491    
492        ## Step 7: default
493        ## TODO: Make this configurable.
494        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
495            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
496            ## detectable in the step 6.
497        require Whatpm::Charset::DecodeHandle;
498        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
499            ($byte_stream);
500        ($char_stream, $e_status)
501            = $charset->get_decode_handle ($buffer,
502                                           allow_error_reporting => 1,
503                                           allow_fallback => 1,
504                                           byte_buffer => \$byte_buffer);
505        $buffer->{buffer} = $byte_buffer;
506        !!!parse-error (type => 'sniffing:default',
507                        text => 'windows-1252',
508                        level => $self->{level}->{info},
509                        line => 1, column => 1,
510                        layer => 'encode');
511        $self->{confident} = 0;
512      } # SNIFFING
513    
514      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
515        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
516        !!!parse-error (type => 'chardecode:fallback',
517                        #text => $self->{input_encoding},
518                        level => $self->{level}->{uncertain},
519                        line => 1, column => 1,
520                        layer => 'encode');
521      } elsif (not ($e_status &
522                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
523        $self->{input_encoding} = $charset->get_iana_name;
524        !!!parse-error (type => 'chardecode:no error',
525                        text => $self->{input_encoding},
526                        level => $self->{level}->{uncertain},
527                        line => 1, column => 1,
528                        layer => 'encode');
529      } else {
530        $self->{input_encoding} = $charset->get_iana_name;
531      }
532    
533      $self->{change_encoding} = sub {
534        my $self = shift;
535        $charset_name = shift;
536        my $token = shift;
537    
538        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
539        ($char_stream, $e_status) = $charset->get_decode_handle
540            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
541             byte_buffer => \ $buffer->{buffer});
542        
543        if ($char_stream) { # if supported
544          ## "Change the encoding" algorithm:
545    
546          ## Step 1    
547          if ($charset->{category} &
548              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
549            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
550            ($char_stream, $e_status) = $charset->get_decode_handle
551                ($byte_stream,
552                 byte_buffer => \ $buffer->{buffer});
553          }
554          $charset_name = $charset->get_iana_name;
555          
556          ## Step 2
557          if (defined $self->{input_encoding} and
558              $self->{input_encoding} eq $charset_name) {
559            !!!parse-error (type => 'charset label:matching',
560                            text => $charset_name,
561                            level => $self->{level}->{info});
562            $self->{confident} = 1;
563            return;
564          }
565    
566          !!!parse-error (type => 'charset label detected',
567                          text => $self->{input_encoding},
568                          value => $charset_name,
569                          level => $self->{level}->{warn},
570                          token => $token);
571          
572          ## Step 3
573          # if (can) {
574            ## change the encoding on the fly.
575            #$self->{confident} = 1;
576            #return;
577          # }
578          
579          ## Step 4
580          throw Whatpm::HTML::RestartParser ();
581        }
582      }; # $self->{change_encoding}
583    
584      my $char_onerror = sub {
585        my (undef, $type, %opt) = @_;
586        !!!parse-error (layer => 'encode',
587                        line => $self->{line}, column => $self->{column} + 1,
588                        %opt, type => $type);
589        if ($opt{octets}) {
590          ${$opt{octets}} = "\x{FFFD}"; # relacement character
591        }
592      };
593    
594      my $wrapped_char_stream = $get_wrapper->($char_stream);
595      $wrapped_char_stream->onerror ($char_onerror);
596    
597      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
598      my $return;
599      try {
600        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
601      } catch Whatpm::HTML::RestartParser with {
602        ## NOTE: Invoked after {change_encoding}.
603    
604        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
605          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
606          !!!parse-error (type => 'chardecode:fallback',
607                          level => $self->{level}->{uncertain},
608                          #text => $self->{input_encoding},
609                          line => 1, column => 1,
610                          layer => 'encode');
611        } elsif (not ($e_status &
612                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
613          $self->{input_encoding} = $charset->get_iana_name;
614          !!!parse-error (type => 'chardecode:no error',
615                          text => $self->{input_encoding},
616                          level => $self->{level}->{uncertain},
617                          line => 1, column => 1,
618                          layer => 'encode');
619        } else {
620          $self->{input_encoding} = $charset->get_iana_name;
621        }
622        $self->{confident} = 1;
623    
624        $wrapped_char_stream = $get_wrapper->($char_stream);
625        $wrapped_char_stream->onerror ($char_onerror);
626    
627        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
628      };
629      return $return;
630    } # parse_byte_stream
631    
632    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
633    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
634    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
635    ## because the core part of our HTML parser expects a string of character,
636    ## not a string of bytes or code units or anything which might contain a BOM.
637    ## Therefore, any parser interface that accepts a string of bytes,
638    ## such as |parse_byte_string| in this module, must ensure that it does
639    ## strip the BOM and never strip any ZWNBSP.
640    
641  sub parse_string ($$$;$) {  sub parse_char_string ($$$;$$) {
642    my $self = shift->new;    #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
643    my $s = \$_[0];    my $self = shift;
644      my $s = ref $_[0] ? $_[0] : \($_[0]);
645      require Whatpm::Charset::DecodeHandle;
646      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
647      return $self->parse_char_stream ($input, @_[1..$#_]);
648    } # parse_char_string
649    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
650    
651    sub parse_char_stream ($$$;$$) {
652      my $self = ref $_[0] ? shift : shift->new;
653      my $input = $_[0];
654    $self->{document} = $_[1];    $self->{document} = $_[1];
655      @{$self->{document}->child_nodes} = ();
656    
657    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
658    
659    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
660    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
661    my $column = 0;        if defined $self->{input_encoding};
662    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
663    
664      $self->{line_prev} = $self->{line} = 1;
665      $self->{column_prev} = -1;
666      $self->{column} = 0;
667      $self->{set_nc} = sub {
668      my $self = shift;      my $self = shift;
669    
670      pop @{$self->{prev_input_character}};      my $char = '';
671      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
672          $char = $self->{next_nc};
673          delete $self->{next_nc};
674          $self->{nc} = ord $char;
675        } else {
676          $self->{char_buffer} = '';
677          $self->{char_buffer_pos} = 0;
678    
679      $self->{next_input_character} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
680      $self->{next_input_character} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
681      $column++;        if ($count) {
682            $self->{line_prev} = $self->{line};
683            $self->{column_prev} = $self->{column};
684            $self->{column}++;
685            $self->{nc}
686                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
687            return;
688          }
689    
690          if ($input->read ($char, 1)) {
691            $self->{nc} = ord $char;
692          } else {
693            $self->{nc} = -1;
694            return;
695          }
696        }
697    
698        ($self->{line_prev}, $self->{column_prev})
699            = ($self->{line}, $self->{column});
700        $self->{column}++;
701            
702      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
703        $line++;        !!!cp ('j1');
704        $column = 0;        $self->{line}++;
705      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
706        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
707        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
708        $line++;  ## TODO: support for abort/streaming
709        $column = 0;        my $next = '';
710      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
711        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
712      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
713          $self->{nc} = 0x000A; # LF # MUST
714          $self->{line}++;
715          $self->{column} = 0;
716        } elsif ($self->{nc} == 0x0000) { # NULL
717          !!!cp ('j4');
718        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
719        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
720      }      }
721    };    };
722    $self->{prev_input_character} = [-1, -1, -1];  
723    $self->{next_input_character} = -1;    $self->{read_until} = sub {
724        #my ($scalar, $specials_range, $offset) = @_;
725        return 0 if defined $self->{next_nc};
726    
727        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
728        my $offset = $_[2] || 0;
729    
730        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
731          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
732          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
733            substr ($_[0], $offset)
734                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
735            my $count = $+[0] - $-[0];
736            if ($count) {
737              $self->{column} += $count;
738              $self->{char_buffer_pos} += $count;
739              $self->{line_prev} = $self->{line};
740              $self->{column_prev} = $self->{column} - 1;
741              $self->{nc} = -1;
742            }
743            return $count;
744          } else {
745            return 0;
746          }
747        } else {
748          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
749          if ($count) {
750            $self->{column} += $count;
751            $self->{line_prev} = $self->{line};
752            $self->{column_prev} = $self->{column} - 1;
753            $self->{nc} = -1;
754          }
755          return $count;
756        }
757      }; # $self->{read_until}
758    
759    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
760      my (%opt) = @_;      my (%opt) = @_;
761      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
762        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
763        warn "Parse error ($opt{type}) at line $line column $column\n";
764    };    };
765    $self->{parse_error} = sub {    $self->{parse_error} = sub {
766      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
767    };    };
768    
769      my $char_onerror = sub {
770        my (undef, $type, %opt) = @_;
771        !!!parse-error (layer => 'encode',
772                        line => $self->{line}, column => $self->{column} + 1,
773                        %opt, type => $type);
774      }; # $char_onerror
775    
776      if ($_[3]) {
777        $input = $_[3]->($input);
778        $input->onerror ($char_onerror);
779      } else {
780        $input->onerror ($char_onerror) unless defined $input->onerror;
781      }
782    
783    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
784    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
785    $self->_construct_tree;    $self->_construct_tree;
786    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
787    
788      delete $self->{parse_error}; # remove loop
789    
790    return $self->{document};    return $self->{document};
791  } # parse_string  } # parse_char_stream
792    
793  sub new ($) {  sub new ($) {
794    my $class = shift;    my $class = shift;
795    my $self = bless {}, $class;    my $self = bless {
796    $self->{set_next_input_character} = sub {      level => {must => 'm',
797      $self->{next_input_character} = -1;                should => 's',
798                  warn => 'w',
799                  info => 'i',
800                  uncertain => 'u'},
801      }, $class;
802      $self->{set_nc} = sub {
803        $self->{nc} = -1;
804    };    };
805    $self->{parse_error} = sub {    $self->{parse_error} = sub {
806      #      #
807    };    };
808      $self->{change_encoding} = sub {
809        # if ($_[0] is a supported encoding) {
810        #   run "change the encoding" algorithm;
811        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
812        # }
813      };
814      $self->{application_cache_selection} = sub {
815        #
816      };
817    return $self;    return $self;
818  } # new  } # new
819    
820    sub CM_ENTITY () { 0b001 } # & markup in data
821    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
822    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
823    
824    sub PLAINTEXT_CONTENT_MODEL () { 0 }
825    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
826    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
827    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
828    
829    sub DATA_STATE () { 0 }
830    #sub ENTITY_DATA_STATE () { 1 }
831    sub TAG_OPEN_STATE () { 2 }
832    sub CLOSE_TAG_OPEN_STATE () { 3 }
833    sub TAG_NAME_STATE () { 4 }
834    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
835    sub ATTRIBUTE_NAME_STATE () { 6 }
836    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
837    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
838    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
839    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
840    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
841    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
842    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
843    sub COMMENT_START_STATE () { 14 }
844    sub COMMENT_START_DASH_STATE () { 15 }
845    sub COMMENT_STATE () { 16 }
846    sub COMMENT_END_STATE () { 17 }
847    sub COMMENT_END_DASH_STATE () { 18 }
848    sub BOGUS_COMMENT_STATE () { 19 }
849    sub DOCTYPE_STATE () { 20 }
850    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
851    sub DOCTYPE_NAME_STATE () { 22 }
852    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
853    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
854    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
855    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
856    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
857    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
858    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
859    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
860    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
861    sub BOGUS_DOCTYPE_STATE () { 32 }
862    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
863    sub SELF_CLOSING_START_TAG_STATE () { 34 }
864    sub CDATA_SECTION_STATE () { 35 }
865    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
866    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
867    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
868    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
869    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
870    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
871    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
872    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
873    ## NOTE: "Entity data state", "entity in attribute value state", and
874    ## "consume a character reference" algorithm are jointly implemented
875    ## using the following six states:
876    sub ENTITY_STATE () { 44 }
877    sub ENTITY_HASH_STATE () { 45 }
878    sub NCR_NUM_STATE () { 46 }
879    sub HEXREF_X_STATE () { 47 }
880    sub HEXREF_HEX_STATE () { 48 }
881    sub ENTITY_NAME_STATE () { 49 }
882    sub PCDATA_STATE () { 50 } # "data state" in the spec
883    
884    sub DOCTYPE_TOKEN () { 1 }
885    sub COMMENT_TOKEN () { 2 }
886    sub START_TAG_TOKEN () { 3 }
887    sub END_TAG_TOKEN () { 4 }
888    sub END_OF_FILE_TOKEN () { 5 }
889    sub CHARACTER_TOKEN () { 6 }
890    
891    sub AFTER_HTML_IMS () { 0b100 }
892    sub HEAD_IMS ()       { 0b1000 }
893    sub BODY_IMS ()       { 0b10000 }
894    sub BODY_TABLE_IMS () { 0b100000 }
895    sub TABLE_IMS ()      { 0b1000000 }
896    sub ROW_IMS ()        { 0b10000000 }
897    sub BODY_AFTER_IMS () { 0b100000000 }
898    sub FRAME_IMS ()      { 0b1000000000 }
899    sub SELECT_IMS ()     { 0b10000000000 }
900    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
901        ## NOTE: "in foreign content" insertion mode is special; it is combined
902        ## with the secondary insertion mode.  In this parser, they are stored
903        ## together in the bit-or'ed form.
904    
905    ## NOTE: "initial" and "before html" insertion modes have no constants.
906    
907    ## NOTE: "after after body" insertion mode.
908    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
909    
910    ## NOTE: "after after frameset" insertion mode.
911    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
912    
913    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
914    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
915    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
916    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
917    sub IN_BODY_IM () { BODY_IMS }
918    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
919    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
920    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
921    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
922    sub IN_TABLE_IM () { TABLE_IMS }
923    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
924    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
925    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
926    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
927    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
928    sub IN_COLUMN_GROUP_IM () { 0b10 }
929    
930  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
931    
932  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
933    my $self = shift;    my $self = shift;
934    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
935    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{s_kwd}; # state keyword - initialized when used
936    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    #$self->{entity__value}; # initialized when used
937    undef $self->{current_attribute};    #$self->{entity__match}; # initialized when used
938    undef $self->{last_emitted_start_tag_name};    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
939    undef $self->{last_attribute_value_state};    undef $self->{ct}; # current token
940    $self->{char} = [];    undef $self->{ca}; # current attribute
941    # $self->{next_input_character}    undef $self->{last_stag_name}; # last emitted start tag name
942      #$self->{prev_state}; # initialized when used
943      delete $self->{self_closing};
944      $self->{char_buffer} = '';
945      $self->{char_buffer_pos} = 0;
946      $self->{nc} = -1; # next input character
947      #$self->{next_nc}
948    !!!next-input-character;    !!!next-input-character;
949    $self->{token} = [];    $self->{token} = [];
950    # $self->{escape}    # $self->{escape}
951  } # _initialize_tokenizer  } # _initialize_tokenizer
952    
953  ## A token has:  ## A token has:
954  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
955  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
956  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
957  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
958  ##   ->{system_identifier} (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
959  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{sysid} (DOCTYPE_TOKEN)
960  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
961  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
962    ##        ->{name}
963    ##        ->{value}
964    ##        ->{has_reference} == 1 or 0
965    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
966    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
967    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
968    ##     while the token is pushed back to the stack.
969    
970  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
971    
# Line 179  sub _initialize_tokenizer ($) { Line 975  sub _initialize_tokenizer ($) {
975  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
976  ## and removed from the list.  ## and removed from the list.
977    
978    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
979    ## (This requirement was dropped from HTML5 spec, unfortunately.)
980    
981    my $is_space = {
982      0x0009 => 1, # CHARACTER TABULATION (HT)
983      0x000A => 1, # LINE FEED (LF)
984      #0x000B => 0, # LINE TABULATION (VT)
985      0x000C => 1, # FORM FEED (FF)
986      #0x000D => 1, # CARRIAGE RETURN (CR)
987      0x0020 => 1, # SPACE (SP)
988    };
989    
990  sub _get_next_token ($) {  sub _get_next_token ($) {
991    my $self = shift;    my $self = shift;
992    
993      if ($self->{self_closing}) {
994        !!!parse-error (type => 'nestc', token => $self->{ct});
995        ## NOTE: The |self_closing| flag is only set by start tag token.
996        ## In addition, when a start tag token is emitted, it is always set to
997        ## |ct|.
998        delete $self->{self_closing};
999      }
1000    
1001    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1002        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1003      return shift @{$self->{token}};      return shift @{$self->{token}};
1004    }    }
1005    
1006    A: {    A: {
1007      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1008        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1009          if ($self->{content_model_flag} eq 'PCDATA' or  
1010              $self->{content_model_flag} eq 'RCDATA') {        if ($self->{nc} == 0x0026) { # &
1011            $self->{state} = 'entity data';          !!!cp (0.1);
1012            ## NOTE: In the spec, the tokenizer is switched to the
1013            ## "entity data state".  In this implementation, the tokenizer
1014            ## is switched to the |ENTITY_STATE|, which is an implementation
1015            ## of the "consume a character reference" algorithm.
1016            $self->{entity_add} = -1;
1017            $self->{prev_state} = DATA_STATE;
1018            $self->{state} = ENTITY_STATE;
1019            !!!next-input-character;
1020            redo A;
1021          } elsif ($self->{nc} == 0x003C) { # <
1022            !!!cp (0.2);
1023            $self->{state} = TAG_OPEN_STATE;
1024            !!!next-input-character;
1025            redo A;
1026          } elsif ($self->{nc} == -1) {
1027            !!!cp (0.3);
1028            !!!emit ({type => END_OF_FILE_TOKEN,
1029                      line => $self->{line}, column => $self->{column}});
1030            last A; ## TODO: ok?
1031          } else {
1032            !!!cp (0.4);
1033            #
1034          }
1035    
1036          # Anything else
1037          my $token = {type => CHARACTER_TOKEN,
1038                       data => chr $self->{nc},
1039                       line => $self->{line}, column => $self->{column},
1040                      };
1041          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1042    
1043          ## Stay in the state.
1044          !!!next-input-character;
1045          !!!emit ($token);
1046          redo A;
1047        } elsif ($self->{state} == DATA_STATE) {
1048          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1049          if ($self->{nc} == 0x0026) { # &
1050            $self->{s_kwd} = '';
1051            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1052                not $self->{escape}) {
1053              !!!cp (1);
1054              ## NOTE: In the spec, the tokenizer is switched to the
1055              ## "entity data state".  In this implementation, the tokenizer
1056              ## is switched to the |ENTITY_STATE|, which is an implementation
1057              ## of the "consume a character reference" algorithm.
1058              $self->{entity_add} = -1;
1059              $self->{prev_state} = DATA_STATE;
1060              $self->{state} = ENTITY_STATE;
1061            !!!next-input-character;            !!!next-input-character;
1062            redo A;            redo A;
1063          } else {          } else {
1064              !!!cp (2);
1065            #            #
1066          }          }
1067        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1068          if ($self->{content_model_flag} eq 'RCDATA' or          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1069              $self->{content_model_flag} eq 'CDATA') {            $self->{s_kwd} .= '-';
1070            unless ($self->{escape}) {            
1071              if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '<!--') {
1072                  $self->{prev_input_character}->[1] == 0x0021 and # !              !!!cp (3);
1073                  $self->{prev_input_character}->[2] == 0x003C) { # <              $self->{escape} = 1; # unless $self->{escape};
1074                $self->{escape} = 1;              $self->{s_kwd} = '--';
1075              }              #
1076              } elsif ($self->{s_kwd} eq '---') {
1077                !!!cp (4);
1078                $self->{s_kwd} = '--';
1079                #
1080              } else {
1081                !!!cp (5);
1082                #
1083            }            }
1084          }          }
1085                    
1086          #          #
1087        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1088          if ($self->{content_model_flag} eq 'PCDATA' or          if (length $self->{s_kwd}) {
1089              (($self->{content_model_flag} eq 'CDATA' or            !!!cp (5.1);
1090                $self->{content_model_flag} eq 'RCDATA') and            $self->{s_kwd} .= '!';
1091              #
1092            } else {
1093              !!!cp (5.2);
1094              #$self->{s_kwd} = '';
1095              #
1096            }
1097            #
1098          } elsif ($self->{nc} == 0x003C) { # <
1099            if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1100                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1101               not $self->{escape})) {               not $self->{escape})) {
1102            $self->{state} = 'tag open';            !!!cp (6);
1103              $self->{state} = TAG_OPEN_STATE;
1104            !!!next-input-character;            !!!next-input-character;
1105            redo A;            redo A;
1106          } else {          } else {
1107              !!!cp (7);
1108              $self->{s_kwd} = '';
1109            #            #
1110          }          }
1111        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1112          if ($self->{escape} and          if ($self->{escape} and
1113              ($self->{content_model_flag} eq 'RCDATA' or              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1114               $self->{content_model_flag} eq 'CDATA')) {            if ($self->{s_kwd} eq '--') {
1115            if ($self->{prev_input_character}->[0] == 0x002D and # -              !!!cp (8);
               $self->{prev_input_character}->[1] == 0x002D) { # -  
1116              delete $self->{escape};              delete $self->{escape};
1117              } else {
1118                !!!cp (9);
1119            }            }
1120            } else {
1121              !!!cp (10);
1122          }          }
1123                    
1124            $self->{s_kwd} = '';
1125          #          #
1126        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1127          !!!emit ({type => 'end-of-file'});          !!!cp (11);
1128            $self->{s_kwd} = '';
1129            !!!emit ({type => END_OF_FILE_TOKEN,
1130                      line => $self->{line}, column => $self->{column}});
1131          last A; ## TODO: ok?          last A; ## TODO: ok?
1132          } else {
1133            !!!cp (12);
1134            $self->{s_kwd} = '';
1135            #
1136        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
1137    
1138        !!!emit ($token);        # Anything else
1139          my $token = {type => CHARACTER_TOKEN,
1140        redo A;                     data => chr $self->{nc},
1141      } elsif ($self->{state} eq 'entity data') {                     line => $self->{line}, column => $self->{column},
1142        ## (cannot happen in CDATA state)                    };
1143                if ($self->{read_until}->($token->{data}, q[-!<>&],
1144        my $token = $self->_tokenize_attempt_to_consume_an_entity;                                  length $token->{data})) {
1145            $self->{s_kwd} = '';
1146        $self->{state} = 'data';        }
       # next-input-character is already done  
1147    
1148        unless (defined $token) {        ## Stay in the data state.
1149          !!!emit ({type => 'character', data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1150            !!!cp (13);
1151            $self->{state} = PCDATA_STATE;
1152        } else {        } else {
1153          !!!emit ($token);          !!!cp (14);
1154            ## Stay in the state.
1155        }        }
1156          !!!next-input-character;
1157          !!!emit ($token);
1158        redo A;        redo A;
1159      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1160        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1161            $self->{content_model_flag} eq 'CDATA') {          if ($self->{nc} == 0x002F) { # /
1162          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1163            !!!next-input-character;            !!!next-input-character;
1164            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1165            redo A;            redo A;
1166            } elsif ($self->{nc} == 0x0021) { # !
1167              !!!cp (15.1);
1168              $self->{s_kwd} = '<' unless $self->{escape};
1169              #
1170          } else {          } else {
1171            ## reconsume            !!!cp (16);
1172            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1173          }          }
1174        } elsif ($self->{content_model_flag} eq 'PCDATA') {  
1175          if ($self->{next_input_character} == 0x0021) { # !          ## reconsume
1176            $self->{state} = 'markup declaration open';          $self->{state} = DATA_STATE;
1177            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1178                      line => $self->{line_prev},
1179                      column => $self->{column_prev},
1180                     });
1181            redo A;
1182          } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1183            if ($self->{nc} == 0x0021) { # !
1184              !!!cp (17);
1185              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1186            !!!next-input-character;            !!!next-input-character;
1187            redo A;            redo A;
1188          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1189            $self->{state} = 'close tag open';            !!!cp (18);
1190              $self->{state} = CLOSE_TAG_OPEN_STATE;
1191            !!!next-input-character;            !!!next-input-character;
1192            redo A;            redo A;
1193          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1194                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1195            $self->{current_token}            !!!cp (19);
1196              = {type => 'start tag',            $self->{ct}
1197                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1198            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1199                   line => $self->{line_prev},
1200                   column => $self->{column_prev}};
1201              $self->{state} = TAG_NAME_STATE;
1202            !!!next-input-character;            !!!next-input-character;
1203            redo A;            redo A;
1204          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1205                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1206            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1207                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1208            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1209                                        line => $self->{line_prev},
1210                                        column => $self->{column_prev}};
1211              $self->{state} = TAG_NAME_STATE;
1212            !!!next-input-character;            !!!next-input-character;
1213            redo A;            redo A;
1214          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1215            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1216            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1217                              line => $self->{line_prev},
1218                              column => $self->{column_prev});
1219              $self->{state} = DATA_STATE;
1220            !!!next-input-character;            !!!next-input-character;
1221    
1222            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1223                        line => $self->{line_prev},
1224                        column => $self->{column_prev},
1225                       });
1226    
1227            redo A;            redo A;
1228          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1229            !!!parse-error (type => 'pio');            !!!cp (22);
1230            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1231            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1232                              column => $self->{column_prev});
1233              $self->{state} = BOGUS_COMMENT_STATE;
1234              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1235                                        line => $self->{line_prev},
1236                                        column => $self->{column_prev},
1237                                       };
1238              ## $self->{nc} is intentionally left as is
1239            redo A;            redo A;
1240          } else {          } else {
1241            !!!parse-error (type => 'bare stago');            !!!cp (23);
1242            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1243                              line => $self->{line_prev},
1244                              column => $self->{column_prev});
1245              $self->{state} = DATA_STATE;
1246            ## reconsume            ## reconsume
1247    
1248            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1249                        line => $self->{line_prev},
1250                        column => $self->{column_prev},
1251                       });
1252    
1253            redo A;            redo A;
1254          }          }
1255        } else {        } else {
1256          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1257        }        }
1258      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1259        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1260            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1261          if (defined $self->{last_emitted_start_tag_name}) {  
1262            my @next_char;        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1263            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1264              push @next_char, $self->{next_input_character};          if (defined $self->{last_stag_name}) {
1265              my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1266              my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            $self->{s_kwd} = '';
1267              if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {            ## Reconsume.
1268                !!!next-input-character;            redo A;
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = 'data';  
   
               !!!emit ({type => 'character', data => '</'});  
     
               redo A;  
             }  
           }  
           push @next_char, $self->{next_input_character};  
         
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -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...  
           }  
1269          } else {          } else {
1270            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1271            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1272            $self->{state} = 'data';            !!!cp (28);
1273            !!!emit ({type => 'character', data => '</'});            $self->{state} = DATA_STATE;
1274              ## Reconsume.
1275              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1276                        line => $l, column => $c,
1277                       });
1278            redo A;            redo A;
1279          }          }
1280        }        }
1281          
1282        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1283            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1284          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1285                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1286          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1287          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1288          redo A;                 line => $l, column => $c};
1289        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1290                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1291          $self->{current_token} = {type => 'end tag',          redo A;
1292                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1293          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1294          !!!next-input-character;          !!!cp (30);
1295          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1296        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1297          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1298          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1299            !!!next-input-character;
1300            redo A;
1301          } elsif ($self->{nc} == 0x003E) { # >
1302            !!!cp (31);
1303            !!!parse-error (type => 'empty end tag',
1304                            line => $self->{line_prev}, ## "<" in "</>"
1305                            column => $self->{column_prev} - 1);
1306            $self->{state} = DATA_STATE;
1307          !!!next-input-character;          !!!next-input-character;
1308          redo A;          redo A;
1309        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1310            !!!cp (32);
1311          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1312          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1313          # reconsume          # reconsume
1314    
1315          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1316                      line => $l, column => $c,
1317                     });
1318    
1319          redo A;          redo A;
1320        } else {        } else {
1321            !!!cp (33);
1322          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1323          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1324          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1325          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1326                                      column => $self->{column_prev} - 1,
1327                                     };
1328            ## NOTE: $self->{nc} is intentionally left as is.
1329            ## Although the "anything else" case of the spec not explicitly
1330            ## states that the next input character is to be reconsumed,
1331            ## it will be included to the |data| of the comment token
1332            ## generated from the bogus end tag, as defined in the
1333            ## "bogus comment state" entry.
1334            redo A;
1335          }
1336        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1337          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1338          if (length $ch) {
1339            my $CH = $ch;
1340            $ch =~ tr/a-z/A-Z/;
1341            my $nch = chr $self->{nc};
1342            if ($nch eq $ch or $nch eq $CH) {
1343              !!!cp (24);
1344              ## Stay in the state.
1345              $self->{s_kwd} .= $nch;
1346              !!!next-input-character;
1347              redo A;
1348            } else {
1349              !!!cp (25);
1350              $self->{state} = DATA_STATE;
1351              ## Reconsume.
1352              !!!emit ({type => CHARACTER_TOKEN,
1353                        data => '</' . $self->{s_kwd},
1354                        line => $self->{line_prev},
1355                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1356                       });
1357              redo A;
1358            }
1359          } else { # after "<{tag-name}"
1360            unless ($is_space->{$self->{nc}} or
1361                    {
1362                     0x003E => 1, # >
1363                     0x002F => 1, # /
1364                     -1 => 1, # EOF
1365                    }->{$self->{nc}}) {
1366              !!!cp (26);
1367              ## Reconsume.
1368              $self->{state} = DATA_STATE;
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            } else {
1376              !!!cp (27);
1377              $self->{ct}
1378                  = {type => END_TAG_TOKEN,
1379                     tag_name => $self->{last_stag_name},
1380                     line => $self->{line_prev},
1381                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1382              $self->{state} = TAG_NAME_STATE;
1383              ## Reconsume.
1384              redo A;
1385            }
1386        }        }
1387      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1388        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1389            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
1390            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1391            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1392            $self->{next_input_character} == 0x0020) { # SP          redo A;
1393          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
1394          !!!next-input-character;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1395          redo A;            !!!cp (35);
1396        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{last_stag_name} = $self->{ct}->{tag_name};
1397          if ($self->{current_token}->{type} eq 'start tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1398            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1399          } elsif ($self->{current_token}->{type} eq 'end tag') {            #if ($self->{ct}->{attributes}) {
1400            $self->{content_model_flag} = 'PCDATA'; # MUST            #  ## NOTE: This should never be reached.
1401            if ($self->{current_token}->{attributes}) {            #  !!! cp (36);
1402              !!!parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
1403            }            #} else {
1404                !!!cp (37);
1405              #}
1406          } else {          } else {
1407            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1408          }          }
1409          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1410          !!!next-input-character;          !!!next-input-character;
1411    
1412          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1413    
1414          redo A;          redo A;
1415        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1416                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1417          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1418            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1419            # start tag or end tag            # start tag or end tag
1420          ## Stay in this state          ## Stay in this state
1421          !!!next-input-character;          !!!next-input-character;
1422          redo A;          redo A;
1423        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1424          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1425          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1426            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (39);
1427          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1429            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1430              !!!parse-error (type => 'end tag attribute');            #if ($self->{ct}->{attributes}) {
1431            }            #  ## NOTE: This state should never be reached.
1432              #  !!! cp (40);
1433              #  !!! parse-error (type => 'end tag attribute');
1434              #} else {
1435                !!!cp (41);
1436              #}
1437          } else {          } else {
1438            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1439          }          }
1440          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1441          # reconsume          # reconsume
1442    
1443          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1444    
1445          redo A;          redo A;
1446        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1447            !!!cp (42);
1448            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1449          !!!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  
1450          redo A;          redo A;
1451        } else {        } else {
1452          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1453            $self->{ct}->{tag_name} .= chr $self->{nc};
1454            # start tag or end tag            # start tag or end tag
1455          ## Stay in the state          ## Stay in the state
1456          !!!next-input-character;          !!!next-input-character;
1457          redo A;          redo A;
1458        }        }
1459      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1460        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1461            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1462          ## Stay in the state          ## Stay in the state
1463          !!!next-input-character;          !!!next-input-character;
1464          redo A;          redo A;
1465        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1466          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1467            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (46);
1468          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1469            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1470            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1471              if ($self->{ct}->{attributes}) {
1472                !!!cp (47);
1473              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1474              } else {
1475                !!!cp (48);
1476            }            }
1477          } else {          } else {
1478            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1479          }          }
1480          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1481          !!!next-input-character;          !!!next-input-character;
1482    
1483          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1484    
1485          redo A;          redo A;
1486        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1487                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1488          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1489                                value => ''};          $self->{ca}
1490          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1491                   value => '',
1492                   line => $self->{line}, column => $self->{column}};
1493            $self->{state} = ATTRIBUTE_NAME_STATE;
1494          !!!next-input-character;          !!!next-input-character;
1495          redo A;          redo A;
1496        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1497            !!!cp (50);
1498            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1499          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1500          redo A;          redo A;
1501        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1502          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1503          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1504            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (52);
1505          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1506            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1507            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1508              if ($self->{ct}->{attributes}) {
1509                !!!cp (53);
1510              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1511              } else {
1512                !!!cp (54);
1513            }            }
1514          } else {          } else {
1515            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1516          }          }
1517          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1518          # reconsume          # reconsume
1519    
1520          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1521    
1522          redo A;          redo A;
1523        } else {        } else {
1524          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1525                                value => ''};               0x0022 => 1, # "
1526          $self->{state} = 'attribute name';               0x0027 => 1, # '
1527                 0x003D => 1, # =
1528                }->{$self->{nc}}) {
1529              !!!cp (55);
1530              !!!parse-error (type => 'bad attribute name');
1531            } else {
1532              !!!cp (56);
1533            }
1534            $self->{ca}
1535                = {name => chr ($self->{nc}),
1536                   value => '',
1537                   line => $self->{line}, column => $self->{column}};
1538            $self->{state} = ATTRIBUTE_NAME_STATE;
1539          !!!next-input-character;          !!!next-input-character;
1540          redo A;          redo A;
1541        }        }
1542      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1543        my $before_leave = sub {        my $before_leave = sub {
1544          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1545              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1546            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1547            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1548              ## Discard $self->{ca} # MUST
1549          } else {          } else {
1550            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            !!!cp (58);
1551              = $self->{current_attribute};            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1552                = $self->{ca};
1553          }          }
1554        }; # $before_leave        }; # $before_leave
1555    
1556        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1557            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1558          $before_leave->();          $before_leave->();
1559          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1560          !!!next-input-character;          !!!next-input-character;
1561          redo A;          redo A;
1562        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1563            !!!cp (60);
1564          $before_leave->();          $before_leave->();
1565          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1566          !!!next-input-character;          !!!next-input-character;
1567          redo A;          redo A;
1568        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1569          $before_leave->();          $before_leave->();
1570          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1571            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (61);
1572          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1573            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1574            if ($self->{current_token}->{attributes}) {            !!!cp (62);
1575              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1576              if ($self->{ct}->{attributes}) {
1577              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1578            }            }
1579          } else {          } else {
1580            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1581          }          }
1582          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584    
1585          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1586    
1587          redo A;          redo A;
1588        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1589                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1590          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1591            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1592          ## Stay in the state          ## Stay in the state
1593          !!!next-input-character;          !!!next-input-character;
1594          redo A;          redo A;
1595        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1596            !!!cp (64);
1597          $before_leave->();          $before_leave->();
1598            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1599          !!!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  
1600          redo A;          redo A;
1601        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1602          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1603          $before_leave->();          $before_leave->();
1604          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1605            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (66);
1606          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1607            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1608            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1609              if ($self->{ct}->{attributes}) {
1610                !!!cp (67);
1611              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1612              } else {
1613                ## NOTE: This state should never be reached.
1614                !!!cp (68);
1615            }            }
1616          } else {          } else {
1617            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1618          }          }
1619          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1620          # reconsume          # reconsume
1621    
1622          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1623    
1624          redo A;          redo A;
1625        } else {        } else {
1626          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1627                $self->{nc} == 0x0027) { # '
1628              !!!cp (69);
1629              !!!parse-error (type => 'bad attribute name');
1630            } else {
1631              !!!cp (70);
1632            }
1633            $self->{ca}->{name} .= chr ($self->{nc});
1634          ## Stay in the state          ## Stay in the state
1635          !!!next-input-character;          !!!next-input-character;
1636          redo A;          redo A;
1637        }        }
1638      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1639        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1640            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1641          ## Stay in the state          ## Stay in the state
1642          !!!next-input-character;          !!!next-input-character;
1643          redo A;          redo A;
1644        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1645          $self->{state} = 'before attribute value';          !!!cp (72);
1646            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1647          !!!next-input-character;          !!!next-input-character;
1648          redo A;          redo A;
1649        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1650          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1651            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (73);
1652          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1653            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1654            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1655              if ($self->{ct}->{attributes}) {
1656                !!!cp (74);
1657              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1658              } else {
1659                ## NOTE: This state should never be reached.
1660                !!!cp (75);
1661            }            }
1662          } else {          } else {
1663            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1664          }          }
1665          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1666          !!!next-input-character;          !!!next-input-character;
1667    
1668          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1669    
1670          redo A;          redo A;
1671        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1672                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1673          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1674                                value => ''};          $self->{ca}
1675          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1676                   value => '',
1677                   line => $self->{line}, column => $self->{column}};
1678            $self->{state} = ATTRIBUTE_NAME_STATE;
1679          !!!next-input-character;          !!!next-input-character;
1680          redo A;          redo A;
1681        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1682            !!!cp (77);
1683            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1684          !!!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  
1685          redo A;          redo A;
1686        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1687          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1688          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1689            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (79);
1690          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1691            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1692            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1693              if ($self->{ct}->{attributes}) {
1694                !!!cp (80);
1695              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1696              } else {
1697                ## NOTE: This state should never be reached.
1698                !!!cp (81);
1699            }            }
1700          } else {          } else {
1701            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1702          }          }
1703          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1704          # reconsume          # reconsume
1705    
1706          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1707    
1708          redo A;          redo A;
1709        } else {        } else {
1710          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1711                                value => ''};              $self->{nc} == 0x0027) { # '
1712          $self->{state} = 'attribute name';            !!!cp (78);
1713              !!!parse-error (type => 'bad attribute name');
1714            } else {
1715              !!!cp (82);
1716            }
1717            $self->{ca}
1718                = {name => chr ($self->{nc}),
1719                   value => '',
1720                   line => $self->{line}, column => $self->{column}};
1721            $self->{state} = ATTRIBUTE_NAME_STATE;
1722          !!!next-input-character;          !!!next-input-character;
1723          redo A;                  redo A;        
1724        }        }
1725      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1726        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1727            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1728          ## Stay in the state          ## Stay in the state
1729          !!!next-input-character;          !!!next-input-character;
1730          redo A;          redo A;
1731        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1732          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1733            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1734          !!!next-input-character;          !!!next-input-character;
1735          redo A;          redo A;
1736        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1737          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1738            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1739          ## reconsume          ## reconsume
1740          redo A;          redo A;
1741        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1742          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1743            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1744          !!!next-input-character;          !!!next-input-character;
1745          redo A;          redo A;
1746        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1747          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1748            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1749          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (87);
1750            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
1751            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1752              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1753              if ($self->{ct}->{attributes}) {
1754                !!!cp (88);
1755              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1756              } else {
1757                ## NOTE: This state should never be reached.
1758                !!!cp (89);
1759            }            }
1760          } else {          } else {
1761            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1762          }          }
1763          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1764          !!!next-input-character;          !!!next-input-character;
1765    
1766          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1767    
1768          redo A;          redo A;
1769        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1770          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1771          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1772            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (90);
1773          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1774            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1775            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1776              if ($self->{ct}->{attributes}) {
1777                !!!cp (91);
1778              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1779              } else {
1780                ## NOTE: This state should never be reached.
1781                !!!cp (92);
1782            }            }
1783          } else {          } else {
1784            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1785          }          }
1786          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1787          ## reconsume          ## reconsume
1788    
1789          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1790    
1791          redo A;          redo A;
1792        } else {        } else {
1793          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1794          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1795              !!!parse-error (type => 'bad attribute value');
1796            } else {
1797              !!!cp (94);
1798            }
1799            $self->{ca}->{value} .= chr ($self->{nc});
1800            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1801          !!!next-input-character;          !!!next-input-character;
1802          redo A;          redo A;
1803        }        }
1804      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1805        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1806          $self->{state} = 'before attribute name';          !!!cp (95);
1807            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1808          !!!next-input-character;          !!!next-input-character;
1809          redo A;          redo A;
1810        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1811          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1812          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1813            ## "entity in attribute value state".  In this implementation, the
1814            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1815            ## implementation of the "consume a character reference" algorithm.
1816            $self->{prev_state} = $self->{state};
1817            $self->{entity_add} = 0x0022; # "
1818            $self->{state} = ENTITY_STATE;
1819          !!!next-input-character;          !!!next-input-character;
1820          redo A;          redo A;
1821        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1822          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1823          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1824            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (97);
1825          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1826            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1827            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1828              if ($self->{ct}->{attributes}) {
1829                !!!cp (98);
1830              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1831              } else {
1832                ## NOTE: This state should never be reached.
1833                !!!cp (99);
1834            }            }
1835          } else {          } else {
1836            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1837          }          }
1838          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1839          ## reconsume          ## reconsume
1840    
1841          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1842    
1843          redo A;          redo A;
1844        } else {        } else {
1845          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1846            $self->{ca}->{value} .= chr ($self->{nc});
1847            $self->{read_until}->($self->{ca}->{value},
1848                                  q["&],
1849                                  length $self->{ca}->{value});
1850    
1851          ## Stay in the state          ## Stay in the state
1852          !!!next-input-character;          !!!next-input-character;
1853          redo A;          redo A;
1854        }        }
1855      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1856        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1857          $self->{state} = 'before attribute name';          !!!cp (101);
1858            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1859            !!!next-input-character;
1860            redo A;
1861          } elsif ($self->{nc} == 0x0026) { # &
1862            !!!cp (102);
1863            ## NOTE: In the spec, the tokenizer is switched to the
1864            ## "entity in attribute value state".  In this implementation, the
1865            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1866            ## implementation of the "consume a character reference" algorithm.
1867            $self->{entity_add} = 0x0027; # '
1868            $self->{prev_state} = $self->{state};
1869            $self->{state} = ENTITY_STATE;
1870          !!!next-input-character;          !!!next-input-character;
1871          redo A;          redo A;
1872        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = 'attribute value (single-quoted)';  
         $self->{state} = 'entity in attribute value';  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1873          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1874          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1875            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (103);
1876          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1877            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1878            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1879              if ($self->{ct}->{attributes}) {
1880                !!!cp (104);
1881              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1882              } else {
1883                ## NOTE: This state should never be reached.
1884                !!!cp (105);
1885            }            }
1886          } else {          } else {
1887            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1888          }          }
1889          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1890          ## reconsume          ## reconsume
1891    
1892          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1893    
1894          redo A;          redo A;
1895        } else {        } else {
1896          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1897            $self->{ca}->{value} .= chr ($self->{nc});
1898            $self->{read_until}->($self->{ca}->{value},
1899                                  q['&],
1900                                  length $self->{ca}->{value});
1901    
1902          ## Stay in the state          ## Stay in the state
1903          !!!next-input-character;          !!!next-input-character;
1904          redo A;          redo A;
1905        }        }
1906      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1907        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1908            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1909            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1910            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1911            $self->{next_input_character} == 0x0020) { # SP          redo A;
1912          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1913          !!!next-input-character;          !!!cp (108);
1914          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1915        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1916          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1917          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1918          !!!next-input-character;          $self->{entity_add} = -1;
1919          redo A;          $self->{prev_state} = $self->{state};
1920        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1921          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1922            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          redo A;
1923          } elsif ($self->{current_token}->{type} eq 'end tag') {        } elsif ($self->{nc} == 0x003E) { # >
1924            $self->{content_model_flag} = 'PCDATA'; # MUST          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1925            if ($self->{current_token}->{attributes}) {            !!!cp (109);
1926              $self->{last_stag_name} = $self->{ct}->{tag_name};
1927            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1928              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1929              if ($self->{ct}->{attributes}) {
1930                !!!cp (110);
1931              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1932              } else {
1933                ## NOTE: This state should never be reached.
1934                !!!cp (111);
1935            }            }
1936          } else {          } else {
1937            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1938          }          }
1939          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1940          !!!next-input-character;          !!!next-input-character;
1941    
1942          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1943    
1944          redo A;          redo A;
1945        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1946          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1947          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1948            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (112);
1949          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1950            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1951            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1952              if ($self->{ct}->{attributes}) {
1953                !!!cp (113);
1954              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1955              } else {
1956                ## NOTE: This state should never be reached.
1957                !!!cp (114);
1958            }            }
1959          } else {          } else {
1960            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1961          }          }
1962          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1963          ## reconsume          ## reconsume
1964    
1965          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1966    
1967          redo A;          redo A;
1968        } else {        } else {
1969          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1970                 0x0022 => 1, # "
1971                 0x0027 => 1, # '
1972                 0x003D => 1, # =
1973                }->{$self->{nc}}) {
1974              !!!cp (115);
1975              !!!parse-error (type => 'bad attribute value');
1976            } else {
1977              !!!cp (116);
1978            }
1979            $self->{ca}->{value} .= chr ($self->{nc});
1980            $self->{read_until}->($self->{ca}->{value},
1981                                  q["'=& >],
1982                                  length $self->{ca}->{value});
1983    
1984          ## Stay in the state          ## Stay in the state
1985          !!!next-input-character;          !!!next-input-character;
1986          redo A;          redo A;
1987        }        }
1988      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
1989        my $token = $self->_tokenize_attempt_to_consume_an_entity;        if ($is_space->{$self->{nc}}) {
1990            !!!cp (118);
1991            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1992            !!!next-input-character;
1993            redo A;
1994          } elsif ($self->{nc} == 0x003E) { # >
1995            if ($self->{ct}->{type} == START_TAG_TOKEN) {
1996              !!!cp (119);
1997              $self->{last_stag_name} = $self->{ct}->{tag_name};
1998            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1999              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2000              if ($self->{ct}->{attributes}) {
2001                !!!cp (120);
2002                !!!parse-error (type => 'end tag attribute');
2003              } else {
2004                ## NOTE: This state should never be reached.
2005                !!!cp (121);
2006              }
2007            } else {
2008              die "$0: $self->{ct}->{type}: Unknown token type";
2009            }
2010            $self->{state} = DATA_STATE;
2011            !!!next-input-character;
2012    
2013            !!!emit ($self->{ct}); # start tag or end tag
2014    
2015        unless (defined $token) {          redo A;
2016          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2017            !!!cp (122);
2018            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2019            !!!next-input-character;
2020            redo A;
2021          } elsif ($self->{nc} == -1) {
2022            !!!parse-error (type => 'unclosed tag');
2023            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2024              !!!cp (122.3);
2025              $self->{last_stag_name} = $self->{ct}->{tag_name};
2026            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2027              if ($self->{ct}->{attributes}) {
2028                !!!cp (122.1);
2029                !!!parse-error (type => 'end tag attribute');
2030              } else {
2031                ## NOTE: This state should never be reached.
2032                !!!cp (122.2);
2033              }
2034            } else {
2035              die "$0: $self->{ct}->{type}: Unknown token type";
2036            }
2037            $self->{state} = DATA_STATE;
2038            ## Reconsume.
2039            !!!emit ($self->{ct}); # start tag or end tag
2040            redo A;
2041        } else {        } else {
2042          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2043          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2044            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2045            ## reconsume
2046            redo A;
2047        }        }
2048        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2049          if ($self->{nc} == 0x003E) { # >
2050            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2051              !!!cp ('124.2');
2052              !!!parse-error (type => 'nestc', token => $self->{ct});
2053              ## TODO: Different type than slash in start tag
2054              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2055              if ($self->{ct}->{attributes}) {
2056                !!!cp ('124.4');
2057                !!!parse-error (type => 'end tag attribute');
2058              } else {
2059                !!!cp ('124.5');
2060              }
2061              ## TODO: Test |<title></title/>|
2062            } else {
2063              !!!cp ('124.3');
2064              $self->{self_closing} = 1;
2065            }
2066    
2067        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2068        # next-input-character is already done          !!!next-input-character;
       redo A;  
     } elsif ($self->{state} eq 'bogus comment') {  
       ## (only happen if PCDATA state)  
         
       my $token = {type => 'comment', data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = 'data';  
           !!!next-input-character;  
   
           !!!emit ($token);  
   
           redo A;  
         } elsif ($self->{next_input_character} == -1) {  
           $self->{state} = 'data';  
           ## reconsume  
2069    
2070            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2071    
2072            redo A;          redo A;
2073          } elsif ($self->{nc} == -1) {
2074            !!!parse-error (type => 'unclosed tag');
2075            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2076              !!!cp (124.7);
2077              $self->{last_stag_name} = $self->{ct}->{tag_name};
2078            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2079              if ($self->{ct}->{attributes}) {
2080                !!!cp (124.5);
2081                !!!parse-error (type => 'end tag attribute');
2082              } else {
2083                ## NOTE: This state should never be reached.
2084                !!!cp (124.6);
2085              }
2086          } else {          } else {
2087            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2088          }          }
2089        } # BC          $self->{state} = DATA_STATE;
2090      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2091            !!!emit ($self->{ct}); # start tag or end tag
2092            redo A;
2093          } else {
2094            !!!cp ('124.4');
2095            !!!parse-error (type => 'nestc');
2096            ## TODO: This error type is wrong.
2097            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2098            ## Reconsume.
2099            redo A;
2100          }
2101        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2102        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2103    
2104        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2105        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2106                
2107        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2108            !!!cp (124);
2109            $self->{state} = DATA_STATE;
2110          !!!next-input-character;          !!!next-input-character;
2111          push @next_char, $self->{next_input_character};  
2112          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2113            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2114            $self->{state} = 'comment start';        } elsif ($self->{nc} == -1) {
2115            !!!next-input-character;          !!!cp (125);
2116            redo A;          $self->{state} = DATA_STATE;
2117          }          ## reconsume
2118        } elsif ($self->{next_input_character} == 0x0044 or # D  
2119                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2120            redo A;
2121          } else {
2122            !!!cp (126);
2123            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2124            $self->{read_until}->($self->{ct}->{data},
2125                                  q[>],
2126                                  length $self->{ct}->{data});
2127    
2128            ## Stay in the state.
2129          !!!next-input-character;          !!!next-input-character;
2130          push @next_char, $self->{next_input_character};          redo A;
2131          if ($self->{next_input_character} == 0x004F or # O        }
2132              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2133            !!!next-input-character;        ## (only happen if PCDATA state)
2134            push @next_char, $self->{next_input_character};        
2135            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2136                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2137              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2138              push @next_char, $self->{next_input_character};          !!!next-input-character;
2139              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2140                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2141                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2142                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2143                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2144                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2145                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2146                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2147                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2148                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2149                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2150                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2151                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2152                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2153                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2154                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2155                      !!!next-input-character;          redo A;
2156                      redo A;        } else {
2157                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2158        }        }
2159    
2160        !!!parse-error (type => 'bogus comment open');        !!!parse-error (type => 'bogus comment',
2161        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2162        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2163        $self->{state} = 'bogus comment';        ## Reconsume.
2164          $self->{state} = BOGUS_COMMENT_STATE;
2165          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2166                                    line => $self->{line_prev},
2167                                    column => $self->{column_prev} - 1,
2168                                   };
2169        redo A;        redo A;
2170              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2171        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2172        ## 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);
2173      } elsif ($self->{state} eq 'comment start') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2174        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2175          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2176                                     };
2177            $self->{state} = COMMENT_START_STATE;
2178            !!!next-input-character;
2179            redo A;
2180          } else {
2181            !!!cp (128);
2182            !!!parse-error (type => 'bogus comment',
2183                            line => $self->{line_prev},
2184                            column => $self->{column_prev} - 2);
2185            $self->{state} = BOGUS_COMMENT_STATE;
2186            ## Reconsume.
2187            $self->{ct} = {type => COMMENT_TOKEN,
2188                                      data => '-',
2189                                      line => $self->{line_prev},
2190                                      column => $self->{column_prev} - 2,
2191                                     };
2192            redo A;
2193          }
2194        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2195          ## ASCII case-insensitive.
2196          if ($self->{nc} == [
2197                undef,
2198                0x004F, # O
2199                0x0043, # C
2200                0x0054, # T
2201                0x0059, # Y
2202                0x0050, # P
2203              ]->[length $self->{s_kwd}] or
2204              $self->{nc} == [
2205                undef,
2206                0x006F, # o
2207                0x0063, # c
2208                0x0074, # t
2209                0x0079, # y
2210                0x0070, # p
2211              ]->[length $self->{s_kwd}]) {
2212            !!!cp (131);
2213            ## Stay in the state.
2214            $self->{s_kwd} .= chr $self->{nc};
2215            !!!next-input-character;
2216            redo A;
2217          } elsif ((length $self->{s_kwd}) == 6 and
2218                   ($self->{nc} == 0x0045 or # E
2219                    $self->{nc} == 0x0065)) { # e
2220            !!!cp (129);
2221            $self->{state} = DOCTYPE_STATE;
2222            $self->{ct} = {type => DOCTYPE_TOKEN,
2223                                      quirks => 1,
2224                                      line => $self->{line_prev},
2225                                      column => $self->{column_prev} - 7,
2226                                     };
2227            !!!next-input-character;
2228            redo A;
2229          } else {
2230            !!!cp (132);        
2231            !!!parse-error (type => 'bogus comment',
2232                            line => $self->{line_prev},
2233                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2234            $self->{state} = BOGUS_COMMENT_STATE;
2235            ## Reconsume.
2236            $self->{ct} = {type => COMMENT_TOKEN,
2237                                      data => $self->{s_kwd},
2238                                      line => $self->{line_prev},
2239                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2240                                     };
2241            redo A;
2242          }
2243        } elsif ($self->{state} == MD_CDATA_STATE) {
2244          if ($self->{nc} == {
2245                '[' => 0x0043, # C
2246                '[C' => 0x0044, # D
2247                '[CD' => 0x0041, # A
2248                '[CDA' => 0x0054, # T
2249                '[CDAT' => 0x0041, # A
2250              }->{$self->{s_kwd}}) {
2251            !!!cp (135.1);
2252            ## Stay in the state.
2253            $self->{s_kwd} .= chr $self->{nc};
2254            !!!next-input-character;
2255            redo A;
2256          } elsif ($self->{s_kwd} eq '[CDATA' and
2257                   $self->{nc} == 0x005B) { # [
2258            !!!cp (135.2);
2259            $self->{ct} = {type => CHARACTER_TOKEN,
2260                                      data => '',
2261                                      line => $self->{line_prev},
2262                                      column => $self->{column_prev} - 7};
2263            $self->{state} = CDATA_SECTION_STATE;
2264            !!!next-input-character;
2265            redo A;
2266          } else {
2267            !!!cp (135.3);
2268            !!!parse-error (type => 'bogus comment',
2269                            line => $self->{line_prev},
2270                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2271            $self->{state} = BOGUS_COMMENT_STATE;
2272            ## Reconsume.
2273            $self->{ct} = {type => COMMENT_TOKEN,
2274                                      data => $self->{s_kwd},
2275                                      line => $self->{line_prev},
2276                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2277                                     };
2278            redo A;
2279          }
2280        } elsif ($self->{state} == COMMENT_START_STATE) {
2281          if ($self->{nc} == 0x002D) { # -
2282            !!!cp (137);
2283            $self->{state} = COMMENT_START_DASH_STATE;
2284          !!!next-input-character;          !!!next-input-character;
2285          redo A;          redo A;
2286        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2287            !!!cp (138);
2288          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2289          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2290          !!!next-input-character;          !!!next-input-character;
2291    
2292          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2293    
2294          redo A;          redo A;
2295        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2296            !!!cp (139);
2297          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2298          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2299          ## reconsume          ## reconsume
2300    
2301          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2302    
2303          redo A;          redo A;
2304        } else {        } else {
2305          $self->{current_token}->{data} # comment          !!!cp (140);
2306              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2307          $self->{state} = 'comment';              .= chr ($self->{nc});
2308            $self->{state} = COMMENT_STATE;
2309          !!!next-input-character;          !!!next-input-character;
2310          redo A;          redo A;
2311        }        }
2312      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2313        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2314          $self->{state} = 'comment end';          !!!cp (141);
2315            $self->{state} = COMMENT_END_STATE;
2316          !!!next-input-character;          !!!next-input-character;
2317          redo A;          redo A;
2318        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2319            !!!cp (142);
2320          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2321          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2322          !!!next-input-character;          !!!next-input-character;
2323    
2324          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2325    
2326          redo A;          redo A;
2327        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2328            !!!cp (143);
2329          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2330          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2331          ## reconsume          ## reconsume
2332    
2333          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2334    
2335          redo A;          redo A;
2336        } else {        } else {
2337          $self->{current_token}->{data} # comment          !!!cp (144);
2338              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2339          $self->{state} = 'comment';              .= '-' . chr ($self->{nc});
2340            $self->{state} = COMMENT_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        }        }
2344      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2345        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2346          $self->{state} = 'comment end dash';          !!!cp (145);
2347            $self->{state} = COMMENT_END_DASH_STATE;
2348          !!!next-input-character;          !!!next-input-character;
2349          redo A;          redo A;
2350        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2351            !!!cp (146);
2352          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2353          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2354          ## reconsume          ## reconsume
2355    
2356          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2357    
2358          redo A;          redo A;
2359        } else {        } else {
2360          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2361            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2362            $self->{read_until}->($self->{ct}->{data},
2363                                  q[-],
2364                                  length $self->{ct}->{data});
2365    
2366          ## Stay in the state          ## Stay in the state
2367          !!!next-input-character;          !!!next-input-character;
2368          redo A;          redo A;
2369        }        }
2370      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2371        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2372          $self->{state} = 'comment end';          !!!cp (148);
2373            $self->{state} = COMMENT_END_STATE;
2374          !!!next-input-character;          !!!next-input-character;
2375          redo A;          redo A;
2376        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2377            !!!cp (149);
2378          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2379          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2380          ## reconsume          ## reconsume
2381    
2382          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2383    
2384          redo A;          redo A;
2385        } else {        } else {
2386          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2387          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2388            $self->{state} = COMMENT_STATE;
2389          !!!next-input-character;          !!!next-input-character;
2390          redo A;          redo A;
2391        }        }
2392      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2393        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2394          $self->{state} = 'data';          !!!cp (151);
2395            $self->{state} = DATA_STATE;
2396          !!!next-input-character;          !!!next-input-character;
2397    
2398          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2399    
2400          redo A;          redo A;
2401        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2402          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2403          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2404                            line => $self->{line_prev},
2405                            column => $self->{column_prev});
2406            $self->{ct}->{data} .= '-'; # comment
2407          ## Stay in the state          ## Stay in the state
2408          !!!next-input-character;          !!!next-input-character;
2409          redo A;          redo A;
2410        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2411            !!!cp (153);
2412          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2413          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2414          ## reconsume          ## reconsume
2415    
2416          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2417    
2418          redo A;          redo A;
2419        } else {        } else {
2420          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2421          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2422          $self->{state} = 'comment';                          line => $self->{line_prev},
2423                            column => $self->{column_prev});
2424            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2425            $self->{state} = COMMENT_STATE;
2426          !!!next-input-character;          !!!next-input-character;
2427          redo A;          redo A;
2428        }        }
2429      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2430        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2431            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2432            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'before DOCTYPE name';  
2433          !!!next-input-character;          !!!next-input-character;
2434          redo A;          redo A;
2435        } else {        } else {
2436            !!!cp (156);
2437          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2438          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2439          ## reconsume          ## reconsume
2440          redo A;          redo A;
2441        }        }
2442      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2443        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2444            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2445          ## Stay in the state          ## Stay in the state
2446          !!!next-input-character;          !!!next-input-character;
2447          redo A;          redo A;
2448        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2449            !!!cp (158);
2450          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2451          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2452          !!!next-input-character;          !!!next-input-character;
2453    
2454          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2455    
2456          redo A;          redo A;
2457        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2458            !!!cp (159);
2459          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2460          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2461          ## reconsume          ## reconsume
2462    
2463          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2464    
2465          redo A;          redo A;
2466        } else {        } else {
2467          $self->{current_token}          !!!cp (160);
2468              = {type => 'DOCTYPE',          $self->{ct}->{name} = chr $self->{nc};
2469                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
                correct => 1};  
2470  ## ISSUE: "Set the token's name name to the" in the spec  ## ISSUE: "Set the token's name name to the" in the spec
2471          $self->{state} = 'DOCTYPE name';          $self->{state} = DOCTYPE_NAME_STATE;
2472          !!!next-input-character;          !!!next-input-character;
2473          redo A;          redo A;
2474        }        }
2475      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2476  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2477        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2478            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
2479            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'after DOCTYPE name';  
2480          !!!next-input-character;          !!!next-input-character;
2481          redo A;          redo A;
2482        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2483          $self->{state} = 'data';          !!!cp (162);
2484            $self->{state} = DATA_STATE;
2485          !!!next-input-character;          !!!next-input-character;
2486    
2487          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2488    
2489          redo A;          redo A;
2490        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2491            !!!cp (163);
2492          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2493          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2494          ## reconsume          ## reconsume
2495    
2496          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2497          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2498    
2499          redo A;          redo A;
2500        } else {        } else {
2501          $self->{current_token}->{name}          !!!cp (164);
2502            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2503              .= chr ($self->{nc}); # DOCTYPE
2504          ## Stay in the state          ## Stay in the state
2505          !!!next-input-character;          !!!next-input-character;
2506          redo A;          redo A;
2507        }        }
2508      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2509        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2510            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2511          ## Stay in the state          ## Stay in the state
2512          !!!next-input-character;          !!!next-input-character;
2513          redo A;          redo A;
2514        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2515          $self->{state} = 'data';          !!!cp (166);
2516            $self->{state} = DATA_STATE;
2517          !!!next-input-character;          !!!next-input-character;
2518    
2519          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2520    
2521          redo A;          redo A;
2522        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2523            !!!cp (167);
2524          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2525          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2526          ## reconsume          ## reconsume
2527    
2528          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2529          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2530    
2531          redo A;          redo A;
2532        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2533                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2534            $self->{state} = PUBLIC_STATE;
2535            $self->{s_kwd} = chr $self->{nc};
2536          !!!next-input-character;          !!!next-input-character;
2537          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2538              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2539            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2540            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2541                $self->{next_input_character} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x004C or # L  
                 $self->{next_input_character} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0049 or # I  
                   $self->{next_input_character} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x0043 or # C  
                     $self->{next_input_character} == 0x0063) { # c  
                   $self->{state} = 'before DOCTYPE public identifier';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2542          !!!next-input-character;          !!!next-input-character;
2543          if ($self->{next_input_character} == 0x0059 or # Y          redo A;
             $self->{next_input_character} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_input_character} == 0x0053 or # S  
               $self->{next_input_character} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0045 or # E  
                   $self->{next_input_character} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x004D or # M  
                     $self->{next_input_character} == 0x006D) { # m  
                   $self->{state} = 'before DOCTYPE system identifier';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2544        } else {        } else {
2545            !!!cp (180);
2546            !!!parse-error (type => 'string after DOCTYPE name');
2547            $self->{ct}->{quirks} = 1;
2548    
2549            $self->{state} = BOGUS_DOCTYPE_STATE;
2550          !!!next-input-character;          !!!next-input-character;
2551          #          redo A;
2552        }        }
2553        } elsif ($self->{state} == PUBLIC_STATE) {
2554        !!!parse-error (type => 'string after DOCTYPE name');        ## ASCII case-insensitive
2555        $self->{state} = 'bogus DOCTYPE';        if ($self->{nc} == [
2556        # next-input-character is already done              undef,
2557        redo A;              0x0055, # U
2558      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {              0x0042, # B
2559        if ({              0x004C, # L
2560              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0049, # I
2561              #0x000D => 1, # HT, LF, VT, FF, SP, CR            ]->[length $self->{s_kwd}] or
2562            }->{$self->{next_input_character}}) {            $self->{nc} == [
2563                undef,
2564                0x0075, # u
2565                0x0062, # b
2566                0x006C, # l
2567                0x0069, # i
2568              ]->[length $self->{s_kwd}]) {
2569            !!!cp (175);
2570            ## Stay in the state.
2571            $self->{s_kwd} .= chr $self->{nc};
2572            !!!next-input-character;
2573            redo A;
2574          } elsif ((length $self->{s_kwd}) == 5 and
2575                   ($self->{nc} == 0x0043 or # C
2576                    $self->{nc} == 0x0063)) { # c
2577            !!!cp (168);
2578            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2579            !!!next-input-character;
2580            redo A;
2581          } else {
2582            !!!cp (169);
2583            !!!parse-error (type => 'string after DOCTYPE name',
2584                            line => $self->{line_prev},
2585                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2586            $self->{ct}->{quirks} = 1;
2587    
2588            $self->{state} = BOGUS_DOCTYPE_STATE;
2589            ## Reconsume.
2590            redo A;
2591          }
2592        } elsif ($self->{state} == SYSTEM_STATE) {
2593          ## ASCII case-insensitive
2594          if ($self->{nc} == [
2595                undef,
2596                0x0059, # Y
2597                0x0053, # S
2598                0x0054, # T
2599                0x0045, # E
2600              ]->[length $self->{s_kwd}] or
2601              $self->{nc} == [
2602                undef,
2603                0x0079, # y
2604                0x0073, # s
2605                0x0074, # t
2606                0x0065, # e
2607              ]->[length $self->{s_kwd}]) {
2608            !!!cp (170);
2609            ## Stay in the state.
2610            $self->{s_kwd} .= chr $self->{nc};
2611            !!!next-input-character;
2612            redo A;
2613          } elsif ((length $self->{s_kwd}) == 5 and
2614                   ($self->{nc} == 0x004D or # M
2615                    $self->{nc} == 0x006D)) { # m
2616            !!!cp (171);
2617            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2618            !!!next-input-character;
2619            redo A;
2620          } else {
2621            !!!cp (172);
2622            !!!parse-error (type => 'string after DOCTYPE name',
2623                            line => $self->{line_prev},
2624                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2625            $self->{ct}->{quirks} = 1;
2626    
2627            $self->{state} = BOGUS_DOCTYPE_STATE;
2628            ## Reconsume.
2629            redo A;
2630          }
2631        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2632          if ($is_space->{$self->{nc}}) {
2633            !!!cp (181);
2634          ## Stay in the state          ## Stay in the state
2635          !!!next-input-character;          !!!next-input-character;
2636          redo A;          redo A;
2637        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2638          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2639          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2640            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2641          !!!next-input-character;          !!!next-input-character;
2642          redo A;          redo A;
2643        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2644          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2645          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2646            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2647          !!!next-input-character;          !!!next-input-character;
2648          redo A;          redo A;
2649        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2650            !!!cp (184);
2651          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2652    
2653          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2654          !!!next-input-character;          !!!next-input-character;
2655    
2656          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2657          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2658    
2659          redo A;          redo A;
2660        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2661            !!!cp (185);
2662          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2663    
2664          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2665          ## reconsume          ## reconsume
2666    
2667          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2668          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2669    
2670          redo A;          redo A;
2671        } else {        } else {
2672            !!!cp (186);
2673          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2674          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2675    
2676            $self->{state} = BOGUS_DOCTYPE_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678          redo A;          redo A;
2679        }        }
2680      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2681        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2682          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (187);
2683            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2684            !!!next-input-character;
2685            redo A;
2686          } elsif ($self->{nc} == 0x003E) { # >
2687            !!!cp (188);
2688            !!!parse-error (type => 'unclosed PUBLIC literal');
2689    
2690            $self->{state} = DATA_STATE;
2691          !!!next-input-character;          !!!next-input-character;
2692    
2693            $self->{ct}->{quirks} = 1;
2694            !!!emit ($self->{ct}); # DOCTYPE
2695    
2696          redo A;          redo A;
2697        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2698            !!!cp (189);
2699          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2700    
2701          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2702          ## reconsume          ## reconsume
2703    
2704          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2705          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2706    
2707          redo A;          redo A;
2708        } else {        } else {
2709          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2710              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2711                .= chr $self->{nc};
2712            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2713                                  length $self->{ct}->{pubid});
2714    
2715          ## Stay in the state          ## Stay in the state
2716          !!!next-input-character;          !!!next-input-character;
2717          redo A;          redo A;
2718        }        }
2719      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2720        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2721          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2722            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2723          !!!next-input-character;          !!!next-input-character;
2724          redo A;          redo A;
2725        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2726            !!!cp (192);
2727          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2728    
2729          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2730            !!!next-input-character;
2731    
2732            $self->{ct}->{quirks} = 1;
2733            !!!emit ($self->{ct}); # DOCTYPE
2734    
2735            redo A;
2736          } elsif ($self->{nc} == -1) {
2737            !!!cp (193);
2738            !!!parse-error (type => 'unclosed PUBLIC literal');
2739    
2740            $self->{state} = DATA_STATE;
2741          ## reconsume          ## reconsume
2742    
2743          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2744          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2745    
2746          redo A;          redo A;
2747        } else {        } else {
2748          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2749              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2750                .= chr $self->{nc};
2751            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2752                                  length $self->{ct}->{pubid});
2753    
2754          ## Stay in the state          ## Stay in the state
2755          !!!next-input-character;          !!!next-input-character;
2756          redo A;          redo A;
2757        }        }
2758      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2759        if ({        if ($is_space->{$self->{nc}}) {
2760              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2761          ## Stay in the state          ## Stay in the state
2762          !!!next-input-character;          !!!next-input-character;
2763          redo A;          redo A;
2764        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2765          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2766          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2767            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2768          !!!next-input-character;          !!!next-input-character;
2769          redo A;          redo A;
2770        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2771          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2772          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2773            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2774          !!!next-input-character;          !!!next-input-character;
2775          redo A;          redo A;
2776        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2777          $self->{state} = 'data';          !!!cp (198);
2778            $self->{state} = DATA_STATE;
2779          !!!next-input-character;          !!!next-input-character;
2780    
2781          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2782    
2783          redo A;          redo A;
2784        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2785            !!!cp (199);
2786          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2787    
2788          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2789          ## recomsume          ## reconsume
2790    
2791          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2792          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2793    
2794          redo A;          redo A;
2795        } else {        } else {
2796            !!!cp (200);
2797          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2798          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2799    
2800            $self->{state} = BOGUS_DOCTYPE_STATE;
2801          !!!next-input-character;          !!!next-input-character;
2802          redo A;          redo A;
2803        }        }
2804      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2805        if ({        if ($is_space->{$self->{nc}}) {
2806              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2807          ## Stay in the state          ## Stay in the state
2808          !!!next-input-character;          !!!next-input-character;
2809          redo A;          redo A;
2810        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2811          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2812          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2813            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2814          !!!next-input-character;          !!!next-input-character;
2815          redo A;          redo A;
2816        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2817          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2818          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2819            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2820          !!!next-input-character;          !!!next-input-character;
2821          redo A;          redo A;
2822        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2823            !!!cp (204);
2824          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2825          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2826          !!!next-input-character;          !!!next-input-character;
2827    
2828          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2829          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2830    
2831          redo A;          redo A;
2832        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2833            !!!cp (205);
2834          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2835    
2836          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2837          ## recomsume          ## reconsume
2838    
2839          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2840          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2841    
2842          redo A;          redo A;
2843        } else {        } else {
2844          !!!parse-error (type => 'string after PUBLIC literal');          !!!cp (206);
2845          $self->{state} = 'bogus DOCTYPE';          !!!parse-error (type => 'string after SYSTEM');
2846            $self->{ct}->{quirks} = 1;
2847    
2848            $self->{state} = BOGUS_DOCTYPE_STATE;
2849          !!!next-input-character;          !!!next-input-character;
2850          redo A;          redo A;
2851        }        }
2852      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2853        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2854          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2855            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2856            !!!next-input-character;
2857            redo A;
2858          } elsif ($self->{nc} == 0x003E) { # >
2859            !!!cp (208);
2860            !!!parse-error (type => 'unclosed SYSTEM literal');
2861    
2862            $self->{state} = DATA_STATE;
2863          !!!next-input-character;          !!!next-input-character;
2864    
2865            $self->{ct}->{quirks} = 1;
2866            !!!emit ($self->{ct}); # DOCTYPE
2867    
2868          redo A;          redo A;
2869        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2870            !!!cp (209);
2871          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2872    
2873          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2874          ## reconsume          ## reconsume
2875    
2876          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2877          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2878    
2879          redo A;          redo A;
2880        } else {        } else {
2881          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2882              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2883                .= chr $self->{nc};
2884            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2885                                  length $self->{ct}->{sysid});
2886    
2887          ## Stay in the state          ## Stay in the state
2888          !!!next-input-character;          !!!next-input-character;
2889          redo A;          redo A;
2890        }        }
2891      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2892        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2893          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2894            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2895            !!!next-input-character;
2896            redo A;
2897          } elsif ($self->{nc} == 0x003E) { # >
2898            !!!cp (212);
2899            !!!parse-error (type => 'unclosed SYSTEM literal');
2900    
2901            $self->{state} = DATA_STATE;
2902          !!!next-input-character;          !!!next-input-character;
2903    
2904            $self->{ct}->{quirks} = 1;
2905            !!!emit ($self->{ct}); # DOCTYPE
2906    
2907          redo A;          redo A;
2908        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2909            !!!cp (213);
2910          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2911    
2912          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2913          ## reconsume          ## reconsume
2914    
2915          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2916          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2917    
2918          redo A;          redo A;
2919        } else {        } else {
2920          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2921              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2922                .= chr $self->{nc};
2923            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2924                                  length $self->{ct}->{sysid});
2925    
2926          ## Stay in the state          ## Stay in the state
2927          !!!next-input-character;          !!!next-input-character;
2928          redo A;          redo A;
2929        }        }
2930      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2931        if ({        if ($is_space->{$self->{nc}}) {
2932              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2933          ## Stay in the state          ## Stay in the state
2934          !!!next-input-character;          !!!next-input-character;
2935          redo A;          redo A;
2936        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2937          $self->{state} = 'data';          !!!cp (216);
2938            $self->{state} = DATA_STATE;
2939          !!!next-input-character;          !!!next-input-character;
2940    
2941          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2942    
2943          redo A;          redo A;
2944        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2945            !!!cp (217);
2946          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2947            $self->{state} = DATA_STATE;
2948            ## reconsume
2949    
2950          $self->{state} = 'data';          $self->{ct}->{quirks} = 1;
2951          ## recomsume          !!!emit ($self->{ct}); # DOCTYPE
   
         delete $self->{current_token}->{correct};  
         !!!emit ($self->{current_token}); # DOCTYPE  
2952    
2953          redo A;          redo A;
2954        } else {        } else {
2955            !!!cp (218);
2956          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2957          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2958    
2959            $self->{state} = BOGUS_DOCTYPE_STATE;
2960          !!!next-input-character;          !!!next-input-character;
2961          redo A;          redo A;
2962        }        }
2963      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2964        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2965          $self->{state} = 'data';          !!!cp (219);
2966            $self->{state} = DATA_STATE;
2967          !!!next-input-character;          !!!next-input-character;
2968    
2969          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2970    
2971          redo A;          redo A;
2972        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2973            !!!cp (220);
2974          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2975          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2976          ## reconsume          ## reconsume
2977    
2978          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2979    
2980          redo A;          redo A;
2981        } else {        } else {
2982            !!!cp (221);
2983            my $s = '';
2984            $self->{read_until}->($s, q[>], 0);
2985    
2986          ## Stay in the state          ## Stay in the state
2987          !!!next-input-character;          !!!next-input-character;
2988          redo A;          redo A;
2989        }        }
2990      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
2991        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
2992      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
2993    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
2994          
2995    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
2996  } # _get_next_token          !!!cp (221.1);
2997            $self->{state} = CDATA_SECTION_MSE1_STATE;
2998            !!!next-input-character;
2999            redo A;
3000          } elsif ($self->{nc} == -1) {
3001            $self->{state} = DATA_STATE;
3002            !!!next-input-character;
3003            if (length $self->{ct}->{data}) { # character
3004              !!!cp (221.2);
3005              !!!emit ($self->{ct}); # character
3006            } else {
3007              !!!cp (221.3);
3008              ## No token to emit. $self->{ct} is discarded.
3009            }        
3010            redo A;
3011          } else {
3012            !!!cp (221.4);
3013            $self->{ct}->{data} .= chr $self->{nc};
3014            $self->{read_until}->($self->{ct}->{data},
3015                                  q<]>,
3016                                  length $self->{ct}->{data});
3017    
3018  sub _tokenize_attempt_to_consume_an_entity ($) {          ## Stay in the state.
3019    my $self = shift;          !!!next-input-character;
3020            redo A;
3021          }
3022    
3023    if ({        ## ISSUE: "text tokens" in spec.
3024         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3025         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3026        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3027      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3028      ## No error          !!!next-input-character;
3029      return undef;          redo A;
3030    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3031      !!!next-input-character;          !!!cp (221.6);
3032      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3033          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3034        my $num;          ## Reconsume.
3035        X: {          redo A;
3036          my $x_char = $self->{next_input_character};        }
3037          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3038          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3039              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3040            $num ||= 0;          !!!next-input-character;
3041            $num *= 0x10;          if (length $self->{ct}->{data}) { # character
3042            $num += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3043            redo X;            !!!emit ($self->{ct}); # character
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           ## ISSUE: the spec says U+0078, which is apparently incorrect  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           ## ISSUE: the spec says U+0058, which is apparently incorrect  
           $num ||= 0;  
           $num *= 0x10;  
           $num += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $num) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           $self->{next_input_character} = 0x0023; # #  
           !!!back-next-input-character ($x_char);  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3044          } else {          } else {
3045            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3046              ## No token to emit. $self->{ct} is discarded.
3047          }          }
3048            redo A;
3049          ## TODO: check the definition for |a valid Unicode character|.        } elsif ($self->{nc} == 0x005D) { # ]
3050          ## <http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2006-December/thread.html#8189>          !!!cp (221.9); # character
3051          if ($num > 1114111 or $num == 0) {          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3052            $num = 0xFFFD; # REPLACEMENT CHARACTER          ## Stay in the state.
3053            ## ISSUE: Why this is not an error?          !!!next-input-character;
3054          } elsif (0x80 <= $num and $num <= 0x9F) {          redo A;
3055            !!!parse-error (type => sprintf 'c1 entity:U+%04X', $num);        } else {
3056            $num = $c1_entity_char->{$num};          !!!cp (221.11);
3057          }          $self->{ct}->{data} .= ']]'; # character
3058            $self->{state} = CDATA_SECTION_STATE;
3059          return {type => 'character', data => chr $num};          ## Reconsume.
3060        } # X          redo A;
3061      } elsif (0x0030 <= $self->{next_input_character} and        }
3062               $self->{next_input_character} <= 0x0039) { # 0..9      } elsif ($self->{state} == ENTITY_STATE) {
3063        my $code = $self->{next_input_character} - 0x0030;        if ($is_space->{$self->{nc}} or
3064        !!!next-input-character;            {
3065                      0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3066        while (0x0030 <= $self->{next_input_character} and              $self->{entity_add} => 1,
3067                  $self->{next_input_character} <= 0x0039) { # 0..9            }->{$self->{nc}}) {
3068          $code *= 10;          !!!cp (1001);
3069          $code += $self->{next_input_character} - 0x0030;          ## Don't consume
3070                    ## No error
3071            ## Return nothing.
3072            #
3073          } elsif ($self->{nc} == 0x0023) { # #
3074            !!!cp (999);
3075            $self->{state} = ENTITY_HASH_STATE;
3076            $self->{s_kwd} = '#';
3077            !!!next-input-character;
3078            redo A;
3079          } elsif ((0x0041 <= $self->{nc} and
3080                    $self->{nc} <= 0x005A) or # A..Z
3081                   (0x0061 <= $self->{nc} and
3082                    $self->{nc} <= 0x007A)) { # a..z
3083            !!!cp (998);
3084            require Whatpm::_NamedEntityList;
3085            $self->{state} = ENTITY_NAME_STATE;
3086            $self->{s_kwd} = chr $self->{nc};
3087            $self->{entity__value} = $self->{s_kwd};
3088            $self->{entity__match} = 0;
3089          !!!next-input-character;          !!!next-input-character;
3090            redo A;
3091          } else {
3092            !!!cp (1027);
3093            !!!parse-error (type => 'bare ero');
3094            ## Return nothing.
3095            #
3096        }        }
3097    
3098        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3099          ## reference" algorithm.  In other word, there is an "&" character
3100          ## that does not introduce a character reference, which would be
3101          ## appended to the parent element or the attribute value in later
3102          ## process of the tokenizer.
3103    
3104          if ($self->{prev_state} == DATA_STATE) {
3105            !!!cp (997);
3106            $self->{state} = $self->{prev_state};
3107            ## Reconsume.
3108            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3109                      line => $self->{line_prev},
3110                      column => $self->{column_prev},
3111                     });
3112            redo A;
3113          } else {
3114            !!!cp (996);
3115            $self->{ca}->{value} .= '&';
3116            $self->{state} = $self->{prev_state};
3117            ## Reconsume.
3118            redo A;
3119          }
3120        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3121          if ($self->{nc} == 0x0078 or # x
3122              $self->{nc} == 0x0058) { # X
3123            !!!cp (995);
3124            $self->{state} = HEXREF_X_STATE;
3125            $self->{s_kwd} .= chr $self->{nc};
3126            !!!next-input-character;
3127            redo A;
3128          } elsif (0x0030 <= $self->{nc} and
3129                   $self->{nc} <= 0x0039) { # 0..9
3130            !!!cp (994);
3131            $self->{state} = NCR_NUM_STATE;
3132            $self->{s_kwd} = $self->{nc} - 0x0030;
3133          !!!next-input-character;          !!!next-input-character;
3134            redo A;
3135        } else {        } else {
3136            !!!parse-error (type => 'bare nero',
3137                            line => $self->{line_prev},
3138                            column => $self->{column_prev} - 1);
3139    
3140            ## NOTE: According to the spec algorithm, nothing is returned,
3141            ## and then "&#" is appended to the parent element or the attribute
3142            ## value in the later processing.
3143    
3144            if ($self->{prev_state} == DATA_STATE) {
3145              !!!cp (1019);
3146              $self->{state} = $self->{prev_state};
3147              ## Reconsume.
3148              !!!emit ({type => CHARACTER_TOKEN,
3149                        data => '&#',
3150                        line => $self->{line_prev},
3151                        column => $self->{column_prev} - 1,
3152                       });
3153              redo A;
3154            } else {
3155              !!!cp (993);
3156              $self->{ca}->{value} .= '&#';
3157              $self->{state} = $self->{prev_state};
3158              ## Reconsume.
3159              redo A;
3160            }
3161          }
3162        } elsif ($self->{state} == NCR_NUM_STATE) {
3163          if (0x0030 <= $self->{nc} and
3164              $self->{nc} <= 0x0039) { # 0..9
3165            !!!cp (1012);
3166            $self->{s_kwd} *= 10;
3167            $self->{s_kwd} += $self->{nc} - 0x0030;
3168            
3169            ## Stay in the state.
3170            !!!next-input-character;
3171            redo A;
3172          } elsif ($self->{nc} == 0x003B) { # ;
3173            !!!cp (1013);
3174            !!!next-input-character;
3175            #
3176          } else {
3177            !!!cp (1014);
3178          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3179            ## Reconsume.
3180            #
3181        }        }
3182    
3183        ## TODO: check the definition for |a valid Unicode character|.        my $code = $self->{s_kwd};
3184        if ($code > 1114111 or $code == 0) {        my $l = $self->{line_prev};
3185          $code = 0xFFFD; # REPLACEMENT CHARACTER        my $c = $self->{column_prev};
3186          ## ISSUE: Why this is not an error?        if ($charref_map->{$code}) {
3187        } elsif (0x80 <= $code and $code <= 0x9F) {          !!!cp (1015);
3188          !!!parse-error (type => sprintf 'c1 entity:U+%04X', $code);          !!!parse-error (type => 'invalid character reference',
3189          $code = $c1_entity_char->{$code};                          text => (sprintf 'U+%04X', $code),
3190                            line => $l, column => $c);
3191            $code = $charref_map->{$code};
3192          } elsif ($code > 0x10FFFF) {
3193            !!!cp (1016);
3194            !!!parse-error (type => 'invalid character reference',
3195                            text => (sprintf 'U-%08X', $code),
3196                            line => $l, column => $c);
3197            $code = 0xFFFD;
3198          }
3199    
3200          if ($self->{prev_state} == DATA_STATE) {
3201            !!!cp (992);
3202            $self->{state} = $self->{prev_state};
3203            ## Reconsume.
3204            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3205                      line => $l, column => $c,
3206                     });
3207            redo A;
3208          } else {
3209            !!!cp (991);
3210            $self->{ca}->{value} .= chr $code;
3211            $self->{ca}->{has_reference} = 1;
3212            $self->{state} = $self->{prev_state};
3213            ## Reconsume.
3214            redo A;
3215          }
3216        } elsif ($self->{state} == HEXREF_X_STATE) {
3217          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3218              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3219              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3220            # 0..9, A..F, a..f
3221            !!!cp (990);
3222            $self->{state} = HEXREF_HEX_STATE;
3223            $self->{s_kwd} = 0;
3224            ## Reconsume.
3225            redo A;
3226          } else {
3227            !!!parse-error (type => 'bare hcro',
3228                            line => $self->{line_prev},
3229                            column => $self->{column_prev} - 2);
3230    
3231            ## NOTE: According to the spec algorithm, nothing is returned,
3232            ## and then "&#" followed by "X" or "x" is appended to the parent
3233            ## element or the attribute value in the later processing.
3234    
3235            if ($self->{prev_state} == DATA_STATE) {
3236              !!!cp (1005);
3237              $self->{state} = $self->{prev_state};
3238              ## Reconsume.
3239              !!!emit ({type => CHARACTER_TOKEN,
3240                        data => '&' . $self->{s_kwd},
3241                        line => $self->{line_prev},
3242                        column => $self->{column_prev} - length $self->{s_kwd},
3243                       });
3244              redo A;
3245            } else {
3246              !!!cp (989);
3247              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3248              $self->{state} = $self->{prev_state};
3249              ## Reconsume.
3250              redo A;
3251            }
3252        }        }
3253              } elsif ($self->{state} == HEXREF_HEX_STATE) {
3254        return {type => 'character', data => chr $code};        if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3255      } else {          # 0..9
3256        !!!parse-error (type => 'bare nero');          !!!cp (1002);
3257        !!!back-next-input-character ($self->{next_input_character});          $self->{s_kwd} *= 0x10;
3258        $self->{next_input_character} = 0x0023; # #          $self->{s_kwd} += $self->{nc} - 0x0030;
3259        return undef;          ## Stay in the state.
3260      }          !!!next-input-character;
3261    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3262              $self->{next_input_character} <= 0x005A) or        } elsif (0x0061 <= $self->{nc} and
3263             (0x0061 <= $self->{next_input_character} and                 $self->{nc} <= 0x0066) { # a..f
3264              $self->{next_input_character} <= 0x007A)) {          !!!cp (1003);
3265      my $entity_name = chr $self->{next_input_character};          $self->{s_kwd} *= 0x10;
3266      !!!next-input-character;          $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3267            ## Stay in the state.
3268      my $value = $entity_name;          !!!next-input-character;
3269      my $match;          redo A;
3270      require Whatpm::_NamedEntityList;        } elsif (0x0041 <= $self->{nc} and
3271      our $EntityChar;                 $self->{nc} <= 0x0046) { # A..F
3272            !!!cp (1004);
3273      while (length $entity_name < 10 and          $self->{s_kwd} *= 0x10;
3274             ## NOTE: Some number greater than the maximum length of entity name          $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3275             ((0x0041 <= $self->{next_input_character} and # a          ## Stay in the state.
3276               $self->{next_input_character} <= 0x005A) or # x          !!!next-input-character;
3277              (0x0061 <= $self->{next_input_character} and # a          redo A;
3278               $self->{next_input_character} <= 0x007A) or # z        } elsif ($self->{nc} == 0x003B) { # ;
3279              (0x0030 <= $self->{next_input_character} and # 0          !!!cp (1006);
3280               $self->{next_input_character} <= 0x0039) or # 9          !!!next-input-character;
3281              $self->{next_input_character} == 0x003B)) { # ;          #
3282        $entity_name .= chr $self->{next_input_character};        } else {
3283        if (defined $EntityChar->{$entity_name}) {          !!!cp (1007);
3284          $value = $EntityChar->{$entity_name};          !!!parse-error (type => 'no refc',
3285          if ($self->{next_input_character} == 0x003B) { # ;                          line => $self->{line},
3286            $match = 1;                          column => $self->{column});
3287            ## Reconsume.
3288            #
3289          }
3290    
3291          my $code = $self->{s_kwd};
3292          my $l = $self->{line_prev};
3293          my $c = $self->{column_prev};
3294          if ($charref_map->{$code}) {
3295            !!!cp (1008);
3296            !!!parse-error (type => 'invalid character reference',
3297                            text => (sprintf 'U+%04X', $code),
3298                            line => $l, column => $c);
3299            $code = $charref_map->{$code};
3300          } elsif ($code > 0x10FFFF) {
3301            !!!cp (1009);
3302            !!!parse-error (type => 'invalid character reference',
3303                            text => (sprintf 'U-%08X', $code),
3304                            line => $l, column => $c);
3305            $code = 0xFFFD;
3306          }
3307    
3308          if ($self->{prev_state} == DATA_STATE) {
3309            !!!cp (988);
3310            $self->{state} = $self->{prev_state};
3311            ## Reconsume.
3312            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3313                      line => $l, column => $c,
3314                     });
3315            redo A;
3316          } else {
3317            !!!cp (987);
3318            $self->{ca}->{value} .= chr $code;
3319            $self->{ca}->{has_reference} = 1;
3320            $self->{state} = $self->{prev_state};
3321            ## Reconsume.
3322            redo A;
3323          }
3324        } elsif ($self->{state} == ENTITY_NAME_STATE) {
3325          if (length $self->{s_kwd} < 30 and
3326              ## NOTE: Some number greater than the maximum length of entity name
3327              ((0x0041 <= $self->{nc} and # a
3328                $self->{nc} <= 0x005A) or # x
3329               (0x0061 <= $self->{nc} and # a
3330                $self->{nc} <= 0x007A) or # z
3331               (0x0030 <= $self->{nc} and # 0
3332                $self->{nc} <= 0x0039) or # 9
3333               $self->{nc} == 0x003B)) { # ;
3334            our $EntityChar;
3335            $self->{s_kwd} .= chr $self->{nc};
3336            if (defined $EntityChar->{$self->{s_kwd}}) {
3337              if ($self->{nc} == 0x003B) { # ;
3338                !!!cp (1020);
3339                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3340                $self->{entity__match} = 1;
3341                !!!next-input-character;
3342                #
3343              } else {
3344                !!!cp (1021);
3345                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3346                $self->{entity__match} = -1;
3347                ## Stay in the state.
3348                !!!next-input-character;
3349                redo A;
3350              }
3351            } else {
3352              !!!cp (1022);
3353              $self->{entity__value} .= chr $self->{nc};
3354              $self->{entity__match} *= 2;
3355              ## Stay in the state.
3356            !!!next-input-character;            !!!next-input-character;
3357            last;            redo A;
3358            }
3359          }
3360    
3361          my $data;
3362          my $has_ref;
3363          if ($self->{entity__match} > 0) {
3364            !!!cp (1023);
3365            $data = $self->{entity__value};
3366            $has_ref = 1;
3367            #
3368          } elsif ($self->{entity__match} < 0) {
3369            !!!parse-error (type => 'no refc');
3370            if ($self->{prev_state} != DATA_STATE and # in attribute
3371                $self->{entity__match} < -1) {
3372              !!!cp (1024);
3373              $data = '&' . $self->{s_kwd};
3374              #
3375          } else {          } else {
3376            $match = -1;            !!!cp (1025);
3377              $data = $self->{entity__value};
3378              $has_ref = 1;
3379              #
3380          }          }
3381        } else {        } else {
3382          $value .= chr $self->{next_input_character};          !!!cp (1026);
3383            !!!parse-error (type => 'bare ero',
3384                            line => $self->{line_prev},
3385                            column => $self->{column_prev} - length $self->{s_kwd});
3386            $data = '&' . $self->{s_kwd};
3387            #
3388          }
3389      
3390          ## NOTE: In these cases, when a character reference is found,
3391          ## it is consumed and a character token is returned, or, otherwise,
3392          ## nothing is consumed and returned, according to the spec algorithm.
3393          ## In this implementation, anything that has been examined by the
3394          ## tokenizer is appended to the parent element or the attribute value
3395          ## as string, either literal string when no character reference or
3396          ## entity-replaced string otherwise, in this stage, since any characters
3397          ## that would not be consumed are appended in the data state or in an
3398          ## appropriate attribute value state anyway.
3399    
3400          if ($self->{prev_state} == DATA_STATE) {
3401            !!!cp (986);
3402            $self->{state} = $self->{prev_state};
3403            ## Reconsume.
3404            !!!emit ({type => CHARACTER_TOKEN,
3405                      data => $data,
3406                      line => $self->{line_prev},
3407                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3408                     });
3409            redo A;
3410          } else {
3411            !!!cp (985);
3412            $self->{ca}->{value} .= $data;
3413            $self->{ca}->{has_reference} = 1 if $has_ref;
3414            $self->{state} = $self->{prev_state};
3415            ## Reconsume.
3416            redo A;
3417        }        }
       !!!next-input-character;  
     }  
       
     if ($match > 0) {  
       return {type => 'character', data => $value};  
     } elsif ($match < 0) {  
       !!!parse-error (type => 'refc');  
       return {type => 'character', data => $value};  
3418      } else {      } else {
3419        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       !!!back-token ({type => 'character', data => $value});  
       return undef;  
3420      }      }
3421    } else {    } # A  
3422      ## no characters are consumed  
3423      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3424      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3425    
3426  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3427    my $self = shift;    my $self = shift;
# Line 1728  sub _initialize_tree_constructor ($) { Line 3430  sub _initialize_tree_constructor ($) {
3430    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3431    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3432    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3433      $self->{document}->set_user_data (manakai_source_line => 1);
3434      $self->{document}->set_user_data (manakai_source_column => 1);
3435  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3436    
3437  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1754  sub _construct_tree ($) { Line 3458  sub _construct_tree ($) {
3458        
3459    !!!next-token;    !!!next-token;
3460    
   $self->{insertion_mode} = 'before head';  
3461    undef $self->{form_element};    undef $self->{form_element};
3462    undef $self->{head_element};    undef $self->{head_element};
3463    $self->{open_elements} = [];    $self->{open_elements} = [];
3464    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3465    
3466      ## NOTE: The "initial" insertion mode.
3467    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3468    
3469      ## NOTE: The "before html" insertion mode.
3470    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3471      $self->{insertion_mode} = BEFORE_HEAD_IM;
3472    
3473      ## NOTE: The "before head" insertion mode and so on.
3474    $self->_tree_construction_main;    $self->_tree_construction_main;
3475  } # _construct_tree  } # _construct_tree
3476    
3477  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3478    my $self = shift;    my $self = shift;
3479    
3480      ## NOTE: "initial" insertion mode
3481    
3482    INITIAL: {    INITIAL: {
3483      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
3484        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3485        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
3486        ## language.        ## language.
3487        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3488        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3489        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3490        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3491            defined $token->{public_identifier} or            defined $token->{sysid}) {
3492            defined $token->{system_identifier}) {          !!!cp ('t1');
3493          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3494        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3495          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3496          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3497          } elsif (defined $token->{pubid}) {
3498            if ($token->{pubid} eq 'XSLT-compat') {
3499              !!!cp ('t1.2');
3500              !!!parse-error (type => 'XSLT-compat', token => $token,
3501                              level => $self->{level}->{should});
3502            } else {
3503              !!!parse-error (type => 'not HTML5', token => $token);
3504            }
3505          } else {
3506            !!!cp ('t3');
3507            #
3508        }        }
3509                
3510        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3511          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3512        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3513            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3514        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3515            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3516        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3517        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3518        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3519                
3520        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3521            !!!cp ('t4');
3522          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3523        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3524          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3525          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3526          if ({          my $prefix = [
3527            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3528            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3529            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3530            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3531            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3532            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3533            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3534            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3535            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3536            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3537            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3538            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3539            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3540            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3541            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3542            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3543            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3544            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3545            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3546            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3547            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3548            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3549            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3550            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3551            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3552            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3553            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3554            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3555            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3556            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3557            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3558            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3559            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3560            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3561            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3562            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3563            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3564            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3565            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3566            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3567            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3568            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3569            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3570            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3571            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3572            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3573            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3574            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3575            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3576            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3577            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3578            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3579            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3580            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3581            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3582            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3583            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3584            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3585            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3586            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3587            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3588            "-//W3C//DTD W3 HTML//EN" => 1,            }
3589            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3590            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3591            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3592            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3593            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3594            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3595            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3596          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3597                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3598            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3599                !!!cp ('t6');
3600              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3601            } else {            } else {
3602                !!!cp ('t7');
3603              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3604            }            }
3605          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3606                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3607              !!!cp ('t8');
3608            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3609            } else {
3610              !!!cp ('t9');
3611          }          }
3612          } else {
3613            !!!cp ('t10');
3614        }        }
3615        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3616          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3617          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3618          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") {
3619              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3620              ## marked as quirks.
3621            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3622              !!!cp ('t11');
3623            } else {
3624              !!!cp ('t12');
3625          }          }
3626          } else {
3627            !!!cp ('t13');
3628        }        }
3629                
3630        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3631        !!!next-token;        !!!next-token;
3632        return;        return;
3633      } elsif ({      } elsif ({
3634                'start tag' => 1,                START_TAG_TOKEN, 1,
3635                'end tag' => 1,                END_TAG_TOKEN, 1,
3636                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
3637               }->{$token->{type}}) {               }->{$token->{type}}) {
3638        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3639          !!!parse-error (type => 'no DOCTYPE', token => $token);
3640        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3641        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3642        ## reprocess        ## reprocess
3643          !!!ack-later;
3644        return;        return;
3645      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3646        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3647          ## Ignore the token          ## Ignore the token
3648    
3649          unless (length $token->{data}) {          unless (length $token->{data}) {
3650            ## Stay in the phase            !!!cp ('t15');
3651              ## Stay in the insertion mode.
3652            !!!next-token;            !!!next-token;
3653            redo INITIAL;            redo INITIAL;
3654            } else {
3655              !!!cp ('t16');
3656          }          }
3657          } else {
3658            !!!cp ('t17');
3659        }        }
3660    
3661        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3662        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3663        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3664        ## reprocess        ## reprocess
3665        return;        return;
3666      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
3667          !!!cp ('t18');
3668        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3669        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3670                
3671        ## Stay in the phase.        ## Stay in the insertion mode.
3672        !!!next-token;        !!!next-token;
3673        redo INITIAL;        redo INITIAL;
3674      } else {      } else {
3675        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
3676      }      }
3677    } # INITIAL    } # INITIAL
3678    
3679      die "$0: _tree_construction_initial: This should be never reached";
3680  } # _tree_construction_initial  } # _tree_construction_initial
3681    
3682  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3683    my $self = shift;    my $self = shift;
3684    
3685      ## NOTE: "before html" insertion mode.
3686        
3687    B: {    B: {
3688        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3689          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3690            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3691          ## Ignore the token          ## Ignore the token
3692          ## Stay in the phase          ## Stay in the insertion mode.
3693          !!!next-token;          !!!next-token;
3694          redo B;          redo B;
3695        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3696            !!!cp ('t20');
3697          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3698          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3699          ## Stay in the phase          ## Stay in the insertion mode.
3700          !!!next-token;          !!!next-token;
3701          redo B;          redo B;
3702        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3703          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3704            $self->{document}->manakai_append_text ($1);            ## Ignore the token.
3705            ## ISSUE: DOM3 Core does not allow Document > Text  
3706            unless (length $token->{data}) {            unless (length $token->{data}) {
3707              ## Stay in the phase              !!!cp ('t21');
3708                ## Stay in the insertion mode.
3709              !!!next-token;              !!!next-token;
3710              redo B;              redo B;
3711              } else {
3712                !!!cp ('t22');
3713            }            }
3714            } else {
3715              !!!cp ('t23');
3716          }          }
3717    
3718            $self->{application_cache_selection}->(undef);
3719    
3720          #          #
3721          } elsif ($token->{type} == START_TAG_TOKEN) {
3722            if ($token->{tag_name} eq 'html') {
3723              my $root_element;
3724              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3725              $self->{document}->append_child ($root_element);
3726              push @{$self->{open_elements}},
3727                  [$root_element, $el_category->{html}];
3728    
3729              if ($token->{attributes}->{manifest}) {
3730                !!!cp ('t24');
3731                $self->{application_cache_selection}
3732                    ->($token->{attributes}->{manifest}->{value});
3733                ## ISSUE: Spec is unclear on relative references.
3734                ## According to Hixie (#whatwg 2008-03-19), it should be
3735                ## resolved against the base URI of the document in HTML
3736                ## or xml:base of the element in XHTML.
3737              } else {
3738                !!!cp ('t25');
3739                $self->{application_cache_selection}->(undef);
3740              }
3741    
3742              !!!nack ('t25c');
3743    
3744              !!!next-token;
3745              return; ## Go to the "before head" insertion mode.
3746            } else {
3747              !!!cp ('t25.1');
3748              #
3749            }
3750        } elsif ({        } elsif ({
3751                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3752                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3753                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3754          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3755          #          #
3756        } else {        } else {
3757          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3758        }        }
3759        my $root_element; !!!create-element ($root_element, 'html');  
3760        $self->{document}->append_child ($root_element);      my $root_element;
3761        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3762        #$phase = 'main';      $self->{document}->append_child ($root_element);
3763        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3764        #redo B;  
3765        return;      $self->{application_cache_selection}->(undef);
3766    
3767        ## NOTE: Reprocess the token.
3768        !!!ack-later;
3769        return; ## Go to the "before head" insertion mode.
3770    
3771        ## ISSUE: There is an issue in the spec
3772    } # B    } # B
3773    
3774      die "$0: _tree_construction_root_element: This should never be reached";
3775  } # _tree_construction_root_element  } # _tree_construction_root_element
3776    
3777  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 1991  sub _reset_insertion_mode ($) { Line 3786  sub _reset_insertion_mode ($) {
3786            
3787      ## Step 3      ## Step 3
3788      S3: {      S3: {
3789        $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3790        if (defined $self->{inner_html_node}) {          $last = 1;
3791          if ($self->{inner_html_node}->[1] eq 'td' or          if (defined $self->{inner_html_node}) {
3792              $self->{inner_html_node}->[1] eq 'th') {            !!!cp ('t28');
3793              $node = $self->{inner_html_node};
3794            } else {
3795              die "_reset_insertion_mode: t27";
3796            }
3797          }
3798          
3799          ## Step 4..14
3800          my $new_mode;
3801          if ($node->[1] & FOREIGN_EL) {
3802            !!!cp ('t28.1');
3803            ## NOTE: Strictly spaking, the line below only applies to MathML and
3804            ## SVG elements.  Currently the HTML syntax supports only MathML and
3805            ## SVG elements as foreigners.
3806            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3807          } elsif ($node->[1] & TABLE_CELL_EL) {
3808            if ($last) {
3809              !!!cp ('t28.2');
3810            #            #
3811          } else {          } else {
3812            $node = $self->{inner_html_node};            !!!cp ('t28.3');
3813              $new_mode = IN_CELL_IM;
3814          }          }
3815          } else {
3816            !!!cp ('t28.4');
3817            $new_mode = {
3818                          select => IN_SELECT_IM,
3819                          ## NOTE: |option| and |optgroup| do not set
3820                          ## insertion mode to "in select" by themselves.
3821                          tr => IN_ROW_IM,
3822                          tbody => IN_TABLE_BODY_IM,
3823                          thead => IN_TABLE_BODY_IM,
3824                          tfoot => IN_TABLE_BODY_IM,
3825                          caption => IN_CAPTION_IM,
3826                          colgroup => IN_COLUMN_GROUP_IM,
3827                          table => IN_TABLE_IM,
3828                          head => IN_BODY_IM, # not in head!
3829                          body => IN_BODY_IM,
3830                          frameset => IN_FRAMESET_IM,
3831                         }->{$node->[0]->manakai_local_name};
3832        }        }
       
       ## Step 4..13  
       my $new_mode = {  
                       select => 'in select',  
                       td => 'in cell',  
                       th => 'in cell',  
                       tr => 'in row',  
                       tbody => 'in table body',  
                       thead => 'in table head',  
                       tfoot => 'in table foot',  
                       caption => 'in caption',  
                       colgroup => 'in column group',  
                       table => 'in table',  
                       head => 'in body', # not in head!  
                       body => 'in body',  
                       frameset => 'in frameset',  
                      }->{$node->[1]};  
3833        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3834                
3835        ## Step 14        ## Step 15
3836        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3837          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3838            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3839              $self->{insertion_mode} = BEFORE_HEAD_IM;
3840          } else {          } else {
3841            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3842              !!!cp ('t30');
3843              $self->{insertion_mode} = AFTER_HEAD_IM;
3844          }          }
3845          return;          return;
3846          } else {
3847            !!!cp ('t31');
3848        }        }
3849                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3850        ## Step 16        ## Step 16
3851          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3852          
3853          ## Step 17
3854        $i--;        $i--;
3855        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3856                
3857        ## Step 17        ## Step 18
3858        redo S3;        redo S3;
3859      } # S3      } # S3
3860    
3861      die "$0: _reset_insertion_mode: This line should never be reached";
3862  } # _reset_insertion_mode  } # _reset_insertion_mode
3863    
3864  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3865    my $self = shift;    my $self = shift;
3866    
   my $phase = 'main';  
   
3867    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3868    
3869    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2062  sub _tree_construction_main ($) { Line 3880  sub _tree_construction_main ($) {
3880      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3881      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3882        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3883            !!!cp ('t32');
3884          return;          return;
3885        }        }
3886      }      }
# Line 2076  sub _tree_construction_main ($) { Line 3895  sub _tree_construction_main ($) {
3895    
3896        ## Step 6        ## Step 6
3897        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3898            !!!cp ('t33_1');
3899          #          #
3900        } else {        } else {
3901          my $in_open_elements;          my $in_open_elements;
3902          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3903            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3904                !!!cp ('t33');
3905              $in_open_elements = 1;              $in_open_elements = 1;
3906              last OE;              last OE;
3907            }            }
3908          }          }
3909          if ($in_open_elements) {          if ($in_open_elements) {
3910              !!!cp ('t34');
3911            #            #
3912          } else {          } else {
3913              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3914              !!!cp ('t35');
3915            redo S4;            redo S4;
3916          }          }
3917        }        }
# Line 2110  sub _tree_construction_main ($) { Line 3934  sub _tree_construction_main ($) {
3934    
3935        ## Step 11        ## Step 11
3936        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3937            !!!cp ('t36');
3938          ## Step 7'          ## Step 7'
3939          $i++;          $i++;
3940          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3941                    
3942          redo S7;          redo S7;
3943        }        }
3944    
3945          !!!cp ('t37');
3946      } # S7      } # S7
3947    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3948    
3949    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3950      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3951        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3952            !!!cp ('t38');
3953          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3954          return;          return;
3955        }        }
3956      }      }
3957    
3958        !!!cp ('t39');
3959    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3960    
3961    my $parse_rcdata = sub ($$) {    my $insert;
3962      my ($content_model_flag, $insert) = @_;  
3963      my $parse_rcdata = sub ($) {
3964        my ($content_model_flag) = @_;
3965    
3966      ## Step 1      ## Step 1
3967      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3968      my $el;      my $el;
3969      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3970    
3971      ## Step 2      ## Step 2
3972      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3973    
3974      ## Step 3      ## Step 3
3975      $self->{content_model_flag} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3976      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3977    
3978      ## Step 4      ## Step 4
3979      my $text = '';      my $text = '';
3980        !!!nack ('t40.1');
3981      !!!next-token;      !!!next-token;
3982      while ($token->{type} eq 'character') { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3983          !!!cp ('t40');
3984        $text .= $token->{data};        $text .= $token->{data};
3985        !!!next-token;        !!!next-token;
3986      }      }
3987    
3988      ## Step 5      ## Step 5
3989      if (length $text) {      if (length $text) {
3990          !!!cp ('t41');
3991        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
3992        $el->append_child ($text);        $el->append_child ($text);
3993      }      }
3994    
3995      ## Step 6      ## Step 6
3996      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
3997    
3998      ## Step 7      ## Step 7
3999      if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
4000            $token->{tag_name} eq $start_tag_name) {
4001          !!!cp ('t42');
4002        ## Ignore the token        ## Ignore the token
4003      } else {      } else {
4004        !!!parse-error (type => 'in '.$content_model_flag.':#'.$token->{type});        ## NOTE: An end-of-file token.
4005          if ($content_model_flag == CDATA_CONTENT_MODEL) {
4006            !!!cp ('t43');
4007            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4008          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4009            !!!cp ('t44');
4010            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4011          } else {
4012            die "$0: $content_model_flag in parse_rcdata";
4013          }
4014      }      }
4015      !!!next-token;      !!!next-token;
4016    }; # $parse_rcdata    }; # $parse_rcdata
4017    
4018    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
4019      my $script_el;      my $script_el;
4020      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4021      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4022    
4023      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
4024      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4025            
4026      my $text = '';      my $text = '';
4027        !!!nack ('t45.1');
4028      !!!next-token;      !!!next-token;
4029      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
4030          !!!cp ('t45');
4031        $text .= $token->{data};        $text .= $token->{data};
4032        !!!next-token;        !!!next-token;
4033      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4034      if (length $text) {      if (length $text) {
4035          !!!cp ('t46');
4036        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4037      }      }
4038                                
4039      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
4040    
4041      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
4042          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4043          !!!cp ('t47');
4044        ## Ignore the token        ## Ignore the token
4045      } else {      } else {
4046        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4047          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4048        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4049        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4050      }      }
4051            
4052      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4053          !!!cp ('t49');
4054        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4055      } else {      } else {
4056          !!!cp ('t50');
4057        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4058        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4059    
# Line 2215  sub _tree_construction_main ($) { Line 4067  sub _tree_construction_main ($) {
4067      !!!next-token;      !!!next-token;
4068    }; # $script_start_tag    }; # $script_start_tag
4069    
4070      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4071      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4072      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4073    
4074    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4075      my $tag_name = shift;      my $end_tag_token = shift;
4076        my $tag_name = $end_tag_token->{tag_name};
4077    
4078        ## NOTE: The adoption agency algorithm (AAA).
4079    
4080      FET: {      FET: {
4081        ## Step 1        ## Step 1
4082        my $formatting_element;        my $formatting_element;
4083        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4084        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4085          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4086              !!!cp ('t52');
4087              last AFE;
4088            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4089                         eq $tag_name) {
4090              !!!cp ('t51');
4091            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4092            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4093            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4094          }          }
4095        } # AFE        } # AFE
4096        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4097          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4098            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4099          ## Ignore the token          ## Ignore the token
4100          !!!next-token;          !!!next-token;
4101          return;          return;
# Line 2244  sub _tree_construction_main ($) { Line 4107  sub _tree_construction_main ($) {
4107          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4108          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4109            if ($in_scope) {            if ($in_scope) {
4110                !!!cp ('t54');
4111              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4112              last INSCOPE;              last INSCOPE;
4113            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4114              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4115                !!!parse-error (type => 'unmatched end tag',
4116                                text => $token->{tag_name},
4117                                token => $end_tag_token);
4118              ## Ignore the token              ## Ignore the token
4119              !!!next-token;              !!!next-token;
4120              return;              return;
4121            }            }
4122          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4123                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4124            $in_scope = 0;            $in_scope = 0;
4125          }          }
4126        } # INSCOPE        } # INSCOPE
4127        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4128          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4129            !!!parse-error (type => 'unmatched end tag',
4130                            text => $token->{tag_name},
4131                            token => $end_tag_token);
4132          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4133          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4134          return;          return;
4135        }        }
4136        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4137          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4138            !!!parse-error (type => 'not closed',
4139                            text => $self->{open_elements}->[-1]->[0]
4140                                ->manakai_local_name,
4141                            token => $end_tag_token);
4142        }        }
4143                
4144        ## Step 2        ## Step 2
# Line 2274  sub _tree_construction_main ($) { Line 4146  sub _tree_construction_main ($) {
4146        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4147        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4148          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4149          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4150              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4151              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4152               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4153              !!!cp ('t59');
4154            $furthest_block = $node;            $furthest_block = $node;
4155            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4156          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4157              !!!cp ('t60');
4158            last OE;            last OE;
4159          }          }
4160        } # OE        } # OE
4161                
4162        ## Step 3        ## Step 3
4163        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4164            !!!cp ('t61');
4165          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4166          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4167          !!!next-token;          !!!next-token;
# Line 2299  sub _tree_construction_main ($) { Line 4174  sub _tree_construction_main ($) {
4174        ## Step 5        ## Step 5
4175        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4176        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4177            !!!cp ('t62');
4178          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4179        }        }
4180                
# Line 2321  sub _tree_construction_main ($) { Line 4197  sub _tree_construction_main ($) {
4197          S7S2: {          S7S2: {
4198            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4199              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4200                  !!!cp ('t63');
4201                $node_i_in_active = $_;                $node_i_in_active = $_;
4202                last S7S2;                last S7S2;
4203              }              }
# Line 2334  sub _tree_construction_main ($) { Line 4211  sub _tree_construction_main ($) {
4211                    
4212          ## Step 4          ## Step 4
4213          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4214              !!!cp ('t64');
4215            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4216          }          }
4217                    
4218          ## Step 5          ## Step 5
4219          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4220              !!!cp ('t65');
4221            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4222            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4223            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2356  sub _tree_construction_main ($) { Line 4235  sub _tree_construction_main ($) {
4235        } # S7          } # S7  
4236                
4237        ## Step 8        ## Step 8
4238        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4239            my $foster_parent_element;
4240            my $next_sibling;
4241            OE: for (reverse 0..$#{$self->{open_elements}}) {
4242              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4243                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4244                                 if (defined $parent and $parent->node_type == 1) {
4245                                   !!!cp ('t65.1');
4246                                   $foster_parent_element = $parent;
4247                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4248                                 } else {
4249                                   !!!cp ('t65.2');
4250                                   $foster_parent_element
4251                                     = $self->{open_elements}->[$_ - 1]->[0];
4252                                 }
4253                                 last OE;
4254                               }
4255                             } # OE
4256                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4257                               unless defined $foster_parent_element;
4258            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4259            $open_tables->[-1]->[1] = 1; # tainted
4260          } else {
4261            !!!cp ('t65.3');
4262            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4263          }
4264                
4265        ## Step 9        ## Step 9
4266        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2373  sub _tree_construction_main ($) { Line 4277  sub _tree_construction_main ($) {
4277        my $i;        my $i;
4278        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4279          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4280              !!!cp ('t66');
4281            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4282            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4283          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4284              !!!cp ('t67');
4285            $i = $_;            $i = $_;
4286          }          }
4287        } # AFE        } # AFE
# Line 2385  sub _tree_construction_main ($) { Line 4291  sub _tree_construction_main ($) {
4291        undef $i;        undef $i;
4292        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4293          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4294              !!!cp ('t68');
4295            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4296            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4297          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4298              !!!cp ('t69');
4299            $i = $_;            $i = $_;
4300          }          }
4301        } # OE        } # OE
# Line 2398  sub _tree_construction_main ($) { Line 4306  sub _tree_construction_main ($) {
4306      } # FET      } # FET
4307    }; # $formatting_end_tag    }; # $formatting_end_tag
4308    
4309    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4310      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4311    }; # $insert_to_current    }; # $insert_to_current
4312    
4313    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4314                         my $child = shift;      my $child = shift;
4315                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4316                              table => 1, tbody => 1, tfoot => 1,        # MUST
4317                              thead => 1, tr => 1,        my $foster_parent_element;
4318                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4319                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4320                           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') {  
4321                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4322                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4323                                   !!!cp ('t70');
4324                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4325                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4326                               } else {                               } else {
4327                                   !!!cp ('t71');
4328                                 $foster_parent_element                                 $foster_parent_element
4329                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4330                               }                               }
# Line 2428  sub _tree_construction_main ($) { Line 4335  sub _tree_construction_main ($) {
4335                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4336                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4337                             ($child, $next_sibling);                             ($child, $next_sibling);
4338                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4339                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4340                         }        !!!cp ('t72');
4341          $self->{open_elements}->[-1]->[0]->append_child ($child);
4342        }
4343    }; # $insert_to_foster    }; # $insert_to_foster
4344    
4345    my $in_body = sub {    B: while (1) {
4346      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
4347      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
4348        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4349          ## NOTE: This is an "as if in head" code clone        ## Ignore the token
4350          $script_start_tag->($insert);        ## Stay in the phase
4351          return;        !!!next-token;
4352        } elsif ($token->{tag_name} eq 'style') {        next B;
4353          ## NOTE: This is an "as if in head" code clone      } elsif ($token->{type} == START_TAG_TOKEN and
4354          $parse_rcdata->('CDATA', $insert);               $token->{tag_name} eq 'html') {
4355          return;        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4356        } elsif ({          !!!cp ('t79');
4357                  base => 1, link => 1, meta => 1,          !!!parse-error (type => 'after html', text => 'html', token => $token);
4358                 }->{$token->{tag_name}}) {          $self->{insertion_mode} = AFTER_BODY_IM;
4359          ## NOTE: This is an "as if in head" code clone, only "-t" differs        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4360          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!cp ('t80');
4361          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          !!!parse-error (type => 'after html', text => 'html', token => $token);
4362          !!!next-token;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4363          return;        } else {
4364        } elsif ($token->{tag_name} eq 'title') {          !!!cp ('t81');
4365          !!!parse-error (type => 'in body:title');        }
4366          ## NOTE: This is an "as if in head" code clone  
4367          $parse_rcdata->('RCDATA', $insert);        !!!cp ('t82');
4368          return;        !!!parse-error (type => 'not first start tag', token => $token);
4369        } elsif ($token->{tag_name} eq 'body') {        my $top_el = $self->{open_elements}->[0]->[0];
4370          !!!parse-error (type => 'in body:body');        for my $attr_name (keys %{$token->{attributes}}) {
4371                          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4372          if (@{$self->{open_elements}} == 1 or            !!!cp ('t84');
4373              $self->{open_elements}->[1]->[1] ne 'body') {            $top_el->set_attribute_ns
4374            ## Ignore the token              (undef, [undef, $attr_name],
4375          } else {               $token->{attributes}->{$attr_name}->{value});
           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});  
             }  
           }  
4376          }          }
4377          }
4378          !!!nack ('t84.1');
4379          !!!next-token;
4380          next B;
4381        } elsif ($token->{type} == COMMENT_TOKEN) {
4382          my $comment = $self->{document}->create_comment ($token->{data});
4383          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4384            !!!cp ('t85');
4385            $self->{document}->append_child ($comment);
4386          } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4387            !!!cp ('t86');
4388            $self->{open_elements}->[0]->[0]->append_child ($comment);
4389          } else {
4390            !!!cp ('t87');
4391            $self->{open_elements}->[-1]->[0]->append_child ($comment);
4392          }
4393          !!!next-token;
4394          next B;
4395        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4396          if ($token->{type} == CHARACTER_TOKEN) {
4397            !!!cp ('t87.1');
4398            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4399          !!!next-token;          !!!next-token;
4400          return;          next B;
4401        } elsif ({        } elsif ($token->{type} == START_TAG_TOKEN) {
4402                  address => 1, blockquote => 1, center => 1, dir => 1,          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4403                  div => 1, dl => 1, fieldset => 1, listing => 1,               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4404                  menu => 1, ol => 1, p => 1, ul => 1,              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4405                  pre => 1,              ($token->{tag_name} eq 'svg' and
4406                 }->{$token->{tag_name}}) {               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4407          ## has a p element in scope            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4408          INSCOPE: for (reverse @{$self->{open_elements}}) {            !!!cp ('t87.2');
4409            if ($_->[1] eq 'p') {            #
4410              !!!back-token;          } elsif ({
4411              $token = {type => 'end tag', tag_name => 'p'};                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4412              return;                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4413            } elsif ({                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4414                      table => 1, caption => 1, td => 1, th => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4415                      button => 1, marquee => 1, object => 1, html => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4416                     }->{$_->[1]}) {                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4417              last INSCOPE;                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4418            }                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4419          } # INSCOPE                   }->{$token->{tag_name}}) {
4420                        !!!cp ('t87.2');
4421          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            !!!parse-error (type => 'not closed',
4422          if ($token->{tag_name} eq 'pre') {                            text => $self->{open_elements}->[-1]->[0]
4423            !!!next-token;                                ->manakai_local_name,
4424            if ($token->{type} eq 'character') {                            token => $token);
4425              $token->{data} =~ s/^\x0A//;  
4426              unless (length $token->{data}) {            pop @{$self->{open_elements}}
4427                !!!next-token;                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4428              }  
4429            }            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4430          } else {            ## Reprocess.
4431            !!!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;  
4432          } else {          } else {
4433            ## has a p element in scope            my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4434            INSCOPE: for (reverse @{$self->{open_elements}}) {            my $tag_name = $token->{tag_name};
4435              if ($_->[1] eq 'p') {            if ($nsuri eq $SVG_NS) {
4436                !!!back-token;              $tag_name = {
4437                $token = {type => 'end tag', tag_name => 'p'};                 altglyph => 'altGlyph',
4438                return;                 altglyphdef => 'altGlyphDef',
4439              } elsif ({                 altglyphitem => 'altGlyphItem',
4440                        table => 1, caption => 1, td => 1, th => 1,                 animatecolor => 'animateColor',
4441                        button => 1, marquee => 1, object => 1, html => 1,                 animatemotion => 'animateMotion',
4442                       }->{$_->[1]}) {                 animatetransform => 'animateTransform',
4443                last INSCOPE;                 clippath => 'clipPath',
4444              }                 feblend => 'feBlend',
4445            } # INSCOPE                 fecolormatrix => 'feColorMatrix',
4446                               fecomponenttransfer => 'feComponentTransfer',
4447            !!!insert-element-t ($token->{tag_name}, $token->{attributes});                 fecomposite => 'feComposite',
4448            $self->{form_element} = $self->{open_elements}->[-1]->[0];                 feconvolvematrix => 'feConvolveMatrix',
4449            !!!next-token;                 fediffuselighting => 'feDiffuseLighting',
4450            return;                 fedisplacementmap => 'feDisplacementMap',
4451          }                 fedistantlight => 'feDistantLight',
4452        } elsif ($token->{tag_name} eq 'li') {                 feflood => 'feFlood',
4453          ## has a p element in scope                 fefunca => 'feFuncA',
4454          INSCOPE: for (reverse @{$self->{open_elements}}) {                 fefuncb => 'feFuncB',
4455            if ($_->[1] eq 'p') {                 fefuncg => 'feFuncG',
4456              !!!back-token;                 fefuncr => 'feFuncR',
4457              $token = {type => 'end tag', tag_name => 'p'};                 fegaussianblur => 'feGaussianBlur',
4458              return;                 feimage => 'feImage',
4459            } elsif ({                 femerge => 'feMerge',
4460                      table => 1, caption => 1, td => 1, th => 1,                 femergenode => 'feMergeNode',
4461                      button => 1, marquee => 1, object => 1, html => 1,                 femorphology => 'feMorphology',
4462                     }->{$_->[1]}) {                 feoffset => 'feOffset',
4463              last INSCOPE;                 fepointlight => 'fePointLight',
4464            }                 fespecularlighting => 'feSpecularLighting',
4465          } # INSCOPE                 fespotlight => 'feSpotLight',
4466                             fetile => 'feTile',
4467          ## Step 1                 feturbulence => 'feTurbulence',
4468          my $i = -1;                 foreignobject => 'foreignObject',
4469          my $node = $self->{open_elements}->[$i];                 glyphref => 'glyphRef',
4470          LI: {                 lineargradient => 'linearGradient',
4471            ## Step 2                 radialgradient => 'radialGradient',
4472            if ($node->[1] eq 'li') {                 #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4473              if ($i != -1) {                 textpath => 'textPath',  
4474                !!!parse-error (type => 'end tag missing:'.              }->{$tag_name} || $tag_name;
                               $self->{open_elements}->[-1]->[1]);  
               ## TODO: test  
             }  
             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;  
4475            }            }
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
               ## TODO: test  
             }  
             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);  
4476    
4477          !!!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];  
4478    
4479          !!!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);  
4480    
4481          ## 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') {  
             !!!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', ''];  
4482    
4483          !!!next-token;            if ($self->{self_closing}) {
4484          return;              pop @{$self->{open_elements}};
4485        } 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';  
         }  
           
         $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}};  
4486            } else {            } else {
4487              push @tokens, {type => 'character',              !!!cp ('t87.4');
                            data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD  
             ## TODO: make this configurable  
           }  
           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;  
4488            }            }
4489          }  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
4490            !!!next-token;            !!!next-token;
4491              next B;
4492          }          }
4493          if (length $text) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4494            $el->manakai_append_text ($text);          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4495          }          !!!cp ('t87.5');
4496                    #
4497          $self->{content_model_flag} = 'PCDATA';        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4498                    !!!cp ('t87.6');
4499          if ($token->{type} eq 'end tag' and          !!!parse-error (type => 'not closed',
4500              $token->{tag_name} eq $tag_name) {                          text => $self->{open_elements}->[-1]->[0]
4501            ## Ignore the token                              ->manakai_local_name,
4502          } else {                          token => $token);
4503            !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4504          }          pop @{$self->{open_elements}}
4505          !!!next-token;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4506          return;  
4507        } elsif ({          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4508                  iframe => 1,          ## Reprocess.
4509                  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.  
4510        } else {        } else {
4511          $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;  
4512        }        }
4513      } 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,  
                     }->{$_->[1]}) {  
               !!!parse-error (type => 'not closed:'.$_->[1]);  
             }  
           }  
4514    
4515            $self->{insertion_mode} = 'after body';      if ($self->{insertion_mode} & HEAD_IMS) {
4516            !!!next-token;        if ($token->{type} == CHARACTER_TOKEN) {
4517            return;          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4518          } else {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4519            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.2');
4520            ## Ignore the token              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4521            !!!next-token;              #
4522            return;            } else {
4523          }              !!!cp ('t88.1');
4524        } elsif ($token->{tag_name} eq 'html') {              ## Ignore the token.
4525          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {              #
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
4526            }            }
4527            $self->{insertion_mode} = 'after body';            unless (length $token->{data}) {
4528            ## reprocess              !!!cp ('t88');
4529            return;              !!!next-token;
4530          } 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,  
                 }->{$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;  
4531            }            }
4532          } # INSCOPE  ## TODO: set $token->{column} appropriately
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4533          }          }
4534            
4535          splice @{$self->{open_elements}}, $i if defined $i;          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4536          $clear_up_to_marker->()            !!!cp ('t89');
4537            if {            ## As if <head>
4538              button => 1, marquee => 1, object => 1,            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4539            }->{$token->{tag_name}};            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4540          !!!next-token;            push @{$self->{open_elements}},
4541          return;                [$self->{head_element}, $el_category->{head}];
4542        } elsif ($token->{tag_name} eq 'form') {  
4543          ## has an element in scope            ## Reprocess in the "in head" insertion mode...
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
4544            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
4545    
4546          undef $self->{form_element};            ## Reprocess in the "after head" insertion mode...
4547          !!!next-token;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4548          return;            !!!cp ('t90');
4549        } elsif ({            ## As if </noscript>
4550                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,            pop @{$self->{open_elements}};
4551                 }->{$token->{tag_name}}) {            !!!parse-error (type => 'in noscript:#text', token => $token);
4552          ## has an element in scope            
4553          my $i;            ## Reprocess in the "in head" insertion mode...
4554          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            ## As if </head>
4555            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,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
 ## TODO: <http://html5.org/tools/web-apps-tracker?from=883&to=884>  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
4556    
4557          ## Step 2            ## Reprocess in the "after head" insertion mode...
4558          S2: {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4559            if ($node->[1] eq $token->{tag_name}) {            !!!cp ('t91');
4560              ## Step 1            pop @{$self->{open_elements}};
4561              ## generate implied end tags  
4562              if ({            ## Reprocess in the "after head" insertion mode...
4563                   dd => 1, dt => 1, li => 1, p => 1,          } else {
4564                   td => 1, th => 1, tr => 1,            !!!cp ('t92');
4565                  }->{$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;  
4566    
4567            ## "after head" insertion mode
4568            ## As if <body>
4569            !!!insert-element ('body',, $token);
4570            $self->{insertion_mode} = IN_BODY_IM;
4571            ## reprocess
4572            next B;
4573          } elsif ($token->{type} == START_TAG_TOKEN) {
4574            if ($token->{tag_name} eq 'head') {
4575              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4576                !!!cp ('t93');
4577                !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4578                $self->{open_elements}->[-1]->[0]->append_child
4579                    ($self->{head_element});
4580                push @{$self->{open_elements}},
4581                    [$self->{head_element}, $el_category->{head}];
4582                $self->{insertion_mode} = IN_HEAD_IM;
4583                !!!nack ('t93.1');
4584              !!!next-token;              !!!next-token;
4585              last S2;              next B;
4586              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4587                !!!cp ('t93.2');
4588                !!!parse-error (type => 'after head', text => 'head',
4589                                token => $token);
4590                ## Ignore the token
4591                !!!nack ('t93.3');
4592                !!!next-token;
4593                next B;
4594            } else {            } else {
4595              ## Step 3              !!!cp ('t95');
4596              if (not $formatting_category->{$node->[1]} and              !!!parse-error (type => 'in head:head',
4597                  #not $phrasing_category->{$node->[1]} and                              token => $token); # or in head noscript
4598                  ($special_category->{$node->[1]} or              ## Ignore the token
4599                   $scoping_category->{$node->[1]})) {              !!!nack ('t95.1');
4600                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!next-token;
4601                ## Ignore the token              next B;
               !!!next-token;  
               last S2;  
             }  
4602            }            }
4603                      } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4604            ## Step 4            !!!cp ('t96');
4605            $node_i--;            ## As if <head>
4606            $node = $self->{open_elements}->[$node_i];            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4607                        $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4608            ## Step 5;            push @{$self->{open_elements}},
4609            redo S2;                [$self->{head_element}, $el_category->{head}];
         } # S2  
         return;  
       }  
     }  
   }; # $in_body  
4610    
4611    B: {            $self->{insertion_mode} = IN_HEAD_IM;
4612      if ($phase eq 'main') {            ## Reprocess in the "in head" insertion mode...
4613        if ($token->{type} eq 'DOCTYPE') {          } else {
4614          !!!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') {  
         ## TODO: unless it is the first start tag token, parse-error  
         my $top_el = $self->{open_elements}->[0]->[0];  
         for my $attr_name (keys %{$token->{attributes}}) {  
           unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
             $top_el->set_attribute_ns  
               (undef, [undef, $attr_name],  
                $token->{attributes}->{$attr_name}->{value});  
           }  
         }  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4615          }          }
4616    
4617          ## Stop parsing              if ($token->{tag_name} eq 'base') {
4618          last B;                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4619                    !!!cp ('t98');
4620                    ## As if </noscript>
4621                    pop @{$self->{open_elements}};
4622                    !!!parse-error (type => 'in noscript', text => 'base',
4623                                    token => $token);
4624                  
4625                    $self->{insertion_mode} = IN_HEAD_IM;
4626                    ## Reprocess in the "in head" insertion mode...
4627                  } else {
4628                    !!!cp ('t99');
4629                  }
4630    
4631          ## ISSUE: There is an issue in the spec.                ## NOTE: There is a "as if in head" code clone.
4632        } else {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4633          if ($self->{insertion_mode} eq 'before head') {                  !!!cp ('t100');
4634            if ($token->{type} eq 'character') {                  !!!parse-error (type => 'after head',
4635              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                                  text => $token->{tag_name}, token => $token);
4636                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                  push @{$self->{open_elements}},
4637                unless (length $token->{data}) {                      [$self->{head_element}, $el_category->{head}];
4638                  !!!next-token;                } else {
4639                  redo B;                  !!!cp ('t101');
4640                }                }
4641              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4642              ## As if <head>                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4643              !!!create-element ($self->{head_element}, 'head');                pop @{$self->{open_elements}} # <head>
4644              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4645              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}->{$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.  
4646                !!!next-token;                !!!next-token;
4647                redo B;                next B;
4648              }              } 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}}) {  
4649                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4650                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4651                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t102');
4652                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4653                                    text => $token->{tag_name}, token => $token);
4654                    push @{$self->{open_elements}},
4655                        [$self->{head_element}, $el_category->{head}];
4656                  } else {
4657                    !!!cp ('t103');
4658                }                }
4659                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4660                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4661                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4662                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4663                  !!!ack ('t103.1');
4664                !!!next-token;                !!!next-token;
4665                redo B;                next B;
4666              } elsif ($token->{tag_name} eq 'title' and              } elsif ($token->{tag_name} eq 'meta') {
                      $self->{insertion_mode} eq 'in head') {  
4667                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4668                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4669                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t104');
4670                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4671                                    text => $token->{tag_name}, token => $token);
4672                    push @{$self->{open_elements}},
4673                        [$self->{head_element}, $el_category->{head}];
4674                  } else {
4675                    !!!cp ('t105');
4676                }                }
4677                $parse_rcdata->('RCDATA', $insert_to_current);                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4678                pop @{$self->{open_elements}}                my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4679                    if $self->{insertion_mode} eq 'after head';  
4680                redo B;                unless ($self->{confident}) {
4681              } elsif ($token->{tag_name} eq 'style') {                  if ($token->{attributes}->{charset}) {
4682                      !!!cp ('t106');
4683                      ## NOTE: Whether the encoding is supported or not is handled
4684                      ## in the {change_encoding} callback.
4685                      $self->{change_encoding}
4686                          ->($self, $token->{attributes}->{charset}->{value},
4687                             $token);
4688                      
4689                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4690                          ->set_user_data (manakai_has_reference =>
4691                                               $token->{attributes}->{charset}
4692                                                   ->{has_reference});
4693                    } elsif ($token->{attributes}->{content}) {
4694                      if ($token->{attributes}->{content}->{value}
4695                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4696                              [\x09\x0A\x0C\x0D\x20]*=
4697                              [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4698                              ([^"'\x09\x0A\x0C\x0D\x20]
4699                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4700                        !!!cp ('t107');
4701                        ## NOTE: Whether the encoding is supported or not is handled
4702                        ## in the {change_encoding} callback.
4703                        $self->{change_encoding}
4704                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4705                               $token);
4706                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4707                            ->set_user_data (manakai_has_reference =>
4708                                                 $token->{attributes}->{content}
4709                                                       ->{has_reference});
4710                      } else {
4711                        !!!cp ('t108');
4712                      }
4713                    }
4714                  } else {
4715                    if ($token->{attributes}->{charset}) {
4716                      !!!cp ('t109');
4717                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4718                          ->set_user_data (manakai_has_reference =>
4719                                               $token->{attributes}->{charset}
4720                                                   ->{has_reference});
4721                    }
4722                    if ($token->{attributes}->{content}) {
4723                      !!!cp ('t110');
4724                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4725                          ->set_user_data (manakai_has_reference =>
4726                                               $token->{attributes}->{content}
4727                                                   ->{has_reference});
4728                    }
4729                  }
4730    
4731                  pop @{$self->{open_elements}} # <head>
4732                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4733                  !!!ack ('t110.1');
4734                  !!!next-token;
4735                  next B;
4736                } elsif ($token->{tag_name} eq 'title') {
4737                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4738                    !!!cp ('t111');
4739                    ## As if </noscript>
4740                    pop @{$self->{open_elements}};
4741                    !!!parse-error (type => 'in noscript', text => 'title',
4742                                    token => $token);
4743                  
4744                    $self->{insertion_mode} = IN_HEAD_IM;
4745                    ## Reprocess in the "in head" insertion mode...
4746                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4747                    !!!cp ('t112');
4748                    !!!parse-error (type => 'after head',
4749                                    text => $token->{tag_name}, token => $token);
4750                    push @{$self->{open_elements}},
4751                        [$self->{head_element}, $el_category->{head}];
4752                  } else {
4753                    !!!cp ('t113');
4754                  }
4755    
4756                  ## NOTE: There is a "as if in head" code clone.
4757                  my $parent = defined $self->{head_element} ? $self->{head_element}
4758                      : $self->{open_elements}->[-1]->[0];
4759                  $parse_rcdata->(RCDATA_CONTENT_MODEL);
4760                  pop @{$self->{open_elements}} # <head>
4761                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4762                  next B;
4763                } elsif ($token->{tag_name} eq 'style' or
4764                         $token->{tag_name} eq 'noframes') {
4765                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4766                ## insertion mode 'in head')                ## insertion mode IN_HEAD_IM)
4767                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4768                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4769                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t114');
4770                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4771                                    text => $token->{tag_name}, token => $token);
4772                    push @{$self->{open_elements}},
4773                        [$self->{head_element}, $el_category->{head}];
4774                  } else {
4775                    !!!cp ('t115');
4776                }                }
4777                $parse_rcdata->('CDATA', $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4778                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4779                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4780                redo B;                next B;
4781              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4782                if ($self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4783                    !!!cp ('t116');
4784                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4785                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4786                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4787                    !!!nack ('t116.1');
4788                  !!!next-token;                  !!!next-token;
4789                  redo B;                  next B;
4790                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4791                  !!!parse-error (type => 'noscript in noscript');                  !!!cp ('t117');
4792                    !!!parse-error (type => 'in noscript', text => 'noscript',
4793                                    token => $token);
4794                  ## Ignore the token                  ## Ignore the token
4795                  redo B;                  !!!nack ('t117.1');
4796                    !!!next-token;
4797                    next B;
4798                } else {                } else {
4799                    !!!cp ('t118');
4800                  #                  #
4801                }                }
4802              } elsif ($token->{tag_name} eq 'head' and              } elsif ($token->{tag_name} eq 'script') {
4803                       $self->{insertion_mode} ne 'after head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4804                !!!parse-error (type => 'in head:head'); # or in head noscript                  !!!cp ('t119');
4805                ## Ignore the token                  ## As if </noscript>
4806                !!!next-token;                  pop @{$self->{open_elements}};
4807                redo B;                  !!!parse-error (type => 'in noscript', text => 'script',
4808              } elsif ($self->{insertion_mode} ne 'in head noscript' and                                  token => $token);
4809                       $token->{tag_name} eq 'script') {                
4810                if ($self->{insertion_mode} eq 'after head') {                  $self->{insertion_mode} = IN_HEAD_IM;
4811                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  ## Reprocess in the "in head" insertion mode...
4812                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4813                    !!!cp ('t120');
4814                    !!!parse-error (type => 'after head',
4815                                    text => $token->{tag_name}, token => $token);
4816                    push @{$self->{open_elements}},
4817                        [$self->{head_element}, $el_category->{head}];
4818                  } else {
4819                    !!!cp ('t121');
4820                }                }
4821    
4822                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4823                $script_start_tag->($insert_to_current);                $script_start_tag->();
4824                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4825                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4826                redo B;                next B;
4827              } 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  
4828                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4829                !!!insert-element ('frameset', $token->{attributes});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4830                $self->{insertion_mode} = 'in frameset';                  !!!cp ('t122');
4831                    ## As if </noscript>
4832                    pop @{$self->{open_elements}};
4833                    !!!parse-error (type => 'in noscript',
4834                                    text => $token->{tag_name}, token => $token);
4835                    
4836                    ## Reprocess in the "in head" insertion mode...
4837                    ## As if </head>
4838                    pop @{$self->{open_elements}};
4839                    
4840                    ## Reprocess in the "after head" insertion mode...
4841                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4842                    !!!cp ('t124');
4843                    pop @{$self->{open_elements}};
4844                    
4845                    ## Reprocess in the "after head" insertion mode...
4846                  } else {
4847                    !!!cp ('t125');
4848                  }
4849    
4850                  ## "after head" insertion mode
4851                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4852                  if ($token->{tag_name} eq 'body') {
4853                    !!!cp ('t126');
4854                    $self->{insertion_mode} = IN_BODY_IM;
4855                  } elsif ($token->{tag_name} eq 'frameset') {
4856                    !!!cp ('t127');
4857                    $self->{insertion_mode} = IN_FRAMESET_IM;
4858                  } else {
4859                    die "$0: tag name: $self->{tag_name}";
4860                  }
4861                  !!!nack ('t127.1');
4862                !!!next-token;                !!!next-token;
4863                redo B;                next B;
4864              } else {              } else {
4865                  !!!cp ('t128');
4866                #                #
4867              }              }
4868            } elsif ($token->{type} eq 'end tag') {  
4869              if ($self->{insertion_mode} eq 'in head' and              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4870                  $token->{tag_name} eq 'head') {                !!!cp ('t129');
4871                  ## As if </noscript>
4872                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4873                $self->{insertion_mode} = 'after head';                !!!parse-error (type => 'in noscript:/',
4874                !!!next-token;                                text => $token->{tag_name}, token => $token);
4875                redo B;                
4876              } elsif ($self->{insertion_mode} eq 'in head noscript' and                ## Reprocess in the "in head" insertion mode...
4877                  $token->{tag_name} eq 'noscript') {                ## As if </head>
4878                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  
                      ($token->{tag_name} eq 'body' or  
                       $token->{tag_name} eq 'html')) {  
               #  
             } 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 {  
             #  
           }  
   
           ## As if </head> or </noscript> or <body>  
           if ($self->{insertion_mode} eq 'in head') {  
             pop @{$self->{open_elements}};  
             $self->{insertion_mode} = 'after head';  
           } elsif ($self->{insertion_mode} eq 'in head noscript') {  
             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;  
4879    
4880            ## ISSUE: An issue in the spec.                ## Reprocess in the "after head" insertion mode...
4881          } elsif ($self->{insertion_mode} eq 'in body') {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4882            if ($token->{type} eq 'character') {                !!!cp ('t130');
4883              ## NOTE: There is a code clone of "character in body".                ## As if </head>
4884              $reconstruct_active_formatting_elements->($insert_to_current);                pop @{$self->{open_elements}};
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
4885    
4886              !!!next-token;                ## Reprocess in the "after head" insertion mode...
4887              redo B;              } else {
4888            } 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;  
               }  
4889              }              }
4890    
4891              !!!parse-error (type => 'in table:#character');              ## "after head" insertion mode
4892                ## As if <body>
4893                !!!insert-element ('body',, $token);
4894                $self->{insertion_mode} = IN_BODY_IM;
4895                ## reprocess
4896                !!!ack-later;
4897                next B;
4898              } elsif ($token->{type} == END_TAG_TOKEN) {
4899                if ($token->{tag_name} eq 'head') {
4900                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4901                    !!!cp ('t132');
4902                    ## As if <head>
4903                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4904                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4905                    push @{$self->{open_elements}},
4906                        [$self->{head_element}, $el_category->{head}];
4907    
4908              ## As if in body, but insert into foster parent element                  ## Reprocess in the "in head" insertion mode...
4909              ## ISSUE: Spec says that "whenever a node would be inserted                  pop @{$self->{open_elements}};
4910              ## into the current node" while characters might not be                  $self->{insertion_mode} = AFTER_HEAD_IM;
4911              ## result in a new Text node.                  !!!next-token;
4912              $reconstruct_active_formatting_elements->($insert_to_foster);                  next B;
4913                              } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4914              if ({                  !!!cp ('t133');
4915                   table => 1, tbody => 1, tfoot => 1,                  ## As if </noscript>
4916                   thead => 1, tr => 1,                  pop @{$self->{open_elements}};
4917                  }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'in noscript:/',
4918                # MUST                                  text => 'head', token => $token);
4919                my $foster_parent_element;                  
4920                my $next_sibling;                  ## Reprocess in the "in head" insertion mode...
4921                my $prev_sibling;                  pop @{$self->{open_elements}};
4922                OE: for (reverse 0..$#{$self->{open_elements}}) {                  $self->{insertion_mode} = AFTER_HEAD_IM;
4923                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  !!!next-token;
4924                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  next B;
4925                    if (defined $parent and $parent->node_type == 1) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4926                      $foster_parent_element = $parent;                  !!!cp ('t134');
4927                      $next_sibling = $self->{open_elements}->[$_]->[0];                  pop @{$self->{open_elements}};
4928                      $prev_sibling = $next_sibling->previous_sibling;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4929                    } else {                  !!!next-token;
4930                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                  next B;
4931                      $prev_sibling = $foster_parent_element->last_child;                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4932                    }                  !!!cp ('t134.1');
4933                    last OE;                  !!!parse-error (type => 'unmatched end tag', text => 'head',
4934                  }                                  token => $token);
4935                } # OE                  ## Ignore the token
4936                $foster_parent_element = $self->{open_elements}->[0]->[0] and                  !!!next-token;
4937                $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});  
4938                } else {                } else {
4939                  $foster_parent_element->insert_before                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
4940                }                }
4941              } else {              } elsif ($token->{tag_name} eq 'noscript') {
4942                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4943              }                  !!!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]);  
4944                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4945                    $self->{insertion_mode} = IN_HEAD_IM;
4946                    !!!next-token;
4947                    next B;
4948                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4949                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4950                    !!!cp ('t137');
4951                    !!!parse-error (type => 'unmatched end tag',
4952                                    text => 'noscript', token => $token);
4953                    ## Ignore the token ## ISSUE: An issue in the spec.
4954                    !!!next-token;
4955                    next B;
4956                  } else {
4957                    !!!cp ('t138');
4958                    #
4959                }                }
   
               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;  
4960              } elsif ({              } elsif ({
4961                        col => 1,                        body => 1, html => 1,
                       td => 1, th => 1, tr => 1,  
4962                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4963                ## Clear back to table context                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
4964                while ($self->{open_elements}->[-1]->[1] ne 'table' and                    $self->{insertion_mode} == IN_HEAD_IM or
4965                       $self->{open_elements}->[-1]->[1] ne 'html') {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4966                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t140');
4967                  pop @{$self->{open_elements}};                  !!!parse-error (type => 'unmatched end tag',
4968                                    text => $token->{tag_name}, token => $token);
4969                    ## Ignore the token
4970                    !!!next-token;
4971                    next B;
4972                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4973                    !!!cp ('t140.1');
4974                    !!!parse-error (type => 'unmatched end tag',
4975                                    text => $token->{tag_name}, token => $token);
4976                    ## Ignore the token
4977                    !!!next-token;
4978                    next B;
4979                  } else {
4980                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4981                }                }
4982                } elsif ($token->{tag_name} eq 'p') {
4983                  !!!cp ('t142');
4984                  !!!parse-error (type => 'unmatched end tag',
4985                                  text => $token->{tag_name}, token => $token);
4986                  ## Ignore the token
4987                  !!!next-token;
4988                  next B;
4989                } elsif ($token->{tag_name} eq 'br') {
4990                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4991                    !!!cp ('t142.2');
4992                    ## (before head) as if <head>, (in head) as if </head>
4993                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4994                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4995                    $self->{insertion_mode} = AFTER_HEAD_IM;
4996      
4997                    ## Reprocess in the "after head" insertion mode...
4998                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4999                    !!!cp ('t143.2');
5000                    ## As if </head>
5001                    pop @{$self->{open_elements}};
5002                    $self->{insertion_mode} = AFTER_HEAD_IM;
5003      
5004                    ## Reprocess in the "after head" insertion mode...
5005                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5006                    !!!cp ('t143.3');
5007                    ## ISSUE: Two parse errors for <head><noscript></br>
5008                    !!!parse-error (type => 'unmatched end tag',
5009                                    text => 'br', token => $token);
5010                    ## As if </noscript>
5011                    pop @{$self->{open_elements}};
5012                    $self->{insertion_mode} = IN_HEAD_IM;
5013    
5014                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                  ## Reprocess in the "in head" insertion mode...
5015                $self->{insertion_mode} = $token->{tag_name} eq 'col'                  ## As if </head>
5016                  ? 'in column group' : 'in table body';                  pop @{$self->{open_elements}};
5017                ## 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]);  
5018    
5019                ## As if </table>                  ## Reprocess in the "after head" insertion mode...
5020                ## have a table element in table scope                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5021                my $i;                  !!!cp ('t143.4');
5022                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  #
5023                  my $node = $self->{open_elements}->[$_];                } else {
5024                  if ($node->[1] eq 'table') {                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5025                }                }
5026    
5027                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5028                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'unmatched end tag',
5029                }                                text => 'br', token => $token);
5030                  ## Ignore the token
5031                  !!!next-token;
5032                  next B;
5033                } else {
5034                  !!!cp ('t145');
5035                  !!!parse-error (type => 'unmatched end tag',
5036                                  text => $token->{tag_name}, token => $token);
5037                  ## Ignore the token
5038                  !!!next-token;
5039                  next B;
5040                }
5041    
5042                splice @{$self->{open_elements}}, $i;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5043                  !!!cp ('t146');
5044                  ## As if </noscript>
5045                  pop @{$self->{open_elements}};
5046                  !!!parse-error (type => 'in noscript:/',
5047                                  text => $token->{tag_name}, token => $token);
5048                  
5049                  ## Reprocess in the "in head" insertion mode...
5050                  ## As if </head>
5051                  pop @{$self->{open_elements}};
5052    
5053                $self->_reset_insertion_mode;                ## Reprocess in the "after head" insertion mode...
5054                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5055                  !!!cp ('t147');
5056                  ## As if </head>
5057                  pop @{$self->{open_elements}};
5058    
5059                ## reprocess                ## Reprocess in the "after head" insertion mode...
5060                redo B;              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5061    ## ISSUE: This case cannot be reached?
5062                  !!!cp ('t148');
5063                  !!!parse-error (type => 'unmatched end tag',
5064                                  text => $token->{tag_name}, token => $token);
5065                  ## Ignore the token ## ISSUE: An issue in the spec.
5066                  !!!next-token;
5067                  next B;
5068              } else {              } else {
5069                #                !!!cp ('t149');
5070              }              }
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'table') {  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
5071    
5072                if ($self->{open_elements}->[-1]->[1] ne 'table') {              ## "after head" insertion mode
5073                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## As if <body>
5074                }              !!!insert-element ('body',, $token);
5075                $self->{insertion_mode} = IN_BODY_IM;
5076                ## reprocess
5077                next B;
5078          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5079            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5080              !!!cp ('t149.1');
5081    
5082              ## NOTE: As if <head>
5083              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5084              $self->{open_elements}->[-1]->[0]->append_child
5085                  ($self->{head_element});
5086              #push @{$self->{open_elements}},
5087              #    [$self->{head_element}, $el_category->{head}];
5088              #$self->{insertion_mode} = IN_HEAD_IM;
5089              ## NOTE: Reprocess.
5090    
5091              ## NOTE: As if </head>
5092              #pop @{$self->{open_elements}};
5093              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5094              ## NOTE: Reprocess.
5095              
5096              #
5097            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5098              !!!cp ('t149.2');
5099    
5100                splice @{$self->{open_elements}}, $i;            ## NOTE: As if </head>
5101              pop @{$self->{open_elements}};
5102              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5103              ## NOTE: Reprocess.
5104    
5105                $self->_reset_insertion_mode;            #
5106            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5107              !!!cp ('t149.3');
5108    
5109                !!!next-token;            !!!parse-error (type => 'in noscript:#eof', token => $token);
5110                redo B;  
5111              } elsif ({            ## As if </noscript>
5112                        body => 1, caption => 1, col => 1, colgroup => 1,            pop @{$self->{open_elements}};
5113                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,            #$self->{insertion_mode} = IN_HEAD_IM;
5114                        thead => 1, tr => 1,            ## NOTE: Reprocess.
5115                       }->{$token->{tag_name}}) {  
5116                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: As if </head>
5117                ## Ignore the token            pop @{$self->{open_elements}};
5118                !!!next-token;            #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5119                redo B;            ## NOTE: Reprocess.
5120              } else {  
5121                #            #
5122              }          } else {
5123            } else {            !!!cp ('t149.4');
5124              #            #
5125            }          }
5126    
5127            ## NOTE: As if <body>
5128            !!!insert-element ('body',, $token);
5129            $self->{insertion_mode} = IN_BODY_IM;
5130            ## NOTE: Reprocess.
5131            next B;
5132          } else {
5133            die "$0: $token->{type}: Unknown token type";
5134          }
5135    
5136            !!!parse-error (type => 'in table:'.$token->{tag_name});            ## ISSUE: An issue in the spec.
5137            $in_body->($insert_to_foster);      } elsif ($self->{insertion_mode} & BODY_IMS) {
5138            redo B;            if ($token->{type} == CHARACTER_TOKEN) {
5139          } elsif ($self->{insertion_mode} eq 'in caption') {              !!!cp ('t150');
5140            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".  
5141              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5142                            
5143              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5144    
5145              !!!next-token;              !!!next-token;
5146              redo B;              next B;
5147            } 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') {  
5148              if ({              if ({
5149                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5150                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5151                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5152                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
5153                    ## have an element in table scope
5154                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
5155                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
5156                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
5157                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
5158                  my $node = $self->{open_elements}->[$_];  
5159                  if ($node->[1] eq 'caption') {                      ## Close the cell
5160                    $i = $_;                      !!!back-token; # <x>
5161                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
5162                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
5163                            table => 1, html => 1,                                line => $token->{line},
5164                           }->{$node->[1]}) {                                column => $token->{column}};
5165                    last INSCOPE;                      next B;
5166                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5167                        !!!cp ('t152');
5168                        ## ISSUE: This case can never be reached, maybe.
5169                        last;
5170                      }
5171                  }                  }
5172                } # INSCOPE  
5173                unless (defined $i) {                  !!!cp ('t153');
5174                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
5175                        text => $token->{tag_name}, token => $token);
5176                  ## Ignore the token                  ## Ignore the token
5177                    !!!nack ('t153.1');
5178                  !!!next-token;                  !!!next-token;
5179                  redo B;                  next B;
5180                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5181                                  !!!parse-error (type => 'not closed', text => 'caption',
5182                ## generate implied end tags                                  token => $token);
5183                if ({                  
5184                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5185                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5186                    }->{$self->{open_elements}->[-1]->[1]}) {                  my $i;
5187                  !!!back-token; # <?>                  INSCOPE: {
5188                  $token = {type => 'end tag', tag_name => 'caption'};                    for (reverse 0..$#{$self->{open_elements}}) {
5189                  !!!back-token;                      my $node = $self->{open_elements}->[$_];
5190                  $token = {type => 'end tag',                      if ($node->[1] & CAPTION_EL) {
5191                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        !!!cp ('t155');
5192                  redo B;                        $i = $_;
5193                }                        last INSCOPE;
5194                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5195                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        !!!cp ('t156');
5196                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                        last;
5197                }                      }
5198                      }
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
5199    
5200                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
5201                      !!!parse-error (type => 'start tag not allowed',
5202                                      text => $token->{tag_name}, token => $token);
5203                      ## Ignore the token
5204                      !!!nack ('t157.1');
5205                      !!!next-token;
5206                      next B;
5207                    } # INSCOPE
5208                    
5209                    ## generate implied end tags
5210                    while ($self->{open_elements}->[-1]->[1]
5211                               & END_TAG_OPTIONAL_EL) {
5212                      !!!cp ('t158');
5213                      pop @{$self->{open_elements}};
5214                    }
5215    
5216                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5217                redo B;                    !!!cp ('t159');
5218                      !!!parse-error (type => 'not closed',
5219                                      text => $self->{open_elements}->[-1]->[0]
5220                                          ->manakai_local_name,
5221                                      token => $token);
5222                    } else {
5223                      !!!cp ('t160');
5224                    }
5225                    
5226                    splice @{$self->{open_elements}}, $i;
5227                    
5228                    $clear_up_to_marker->();
5229                    
5230                    $self->{insertion_mode} = IN_TABLE_IM;
5231                    
5232                    ## reprocess
5233                    !!!ack-later;
5234                    next B;
5235                  } else {
5236                    !!!cp ('t161');
5237                    #
5238                  }
5239              } else {              } else {
5240                  !!!cp ('t162');
5241                #                #
5242              }              }
5243            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5244              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5245                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
5246                my $i;                  ## have an element in table scope
5247                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
5248                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5249                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5250                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5251                    last INSCOPE;                      !!!cp ('t163');
5252                  } elsif ({                      $i = $_;
5253                            table => 1, html => 1,                      last INSCOPE;
5254                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5255                    last INSCOPE;                      !!!cp ('t164');
5256                        last INSCOPE;
5257                      }
5258                    } # INSCOPE
5259                      unless (defined $i) {
5260                        !!!cp ('t165');
5261                        !!!parse-error (type => 'unmatched end tag',
5262                                        text => $token->{tag_name},
5263                                        token => $token);
5264                        ## Ignore the token
5265                        !!!next-token;
5266                        next B;
5267                      }
5268                    
5269                    ## generate implied end tags
5270                    while ($self->{open_elements}->[-1]->[1]
5271                               & END_TAG_OPTIONAL_EL) {
5272                      !!!cp ('t166');
5273                      pop @{$self->{open_elements}};
5274                  }                  }
5275                } # INSCOPE  
5276                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5277                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5278                      !!!cp ('t167');
5279                      !!!parse-error (type => 'not closed',
5280                                      text => $self->{open_elements}->[-1]->[0]
5281                                          ->manakai_local_name,
5282                                      token => $token);
5283                    } else {
5284                      !!!cp ('t168');
5285                    }
5286                    
5287                    splice @{$self->{open_elements}}, $i;
5288                    
5289                    $clear_up_to_marker->();
5290                    
5291                    $self->{insertion_mode} = IN_ROW_IM;
5292                    
5293                    !!!next-token;
5294                    next B;
5295                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5296                    !!!cp ('t169');
5297                    !!!parse-error (type => 'unmatched end tag',
5298                                    text => $token->{tag_name}, token => $token);
5299                  ## Ignore the token                  ## Ignore the token
5300                  !!!next-token;                  !!!next-token;
5301                  redo B;                  next B;
5302                }                } else {
5303                                  !!!cp ('t170');
5304                ## generate implied end tags                  #
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5305                }                }
5306                } elsif ($token->{tag_name} eq 'caption') {
5307                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5308                    ## have a table element in table scope
5309                    my $i;
5310                    INSCOPE: {
5311                      for (reverse 0..$#{$self->{open_elements}}) {
5312                        my $node = $self->{open_elements}->[$_];
5313                        if ($node->[1] & CAPTION_EL) {
5314                          !!!cp ('t171');
5315                          $i = $_;
5316                          last INSCOPE;
5317                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5318                          !!!cp ('t172');
5319                          last;
5320                        }
5321                      }
5322    
5323                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5324                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5325                                      text => $token->{tag_name}, token => $token);
5326                      ## Ignore the token
5327                      !!!next-token;
5328                      next B;
5329                    } # INSCOPE
5330                    
5331                    ## generate implied end tags
5332                    while ($self->{open_elements}->[-1]->[1]
5333                               & END_TAG_OPTIONAL_EL) {
5334                      !!!cp ('t174');
5335                      pop @{$self->{open_elements}};
5336                    }
5337                    
5338                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5339                      !!!cp ('t175');
5340                      !!!parse-error (type => 'not closed',
5341                                      text => $self->{open_elements}->[-1]->[0]
5342                                          ->manakai_local_name,
5343                                      token => $token);
5344                    } else {
5345                      !!!cp ('t176');
5346                    }
5347                    
5348                    splice @{$self->{open_elements}}, $i;
5349                    
5350                    $clear_up_to_marker->();
5351                    
5352                    $self->{insertion_mode} = IN_TABLE_IM;
5353                    
5354                    !!!next-token;
5355                    next B;
5356                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5357                    !!!cp ('t177');
5358                    !!!parse-error (type => 'unmatched end tag',
5359                                    text => $token->{tag_name}, token => $token);
5360                    ## Ignore the token
5361                    !!!next-token;
5362                    next B;
5363                  } else {
5364                    !!!cp ('t178');
5365                    #
5366                }                }
5367                } elsif ({
5368                          table => 1, tbody => 1, tfoot => 1,
5369                          thead => 1, tr => 1,
5370                         }->{$token->{tag_name}} and
5371                         $self->{insertion_mode} == IN_CELL_IM) {
5372                  ## have an element in table scope
5373                  my $i;
5374                  my $tn;
5375                  INSCOPE: {
5376                    for (reverse 0..$#{$self->{open_elements}}) {
5377                      my $node = $self->{open_elements}->[$_];
5378                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5379                        !!!cp ('t179');
5380                        $i = $_;
5381    
5382                        ## Close the cell
5383                        !!!back-token; # </x>
5384                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5385                                  line => $token->{line},
5386                                  column => $token->{column}};
5387                        next B;
5388                      } elsif ($node->[1] & TABLE_CELL_EL) {
5389                        !!!cp ('t180');
5390                        $tn = $node->[0]->manakai_local_name;
5391                        ## NOTE: There is exactly one |td| or |th| element
5392                        ## in scope in the stack of open elements by definition.
5393                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5394                        ## ISSUE: Can this be reached?
5395                        !!!cp ('t181');
5396                        last;
5397                      }
5398                    }
5399    
5400                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5401                    !!!parse-error (type => 'unmatched end tag',
5402                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5403                    ## Ignore the token
5404                $self->{insertion_mode} = 'in table';                  !!!next-token;
5405                    next B;
5406                !!!next-token;                } # INSCOPE
5407                redo B;              } elsif ($token->{tag_name} eq 'table' and
5408              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5409                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5410                                  token => $token);
5411    
5412                ## As if </caption>                ## As if </caption>
5413                ## have a table element in table scope                ## have a table element in table scope
5414                my $i;                my $i;
5415                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5416                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5417                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5418                      !!!cp ('t184');
5419                    $i = $_;                    $i = $_;
5420                    last INSCOPE;                    last INSCOPE;
5421                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5422                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5423                    last INSCOPE;                    last INSCOPE;
5424                  }                  }
5425                } # INSCOPE                } # INSCOPE
5426                unless (defined $i) {                unless (defined $i) {
5427                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5428                    !!!parse-error (type => 'unmatched end tag',
5429                                    text => 'caption', token => $token);
5430                  ## Ignore the token                  ## Ignore the token
5431                  !!!next-token;                  !!!next-token;
5432                  redo B;                  next B;
5433                }                }
5434                                
5435                ## generate implied end tags                ## generate implied end tags
5436                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5437                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5438                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5439                }                }
5440    
5441                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5442                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5443                    !!!parse-error (type => 'not closed',
5444                                    text => $self->{open_elements}->[-1]->[0]
5445                                        ->manakai_local_name,
5446                                    token => $token);
5447                  } else {
5448                    !!!cp ('t189');
5449                }                }
5450    
5451                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5452    
5453                $clear_up_to_marker->();                $clear_up_to_marker->();
5454    
5455                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5456    
5457                ## reprocess                ## reprocess
5458                redo B;                next B;
5459              } elsif ({              } elsif ({
5460                        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,  
5461                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5462                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5463                ## Ignore the token                  !!!cp ('t190');
5464                redo B;                  !!!parse-error (type => 'unmatched end tag',
5465              } 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');  
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  redo B;                  next B;
5469                } else {                } else {
5470                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
5471                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5472                }                }
5473              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5474                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
5475                          thead => 1, tr => 1,
5476                         }->{$token->{tag_name}} and
5477                         $self->{insertion_mode} == IN_CAPTION_IM) {
5478                  !!!cp ('t192');
5479                  !!!parse-error (type => 'unmatched end tag',
5480                                  text => $token->{tag_name}, token => $token);
5481                ## Ignore the token                ## Ignore the token
5482                !!!next-token;                !!!next-token;
5483                redo B;                next B;
5484              } else {              } else {
5485                #                !!!cp ('t193');
5486                  #
5487              }              }
5488            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5489              #          for my $entry (@{$self->{open_elements}}) {
5490              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5491                !!!cp ('t75');
5492                !!!parse-error (type => 'in body:#eof', token => $token);
5493                last;
5494            }            }
5495            }
5496    
5497            ## As if </colgroup>          ## Stop parsing.
5498            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
5499              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
5500              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5501          }
5502    
5503          $insert = $insert_to_current;
5504          #
5505        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5506          if ($token->{type} == CHARACTER_TOKEN) {
5507            if (not $open_tables->[-1]->[1] and # tainted
5508                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5509              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5510                  
5511              unless (length $token->{data}) {
5512                !!!cp ('t194');
5513              !!!next-token;              !!!next-token;
5514              redo B;              next B;
5515            } else {            } else {
5516              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
5517            }            }
5518          } 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;  
               }  
             }  
5519    
5520              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5521    
5522              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5523              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
5524              ## into the current node" while characters might not be              ## into the current node" while characters might not be
5525              ## result in a new Text node.              ## result in a new Text node.
5526              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5527                
5528              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]}) {  
5529                # MUST                # MUST
5530                my $foster_parent_element;                my $foster_parent_element;
5531                my $next_sibling;                my $next_sibling;
5532                my $prev_sibling;                my $prev_sibling;
5533                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5534                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5535                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5536                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5537                        !!!cp ('t196');
5538                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5539                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5540                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5541                    } else {                    } else {
5542                        !!!cp ('t197');
5543                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5544                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5545                    }                    }
# Line 3997  sub _tree_construction_main ($) { Line 5551  sub _tree_construction_main ($) {
5551                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5552                if (defined $prev_sibling and                if (defined $prev_sibling and
5553                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5554                    !!!cp ('t198');
5555                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5556                } else {                } else {
5557                    !!!cp ('t199');
5558                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5559                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5560                     $next_sibling);                     $next_sibling);
5561                }                }
5562              } else {            $open_tables->[-1]->[1] = 1; # tainted
5563                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5564              !!!cp ('t200');
5565              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5566            }
5567                
5568            !!!next-token;
5569            next B;
5570          } elsif ($token->{type} == START_TAG_TOKEN) {
5571            if ({
5572                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5573                 th => 1, td => 1,
5574                }->{$token->{tag_name}}) {
5575              if ($self->{insertion_mode} == IN_TABLE_IM) {
5576                ## Clear back to table context
5577                while (not ($self->{open_elements}->[-1]->[1]
5578                                & TABLE_SCOPING_EL)) {
5579                  !!!cp ('t201');
5580                  pop @{$self->{open_elements}};
5581              }              }
5582                            
5583              !!!next-token;              !!!insert-element ('tbody',, $token);
5584              redo B;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5585            } elsif ($token->{type} eq 'comment') {              ## reprocess in the "in table body" insertion mode...
5586              ## Copied from 'in table'            }
5587              my $comment = $self->{document}->create_comment ($token->{data});            
5588              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5589              !!!next-token;              unless ($token->{tag_name} eq 'tr') {
5590              redo B;                !!!cp ('t202');
5591            } elsif ($token->{type} eq 'start tag') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
5592              if ({              }
5593                   tr => 1,                  
5594                   th => 1, td => 1,              ## Clear back to table body context
5595                  }->{$token->{tag_name}}) {              while (not ($self->{open_elements}->[-1]->[1]
5596                unless ($token->{tag_name} eq 'tr') {                              & TABLE_ROWS_SCOPING_EL)) {
5597                  !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t203');
5598                  ## ISSUE: Can this case be reached?
5599                  pop @{$self->{open_elements}};
5600                }
5601                    
5602                    $self->{insertion_mode} = IN_ROW_IM;
5603                    if ($token->{tag_name} eq 'tr') {
5604                      !!!cp ('t204');
5605                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5606                      !!!nack ('t204');
5607                      !!!next-token;
5608                      next B;
5609                    } else {
5610                      !!!cp ('t205');
5611                      !!!insert-element ('tr',, $token);
5612                      ## reprocess in the "in row" insertion mode
5613                    }
5614                  } else {
5615                    !!!cp ('t206');
5616                }                }
5617    
5618                ## Clear back to table body context                ## Clear back to table row context
5619                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5620                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5621                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5622                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5623                }                }
5624                                
5625                $self->{insertion_mode} = 'in row';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5626                if ($token->{tag_name} eq 'tr') {                $self->{insertion_mode} = IN_CELL_IM;
5627                  !!!insert-element ($token->{tag_name}, $token->{attributes});  
5628                  !!!next-token;                push @$active_formatting_elements, ['#marker', ''];
5629                } else {                
5630                  !!!insert-element ('tr');                !!!nack ('t207.1');
5631                  ## reprocess                !!!next-token;
5632                }                next B;
               redo B;  
5633              } elsif ({              } elsif ({
5634                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5635                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5636                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5637                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5638                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
5639                my $i;                  ## As if </tr>
5640                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
5641                  my $node = $self->{open_elements}->[$_];                  my $i;
5642                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5643                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
5644                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
5645                    $i = $_;                      !!!cp ('t208');
5646                    last INSCOPE;                      $i = $_;
5647                  } elsif ({                      last INSCOPE;
5648                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5649                           }->{$node->[1]}) {                      !!!cp ('t209');
5650                    last INSCOPE;                      last INSCOPE;
5651                      }
5652                    } # INSCOPE
5653                    unless (defined $i) {
5654                      !!!cp ('t210');
5655    ## TODO: This type is wrong.
5656                      !!!parse-error (type => 'unmacthed end tag',
5657                                      text => $token->{tag_name}, token => $token);
5658                      ## Ignore the token
5659                      !!!nack ('t210.1');
5660                      !!!next-token;
5661                      next B;
5662                    }
5663                    
5664                    ## Clear back to table row context
5665                    while (not ($self->{open_elements}->[-1]->[1]
5666                                    & TABLE_ROW_SCOPING_EL)) {
5667                      !!!cp ('t211');
5668                      ## ISSUE: Can this case be reached?
5669                      pop @{$self->{open_elements}};
5670                    }
5671                    
5672                    pop @{$self->{open_elements}}; # tr
5673                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5674                    if ($token->{tag_name} eq 'tr') {
5675                      !!!cp ('t212');
5676                      ## reprocess
5677                      !!!ack-later;
5678                      next B;
5679                    } else {
5680                      !!!cp ('t213');
5681                      ## reprocess in the "in table body" insertion mode...
5682                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5683                }                }
5684    
5685                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5686                while (not {                  ## have an element in table scope
5687                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5688                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5689                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5690                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5691                        !!!cp ('t214');
5692                        $i = $_;
5693                        last INSCOPE;
5694                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5695                        !!!cp ('t215');
5696                        last INSCOPE;
5697                      }
5698                    } # INSCOPE
5699                    unless (defined $i) {
5700                      !!!cp ('t216');
5701    ## TODO: This erorr type is wrong.
5702                      !!!parse-error (type => 'unmatched end tag',
5703                                      text => $token->{tag_name}, token => $token);
5704                      ## Ignore the token
5705                      !!!nack ('t216.1');
5706                      !!!next-token;
5707                      next B;
5708                    }
5709    
5710                    ## Clear back to table body context
5711                    while (not ($self->{open_elements}->[-1]->[1]
5712                                    & TABLE_ROWS_SCOPING_EL)) {
5713                      !!!cp ('t217');
5714                      ## ISSUE: Can this state be reached?
5715                      pop @{$self->{open_elements}};
5716                    }
5717                    
5718                    ## As if <{current node}>
5719                    ## have an element in table scope
5720                    ## true by definition
5721                    
5722                    ## Clear back to table body context
5723                    ## nop by definition
5724                    
5725                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5726                    $self->{insertion_mode} = IN_TABLE_IM;
5727                    ## reprocess in "in table" insertion mode...
5728                  } else {
5729                    !!!cp ('t218');
5730                }                }
5731    
5732                ## As if <{current node}>                if ($token->{tag_name} eq 'col') {
5733                ## have an element in table scope                  ## Clear back to table context
5734                ## true by definition                  while (not ($self->{open_elements}->[-1]->[1]
5735                                    & TABLE_SCOPING_EL)) {
5736                ## Clear back to table body context                    !!!cp ('t219');
5737                ## nop by definition                    ## ISSUE: Can this state be reached?
5738                      pop @{$self->{open_elements}};
5739                pop @{$self->{open_elements}};                  }
5740                $self->{insertion_mode} = 'in table';                  
5741                ## reprocess                  !!!insert-element ('colgroup',, $token);
5742                redo B;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5743                    ## reprocess
5744                    !!!ack-later;
5745                    next B;
5746                  } elsif ({
5747                            caption => 1,
5748                            colgroup => 1,
5749                            tbody => 1, tfoot => 1, thead => 1,
5750                           }->{$token->{tag_name}}) {
5751                    ## Clear back to table context
5752                    while (not ($self->{open_elements}->[-1]->[1]
5753                                    & TABLE_SCOPING_EL)) {
5754                      !!!cp ('t220');
5755                      ## ISSUE: Can this state be reached?
5756                      pop @{$self->{open_elements}};
5757                    }
5758                    
5759                    push @$active_formatting_elements, ['#marker', '']
5760                        if $token->{tag_name} eq 'caption';
5761                    
5762                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5763                    $self->{insertion_mode} = {
5764                                               caption => IN_CAPTION_IM,
5765                                               colgroup => IN_COLUMN_GROUP_IM,
5766                                               tbody => IN_TABLE_BODY_IM,
5767                                               tfoot => IN_TABLE_BODY_IM,
5768                                               thead => IN_TABLE_BODY_IM,
5769                                              }->{$token->{tag_name}};
5770                    !!!next-token;
5771                    !!!nack ('t220.1');
5772                    next B;
5773                  } else {
5774                    die "$0: in table: <>: $token->{tag_name}";
5775                  }
5776              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5777                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5778                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5779                                      ->manakai_local_name,
5780                                  token => $token);
5781    
5782                ## As if </table>                ## As if </table>
5783                ## have a table element in table scope                ## have a table element in table scope
5784                my $i;                my $i;
5785                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5786                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5787                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5788                      !!!cp ('t221');
5789                    $i = $_;                    $i = $_;
5790                    last INSCOPE;                    last INSCOPE;
5791                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5792                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5793                    last INSCOPE;                    last INSCOPE;
5794                  }                  }
5795                } # INSCOPE                } # INSCOPE
5796                unless (defined $i) {                unless (defined $i) {
5797                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5798    ## TODO: The following is wrong, maybe.
5799                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5800                                    token => $token);
5801                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5802                    !!!nack ('t223.1');
5803                  !!!next-token;                  !!!next-token;
5804                  redo B;                  next B;
5805                }                }
5806                                
5807    ## TODO: Followings are removed from the latest spec.
5808                ## generate implied end tags                ## generate implied end tags
5809                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5810                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5811                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5812                }                }
5813    
5814                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5815                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5816                    ## NOTE: |<table><tr><table>|
5817                    !!!parse-error (type => 'not closed',
5818                                    text => $self->{open_elements}->[-1]->[0]
5819                                        ->manakai_local_name,
5820                                    token => $token);
5821                  } else {
5822                    !!!cp ('t226');
5823                }                }
5824    
5825                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5826                  pop @{$open_tables};
5827    
5828                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5829    
5830                ## reprocess            ## reprocess
5831                redo B;            !!!ack-later;
5832              } else {            next B;
5833                #          } elsif ($token->{tag_name} eq 'style') {
5834              }            if (not $open_tables->[-1]->[1]) { # tainted
5835            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5836              if ({              ## NOTE: This is a "as if in head" code clone.
5837                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5838                  }->{$token->{tag_name}}) {              next B;
5839                ## have an element in table scope            } else {
5840                my $i;              !!!cp ('t227.7');
5841                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              #
5842                  my $node = $self->{open_elements}->[$_];            }
5843                  if ($node->[1] eq $token->{tag_name}) {          } elsif ($token->{tag_name} eq 'script') {
5844                    $i = $_;            if (not $open_tables->[-1]->[1]) { # tainted
5845                    last INSCOPE;              !!!cp ('t227.6');
5846                  } elsif ({              ## NOTE: This is a "as if in head" code clone.
5847                            table => 1, html => 1,              $script_start_tag->();
5848                           }->{$node->[1]}) {              next B;
5849                    last INSCOPE;            } else {
5850                  }              !!!cp ('t227.5');
5851                } # INSCOPE              #
5852                unless (defined $i) {            }
5853                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'input') {
5854                  ## Ignore the token            if (not $open_tables->[-1]->[1]) { # tainted
5855                  !!!next-token;              if ($token->{attributes}->{type}) { ## TODO: case
5856                  redo B;                my $type = lc $token->{attributes}->{type}->{value};
5857                }                if ($type eq 'hidden') {
5858                    !!!cp ('t227.3');
5859                    !!!parse-error (type => 'in table',
5860                                    text => $token->{tag_name}, token => $token);
5861    
5862                ## 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}};  
               }  
5863    
5864                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;  
               }  
5865    
               ## 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]);  
5866                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
   
               ## Clear back to table body context  
               ## nop by definition  
5867    
5868                pop @{$self->{open_elements}};                  !!!next-token;
5869                $self->{insertion_mode} = 'in table';                  !!!ack ('t227.2.1');
5870                ## reprocess                  next B;
5871                redo B;                } else {
5872              } elsif ({                  !!!cp ('t227.2');
5873                        body => 1, caption => 1, col => 1, colgroup => 1,                  #
5874                        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;  
5875              } else {              } else {
5876                  !!!cp ('t227.1');
5877                #                #
5878              }              }
5879            } else {            } else {
5880                !!!cp ('t227.4');
5881              #              #
5882            }            }
5883                      } else {
5884            ## As if in table            !!!cp ('t227');
5885            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5886            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in row') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
5887    
5888              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table', text => $token->{tag_name},
5889              ## ISSUE: Spec says that "whenever a node would be inserted                          token => $token);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
5890    
5891                push @$active_formatting_elements, ['#marker', ''];          $insert = $insert_to_foster;
5892                          #
5893                !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
5894                redo B;              if ($token->{tag_name} eq 'tr' and
5895              } elsif ({                  $self->{insertion_mode} == IN_ROW_IM) {
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
5896                ## have an element in table scope                ## have an element in table scope
5897                my $i;                my $i;
5898                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5899                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5900                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5901                      !!!cp ('t228');
5902                    $i = $_;                    $i = $_;
5903                    last INSCOPE;                    last INSCOPE;
5904                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5905                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5906                    last INSCOPE;                    last INSCOPE;
5907                  }                  }
5908                } # INSCOPE                } # INSCOPE
5909                unless (defined $i) {                unless (defined $i) {
5910                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
5911                    !!!parse-error (type => 'unmatched end tag',
5912                                    text => $token->{tag_name}, token => $token);
5913                  ## Ignore the token                  ## Ignore the token
5914                    !!!nack ('t230.1');
5915                  !!!next-token;                  !!!next-token;
5916                  redo B;                  next B;
5917                  } else {
5918                    !!!cp ('t232');
5919                }                }
5920    
5921                ## Clear back to table row context                ## Clear back to table row context
5922                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5923                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5924                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
5925                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
5926                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5927                }                }
5928    
5929                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5930                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5931                ## reprocess                !!!next-token;
5932                redo B;                !!!nack ('t231.1');
5933                  next B;
5934              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5935                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
5936                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
5937                    ## have an element in table scope
5938                ## As if </table>                  my $i;
5939                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5940                my $i;                    my $node = $self->{open_elements}->[$_];
5941                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
5942                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
5943                  if ($node->[1] eq 'table') {                      $i = $_;
5944                    $i = $_;                      last INSCOPE;
5945                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5946                  } elsif ({                      !!!cp ('t234');
5947                            table => 1, html => 1,                      last INSCOPE;
5948                           }->{$node->[1]}) {                    }
5949                    last INSCOPE;                  } # INSCOPE
5950                    unless (defined $i) {
5951                      !!!cp ('t235');
5952    ## TODO: The following is wrong.
5953                      !!!parse-error (type => 'unmatched end tag',
5954                                      text => $token->{type}, token => $token);
5955                      ## Ignore the token
5956                      !!!nack ('t236.1');
5957                      !!!next-token;
5958                      next B;
5959                  }                  }
5960                } # INSCOPE                  
5961                unless (defined $i) {                  ## Clear back to table row context
5962                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
5963                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
5964                  !!!next-token;                    !!!cp ('t236');
5965                  redo B;  ## ISSUE: Can this state be reached?
5966                }                    pop @{$self->{open_elements}};
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               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;  
5967                  }                  }
5968                } # INSCOPE                  
5969                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
5970                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5971                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
5972                  !!!next-token;                }
5973                  redo B;  
5974                }                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5975                    ## have an element in table scope
5976                ## Clear back to table row context                  my $i;
5977                while (not {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5978                  tr => 1, html => 1,                    my $node = $self->{open_elements}->[$_];
5979                }->{$self->{open_elements}->[-1]->[1]}) {                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
5980                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      !!!cp ('t237');
5981                        $i = $_;
5982                        last INSCOPE;
5983                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5984                        !!!cp ('t238');
5985                        last INSCOPE;
5986                      }
5987                    } # INSCOPE
5988                    unless (defined $i) {
5989                      !!!cp ('t239');
5990                      !!!parse-error (type => 'unmatched end tag',
5991                                      text => $token->{tag_name}, token => $token);
5992                      ## Ignore the token
5993                      !!!nack ('t239.1');
5994                      !!!next-token;
5995                      next B;
5996                    }
5997                    
5998                    ## Clear back to table body context
5999                    while (not ($self->{open_elements}->[-1]->[1]
6000                                    & TABLE_ROWS_SCOPING_EL)) {
6001                      !!!cp ('t240');
6002                      pop @{$self->{open_elements}};
6003                    }
6004                    
6005                    ## As if <{current node}>
6006                    ## have an element in table scope
6007                    ## true by definition
6008                    
6009                    ## Clear back to table body context
6010                    ## nop by definition
6011                    
6012                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6013                    $self->{insertion_mode} = IN_TABLE_IM;
6014                    ## reprocess in the "in table" insertion mode...
6015                }                }
6016    
6017                pop @{$self->{open_elements}}; # tr                ## NOTE: </table> in the "in table" insertion mode.
6018                $self->{insertion_mode} = 'in table body';                ## When you edit the code fragment below, please ensure that
6019                !!!next-token;                ## the code for <table> in the "in table" insertion mode
6020                redo B;                ## is synced with it.
6021              } elsif ($token->{tag_name} eq 'table') {  
6022                ## As if </tr>                ## have a table element in table scope
               ## have an element in table scope  
6023                my $i;                my $i;
6024                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6025                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6026                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_EL) {
6027                      !!!cp ('t241');
6028                    $i = $_;                    $i = $_;
6029                    last INSCOPE;                    last INSCOPE;
6030                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6031                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6032                    last INSCOPE;                    last INSCOPE;
6033                  }                  }
6034                } # INSCOPE                } # INSCOPE
6035                unless (defined $i) {                unless (defined $i) {
6036                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t243');
6037                    !!!parse-error (type => 'unmatched end tag',
6038                                    text => $token->{tag_name}, token => $token);
6039                  ## Ignore the token                  ## Ignore the token
6040                    !!!nack ('t243.1');
6041                  !!!next-token;                  !!!next-token;
6042                  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}};  
6043                }                }
6044                    
6045                pop @{$self->{open_elements}}; # tr                splice @{$self->{open_elements}}, $i;
6046                $self->{insertion_mode} = 'in table body';                pop @{$open_tables};
6047                ## reprocess                
6048                redo B;                $self->_reset_insertion_mode;
6049                  
6050                  !!!next-token;
6051                  next B;
6052              } elsif ({              } elsif ({
6053                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6054                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}} and
6055                ## have an element in table scope                       $self->{insertion_mode} & ROW_IMS) {
6056                my $i;                if ($self->{insertion_mode} == IN_ROW_IM) {
6057                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
6058                  my $node = $self->{open_elements}->[$_];                  my $i;
6059                  if ($node->[1] eq $token->{tag_name}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6060                    $i = $_;                    my $node = $self->{open_elements}->[$_];
6061                    last INSCOPE;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6062                  } elsif ({                      !!!cp ('t247');
6063                            table => 1, html => 1,                      $i = $_;
6064                           }->{$node->[1]}) {                      last INSCOPE;
6065                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6066                        !!!cp ('t248');
6067                        last INSCOPE;
6068                      }
6069                    } # INSCOPE
6070                      unless (defined $i) {
6071                        !!!cp ('t249');
6072                        !!!parse-error (type => 'unmatched end tag',
6073                                        text => $token->{tag_name}, token => $token);
6074                        ## Ignore the token
6075                        !!!nack ('t249.1');
6076                        !!!next-token;
6077                        next B;
6078                      }
6079                    
6080                    ## As if </tr>
6081                    ## have an element in table scope
6082                    my $i;
6083                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6084                      my $node = $self->{open_elements}->[$_];
6085                      if ($node->[1] & TABLE_ROW_EL) {
6086                        !!!cp ('t250');
6087                        $i = $_;
6088                        last INSCOPE;
6089                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6090                        !!!cp ('t251');
6091                        last INSCOPE;
6092                      }
6093                    } # INSCOPE
6094                      unless (defined $i) {
6095                        !!!cp ('t252');
6096                        !!!parse-error (type => 'unmatched end tag',
6097                                        text => 'tr', token => $token);
6098                        ## Ignore the token
6099                        !!!nack ('t252.1');
6100                        !!!next-token;
6101                        next B;
6102                      }
6103                    
6104                    ## Clear back to table row context
6105                    while (not ($self->{open_elements}->[-1]->[1]
6106                                    & TABLE_ROW_SCOPING_EL)) {
6107                      !!!cp ('t253');
6108    ## ISSUE: Can this case be reached?
6109                      pop @{$self->{open_elements}};
6110                  }                  }
6111                } # INSCOPE                  
6112                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
6113                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6114                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
                 !!!next-token;  
                 redo B;  
6115                }                }
6116    
               ## As if </tr>  
6117                ## have an element in table scope                ## have an element in table scope
6118                my $i;                my $i;
6119                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6120                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6121                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6122                      !!!cp ('t254');
6123                    $i = $_;                    $i = $_;
6124                    last INSCOPE;                    last INSCOPE;
6125                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6126                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6127                    last INSCOPE;                    last INSCOPE;
6128                  }                  }
6129                } # INSCOPE                } # INSCOPE
6130                unless (defined $i) {                unless (defined $i) {
6131                  !!!parse-error (type => 'unmatched end tag:tr');                  !!!cp ('t256');
6132                    !!!parse-error (type => 'unmatched end tag',
6133                                    text => $token->{tag_name}, token => $token);
6134                  ## Ignore the token                  ## Ignore the token
6135                    !!!nack ('t256.1');
6136                  !!!next-token;                  !!!next-token;
6137                  redo B;                  next B;
6138                }                }
6139    
6140                ## Clear back to table row context                ## Clear back to table body context
6141                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6142                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6143                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6144                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6145                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6146                }                }
6147    
6148                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6149                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6150                ## reprocess                !!!nack ('t257.1');
6151                redo B;                !!!next-token;
6152                  next B;
6153              } elsif ({              } elsif ({
6154                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6155                        colgroup => 1, html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6156                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6157                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6158                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6159                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6160                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6161                !!!next-token;                            text => $token->{tag_name}, token => $token);
6162                redo B;            ## Ignore the token
6163              } else {            !!!nack ('t258.1');
6164                #             !!!next-token;
6165              }            next B;
6166            } else {          } else {
6167              #            !!!cp ('t259');
6168            }            !!!parse-error (type => 'in table:/',
6169                              text => $token->{tag_name}, token => $token);
6170    
6171            ## As if in table            $insert = $insert_to_foster;
6172            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
6173            $in_body->($insert_to_foster);          }
6174            redo B;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6175          } elsif ($self->{insertion_mode} eq 'in cell') {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6176            if ($token->{type} eq 'character') {                  @{$self->{open_elements}} == 1) { # redundant, maybe
6177              ## NOTE: This is a code clone of "character in body".            !!!parse-error (type => 'in body:#eof', token => $token);
6178              $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t259.1');
6179                          #
6180              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
6181              !!!cp ('t259.2');
6182              #
6183            }
6184    
6185              !!!next-token;          ## Stop parsing
6186              redo B;          last B;
6187            } elsif ($token->{type} eq 'comment') {        } else {
6188              ## NOTE: This is a code clone of "comment in body".          die "$0: $token->{type}: Unknown token type";
6189              my $comment = $self->{document}->create_comment ($token->{data});        }
6190              $self->{open_elements}->[-1]->[0]->append_child ($comment);      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6191              !!!next-token;            if ($token->{type} == CHARACTER_TOKEN) {
6192              redo B;              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6193            } elsif ($token->{type} eq 'start tag') {                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6194              if ({                unless (length $token->{data}) {
6195                   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  
6196                  !!!next-token;                  !!!next-token;
6197                  redo B;                  next B;
6198                }                }
6199                }
6200                ## Close the cell              
6201                !!!back-token; # <?>              !!!cp ('t261');
6202                $token = {type => 'end tag', tag_name => $tn};              #
6203                redo B;            } elsif ($token->{type} == START_TAG_TOKEN) {
6204              } else {              if ($token->{tag_name} eq 'col') {
6205                  !!!cp ('t262');
6206                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6207                  pop @{$self->{open_elements}};
6208                  !!!ack ('t262.1');
6209                  !!!next-token;
6210                  next B;
6211                } else {
6212                  !!!cp ('t263');
6213                #                #
6214              }              }
6215            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
6216              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'colgroup') {
6217                ## have an element in table scope                if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6218                my $i;                  !!!cp ('t264');
6219                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  !!!parse-error (type => 'unmatched end tag',
6220                  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});  
6221                  ## Ignore the token                  ## Ignore the token
6222                  !!!next-token;                  !!!next-token;
6223                  redo B;                  next B;
6224                }                } else {
6225                                  !!!cp ('t265');
6226                ## generate implied end tags                  pop @{$self->{open_elements}}; # colgroup
6227                if ({                  $self->{insertion_mode} = IN_TABLE_IM;
6228                     dd => 1, dt => 1, li => 1, p => 1,                  !!!next-token;
6229                     td => ($token->{tag_name} eq 'th'),                  next B;            
                    th => ($token->{tag_name} eq 'td'),  
                    tr => 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]);  
6230                }                }
6231                } elsif ($token->{tag_name} eq 'col') {
6232                splice @{$self->{open_elements}}, $i;                !!!cp ('t266');
6233                  !!!parse-error (type => 'unmatched end tag',
6234                $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});  
6235                ## Ignore the token                ## Ignore the token
6236                !!!next-token;                !!!next-token;
6237                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;  
6238              } else {              } else {
6239                #                !!!cp ('t267');
6240                  #
6241              }              }
6242          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6243            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6244                @{$self->{open_elements}} == 1) { # redundant, maybe
6245              !!!cp ('t270.2');
6246              ## Stop parsing.
6247              last B;
6248            } else {
6249              ## NOTE: As if </colgroup>.
6250              !!!cp ('t270.1');
6251              pop @{$self->{open_elements}}; # colgroup
6252              $self->{insertion_mode} = IN_TABLE_IM;
6253              ## Reprocess.
6254              next B;
6255            }
6256          } else {
6257            die "$0: $token->{type}: Unknown token type";
6258          }
6259    
6260              ## As if </colgroup>
6261              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6262                !!!cp ('t269');
6263    ## TODO: Wrong error type?
6264                !!!parse-error (type => 'unmatched end tag',
6265                                text => 'colgroup', token => $token);
6266                ## Ignore the token
6267                !!!nack ('t269.1');
6268                !!!next-token;
6269                next B;
6270            } else {            } else {
6271              #              !!!cp ('t270');
6272                pop @{$self->{open_elements}}; # colgroup
6273                $self->{insertion_mode} = IN_TABLE_IM;
6274                !!!ack-later;
6275                ## reprocess
6276                next B;
6277              }
6278        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6279          if ($token->{type} == CHARACTER_TOKEN) {
6280            !!!cp ('t271');
6281            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6282            !!!next-token;
6283            next B;
6284          } elsif ($token->{type} == START_TAG_TOKEN) {
6285            if ($token->{tag_name} eq 'option') {
6286              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6287                !!!cp ('t272');
6288                ## As if </option>
6289                pop @{$self->{open_elements}};
6290              } else {
6291                !!!cp ('t273');
6292            }            }
             
           $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}};  
               }  
6293    
6294                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6295                !!!next-token;            !!!nack ('t273.1');
6296                redo B;            !!!next-token;
6297              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6298                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6299                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6300                  pop @{$self->{open_elements}};              !!!cp ('t274');
6301                }              ## As if </option>
6302                pop @{$self->{open_elements}};
6303              } else {
6304                !!!cp ('t275');
6305              }
6306    
6307                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6308                  ## As if </optgroup>              !!!cp ('t276');
6309                  pop @{$self->{open_elements}};              ## As if </optgroup>
6310                }              pop @{$self->{open_elements}};
6311              } else {
6312                !!!cp ('t277');
6313              }
6314    
6315                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6316                !!!next-token;            !!!nack ('t277.1');
6317                redo B;            !!!next-token;
6318              } elsif ($token->{tag_name} eq 'select') {            next B;
6319                !!!parse-error (type => 'not closed:select');          } elsif ({
6320                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6321                ## have an element in table scope                   }->{$token->{tag_name}} or
6322                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6323                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6324                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6325                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6326                    $i = $_;                     tr => 1, td => 1, th => 1,
6327                    last INSCOPE;                    }->{$token->{tag_name}})) {
6328                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6329                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6330                           }->{$node->[1]}) {                            token => $token);
6331                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6332                  }            ## as if there were </select> (otherwise).
6333                } # INSCOPE            ## have an element in table scope
6334                unless (defined $i) {            my $i;
6335                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6336                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6337                  !!!next-token;              if ($node->[1] & SELECT_EL) {
6338                  redo B;                !!!cp ('t278');
6339                }                $i = $_;
6340                  last INSCOPE;
6341                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6342                  !!!cp ('t279');
6343                  last INSCOPE;
6344                }
6345              } # INSCOPE
6346              unless (defined $i) {
6347                !!!cp ('t280');
6348                !!!parse-error (type => 'unmatched end tag',
6349                                text => 'select', token => $token);
6350                ## Ignore the token
6351                !!!nack ('t280.1');
6352                !!!next-token;
6353                next B;
6354              }
6355                                
6356                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6357              splice @{$self->{open_elements}}, $i;
6358    
6359                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6360    
6361                !!!next-token;            if ($token->{tag_name} eq 'select') {
6362                redo B;              !!!nack ('t281.2');
6363              } else {              !!!next-token;
6364                #              next B;
6365              } else {
6366                !!!cp ('t281.1');
6367                !!!ack-later;
6368                ## Reprocess the token.
6369                next B;
6370              }
6371            } else {
6372              !!!cp ('t282');
6373              !!!parse-error (type => 'in select',
6374                              text => $token->{tag_name}, token => $token);
6375              ## Ignore the token
6376              !!!nack ('t282.1');
6377              !!!next-token;
6378              next B;
6379            }
6380          } elsif ($token->{type} == END_TAG_TOKEN) {
6381            if ($token->{tag_name} eq 'optgroup') {
6382              if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6383                  $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6384                !!!cp ('t283');
6385                ## As if </option>
6386                splice @{$self->{open_elements}}, -2;
6387              } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6388                !!!cp ('t284');
6389                pop @{$self->{open_elements}};
6390              } else {
6391                !!!cp ('t285');
6392                !!!parse-error (type => 'unmatched end tag',
6393                                text => $token->{tag_name}, token => $token);
6394                ## Ignore the token
6395              }
6396              !!!nack ('t285.1');
6397              !!!next-token;
6398              next B;
6399            } elsif ($token->{tag_name} eq 'option') {
6400              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6401                !!!cp ('t286');
6402                pop @{$self->{open_elements}};
6403              } else {
6404                !!!cp ('t287');
6405                !!!parse-error (type => 'unmatched end tag',
6406                                text => $token->{tag_name}, token => $token);
6407                ## Ignore the token
6408              }
6409              !!!nack ('t287.1');
6410              !!!next-token;
6411              next B;
6412            } elsif ($token->{tag_name} eq 'select') {
6413              ## have an element in table scope
6414              my $i;
6415              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6416                my $node = $self->{open_elements}->[$_];
6417                if ($node->[1] & SELECT_EL) {
6418                  !!!cp ('t288');
6419                  $i = $_;
6420                  last INSCOPE;
6421                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6422                  !!!cp ('t289');
6423                  last INSCOPE;
6424              }              }
6425            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6426              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
6427                if ($self->{open_elements}->[-1]->[1] eq 'option' and              !!!cp ('t290');
6428                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag',
6429                  ## As if </option>                              text => $token->{tag_name}, token => $token);
6430                  splice @{$self->{open_elements}}, -2;              ## Ignore the token
6431                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              !!!nack ('t290.1');
6432                  pop @{$self->{open_elements}};              !!!next-token;
6433                } else {              next B;
6434                  !!!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;  
               }  
6435                                
6436                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6437              splice @{$self->{open_elements}}, $i;
6438    
6439                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6440    
6441                !!!next-token;            !!!nack ('t291.1');
6442                redo B;            !!!next-token;
6443              } elsif ({            next B;
6444                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6445                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6446                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6447                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6448                                   }->{$token->{tag_name}}) {
6449                ## have an element in table scope  ## TODO: The following is wrong?
6450                my $i;            !!!parse-error (type => 'unmatched end tag',
6451                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;  
               }  
6452                                
6453                ## As if </select>            ## have an element in table scope
6454                ## have an element in table scope            my $i;
6455                undef $i;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6456                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              my $node = $self->{open_elements}->[$_];
6457                  my $node = $self->{open_elements}->[$_];              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6458                  if ($node->[1] eq 'select') {                !!!cp ('t292');
6459                    $i = $_;                $i = $_;
6460                    last INSCOPE;                last INSCOPE;
6461                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6462                            table => 1, html => 1,                !!!cp ('t293');
6463                           }->{$node->[1]}) {                last INSCOPE;
6464                    last INSCOPE;              }
6465                  }            } # INSCOPE
6466                } # INSCOPE            unless (defined $i) {
6467                unless (defined $i) {              !!!cp ('t294');
6468                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
6469                  ## Ignore the </select> token              !!!nack ('t294.1');
6470                  !!!next-token; ## TODO: ok?              !!!next-token;
6471                  redo B;              next B;
6472                }            }
6473                                
6474                splice @{$self->{open_elements}}, $i;            ## As if </select>
6475              ## have an element in table scope
6476                $self->_reset_insertion_mode;            undef $i;
6477              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6478                ## reprocess              my $node = $self->{open_elements}->[$_];
6479                redo B;              if ($node->[1] & SELECT_EL) {
6480              } else {                !!!cp ('t295');
6481                #                $i = $_;
6482                  last INSCOPE;
6483                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6484    ## ISSUE: Can this state be reached?
6485                  !!!cp ('t296');
6486                  last INSCOPE;
6487              }              }
6488            } else {            } # INSCOPE
6489              #            unless (defined $i) {
6490                !!!cp ('t297');
6491    ## TODO: The following error type is correct?
6492                !!!parse-error (type => 'unmatched end tag',
6493                                text => 'select', token => $token);
6494                ## Ignore the </select> token
6495                !!!nack ('t297.1');
6496                !!!next-token; ## TODO: ok?
6497                next B;
6498            }            }
6499                  
6500              !!!cp ('t298');
6501              splice @{$self->{open_elements}}, $i;
6502    
6503              $self->_reset_insertion_mode;
6504    
6505            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!ack-later;
6506              ## reprocess
6507              next B;
6508            } else {
6509              !!!cp ('t299');
6510              !!!parse-error (type => 'in select:/',
6511                              text => $token->{tag_name}, token => $token);
6512            ## Ignore the token            ## Ignore the token
6513              !!!nack ('t299.3');
6514            !!!next-token;            !!!next-token;
6515            redo B;            next B;
6516          } elsif ($self->{insertion_mode} eq 'after body') {          }
6517            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6518              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6519                ## As if in body                  @{$self->{open_elements}} == 1) { # redundant, maybe
6520                $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t299.1');
6521              !!!parse-error (type => 'in body:#eof', token => $token);
6522            } else {
6523              !!!cp ('t299.2');
6524            }
6525    
6526            ## Stop parsing.
6527            last B;
6528          } else {
6529            die "$0: $token->{type}: Unknown token type";
6530          }
6531        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6532          if ($token->{type} == CHARACTER_TOKEN) {
6533            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6534              my $data = $1;
6535              ## As if in body
6536              $reconstruct_active_formatting_elements->($insert_to_current);
6537                                
6538                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6539              
6540              unless (length $token->{data}) {
6541                !!!cp ('t300');
6542                !!!next-token;
6543                next B;
6544              }
6545            }
6546            
6547            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6548              !!!cp ('t301');
6549              !!!parse-error (type => 'after html:#text', token => $token);
6550              #
6551            } else {
6552              !!!cp ('t302');
6553              ## "after body" insertion mode
6554              !!!parse-error (type => 'after body:#text', token => $token);
6555              #
6556            }
6557    
6558                unless (length $token->{data}) {          $self->{insertion_mode} = IN_BODY_IM;
6559                  !!!next-token;          ## reprocess
6560                  redo B;          next B;
6561                }        } elsif ($token->{type} == START_TAG_TOKEN) {
6562              }          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6563                          !!!cp ('t303');
6564              #            !!!parse-error (type => 'after html',
6565              !!!parse-error (type => 'after body:#'.$token->{type});                            text => $token->{tag_name}, token => $token);
6566            } elsif ($token->{type} eq 'comment') {            #
6567              my $comment = $self->{document}->create_comment ($token->{data});          } else {
6568              $self->{open_elements}->[0]->[0]->append_child ($comment);            !!!cp ('t304');
6569              ## "after body" insertion mode
6570              !!!parse-error (type => 'after body',
6571                              text => $token->{tag_name}, token => $token);
6572              #
6573            }
6574    
6575            $self->{insertion_mode} = IN_BODY_IM;
6576            !!!ack-later;
6577            ## reprocess
6578            next B;
6579          } elsif ($token->{type} == END_TAG_TOKEN) {
6580            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6581              !!!cp ('t305');
6582              !!!parse-error (type => 'after html:/',
6583                              text => $token->{tag_name}, token => $token);
6584              
6585              $self->{insertion_mode} = IN_BODY_IM;
6586              ## Reprocess.
6587              next B;
6588            } else {
6589              !!!cp ('t306');
6590            }
6591    
6592            ## "after body" insertion mode
6593            if ($token->{tag_name} eq 'html') {
6594              if (defined $self->{inner_html_node}) {
6595                !!!cp ('t307');
6596                !!!parse-error (type => 'unmatched end tag',
6597                                text => 'html', token => $token);
6598                ## Ignore the token
6599              !!!next-token;              !!!next-token;
6600              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});  
             }  
6601            } else {            } else {
6602              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t308');
6603                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6604                !!!next-token;
6605                next B;
6606            }            }
6607            } else {
6608              !!!cp ('t309');
6609              !!!parse-error (type => 'after body:/',
6610                              text => $token->{tag_name}, token => $token);
6611    
6612            $self->{insertion_mode} = 'in body';            $self->{insertion_mode} = IN_BODY_IM;
6613            ## reprocess            ## reprocess
6614            redo B;            next B;
6615          } elsif ($self->{insertion_mode} eq 'in frameset') {          }
6616            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6617              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t309.2');
6618                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          ## Stop parsing
6619            last B;
6620                unless (length $token->{data}) {        } else {
6621                  !!!next-token;          die "$0: $token->{type}: Unknown token type";
6622                  redo B;        }
6623                }      } elsif ($self->{insertion_mode} & FRAME_IMS) {
6624              }        if ($token->{type} == CHARACTER_TOKEN) {
6625            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6626              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6627              
6628              unless (length $token->{data}) {
6629                !!!cp ('t310');
6630                !!!next-token;
6631                next B;
6632              }
6633            }
6634            
6635            if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6636              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6637                !!!cp ('t311');
6638                !!!parse-error (type => 'in frameset:#text', token => $token);
6639              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6640                !!!cp ('t312');
6641                !!!parse-error (type => 'after frameset:#text', token => $token);
6642              } else { # "after after frameset"
6643                !!!cp ('t313');
6644                !!!parse-error (type => 'after html:#text', token => $token);
6645              }
6646              
6647              ## Ignore the token.
6648              if (length $token->{data}) {
6649                !!!cp ('t314');
6650                ## reprocess the rest of characters
6651              } else {
6652                !!!cp ('t315');
6653                !!!next-token;
6654              }
6655              next B;
6656            }
6657            
6658            die qq[$0: Character "$token->{data}"];
6659          } elsif ($token->{type} == START_TAG_TOKEN) {
6660            if ($token->{tag_name} eq 'frameset' and
6661                $self->{insertion_mode} == IN_FRAMESET_IM) {
6662              !!!cp ('t318');
6663              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6664              !!!nack ('t318.1');
6665              !!!next-token;
6666              next B;
6667            } elsif ($token->{tag_name} eq 'frame' and
6668                     $self->{insertion_mode} == IN_FRAMESET_IM) {
6669              !!!cp ('t319');
6670              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6671              pop @{$self->{open_elements}};
6672              !!!ack ('t319.1');
6673              !!!next-token;
6674              next B;
6675            } elsif ($token->{tag_name} eq 'noframes') {
6676              !!!cp ('t320');
6677              ## NOTE: As if in head.
6678              $parse_rcdata->(CDATA_CONTENT_MODEL);
6679              next B;
6680    
6681              #            ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6682            } elsif ($token->{type} eq 'comment') {            ## has no parse error.
6683              my $comment = $self->{document}->create_comment ($token->{data});          } else {
6684              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6685                !!!cp ('t321');
6686                !!!parse-error (type => 'in frameset',
6687                                text => $token->{tag_name}, token => $token);
6688              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6689                !!!cp ('t322');
6690                !!!parse-error (type => 'after frameset',
6691                                text => $token->{tag_name}, token => $token);
6692              } else { # "after after frameset"
6693                !!!cp ('t322.2');
6694                !!!parse-error (type => 'after after frameset',
6695                                text => $token->{tag_name}, token => $token);
6696              }
6697              ## Ignore the token
6698              !!!nack ('t322.1');
6699              !!!next-token;
6700              next B;
6701            }
6702          } elsif ($token->{type} == END_TAG_TOKEN) {
6703            if ($token->{tag_name} eq 'frameset' and
6704                $self->{insertion_mode} == IN_FRAMESET_IM) {
6705              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6706                  @{$self->{open_elements}} == 1) {
6707                !!!cp ('t325');
6708                !!!parse-error (type => 'unmatched end tag',
6709                                text => $token->{tag_name}, token => $token);
6710                ## Ignore the token
6711              !!!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 {  
               #  
             }  
6712            } else {            } else {
6713              #              !!!cp ('t326');
6714                pop @{$self->{open_elements}};
6715                !!!next-token;
6716            }            }
6717              
6718            if (defined $token->{tag_name}) {            if (not defined $self->{inner_html_node} and
6719              !!!parse-error (type => 'in frameset:'.$token->{tag_name});                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6720                !!!cp ('t327');
6721                $self->{insertion_mode} = AFTER_FRAMESET_IM;
6722            } else {            } else {
6723              !!!parse-error (type => 'in frameset:#'.$token->{type});              !!!cp ('t328');
6724              }
6725              next B;
6726            } elsif ($token->{tag_name} eq 'html' and
6727                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6728              !!!cp ('t329');
6729              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6730              !!!next-token;
6731              next B;
6732            } else {
6733              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6734                !!!cp ('t330');
6735                !!!parse-error (type => 'in frameset:/',
6736                                text => $token->{tag_name}, token => $token);
6737              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6738                !!!cp ('t330.1');
6739                !!!parse-error (type => 'after frameset:/',
6740                                text => $token->{tag_name}, token => $token);
6741              } else { # "after after html"
6742                !!!cp ('t331');
6743                !!!parse-error (type => 'after after frameset:/',
6744                                text => $token->{tag_name}, token => $token);
6745            }            }
6746            ## Ignore the token            ## Ignore the token
6747            !!!next-token;            !!!next-token;
6748            redo B;            next B;
6749          } elsif ($self->{insertion_mode} eq 'after frameset') {          }
6750            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6751              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6752                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                  @{$self->{open_elements}} == 1) { # redundant, maybe
6753              !!!cp ('t331.1');
6754              !!!parse-error (type => 'in body:#eof', token => $token);
6755            } else {
6756              !!!cp ('t331.2');
6757            }
6758            
6759            ## Stop parsing
6760            last B;
6761          } else {
6762            die "$0: $token->{type}: Unknown token type";
6763          }
6764    
6765                unless (length $token->{data}) {        ## ISSUE: An issue in spec here
6766                  !!!next-token;      } else {
6767                  redo B;        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6768                }      }
6769    
6770        ## "in body" insertion mode
6771        if ($token->{type} == START_TAG_TOKEN) {
6772          if ($token->{tag_name} eq 'script') {
6773            !!!cp ('t332');
6774            ## NOTE: This is an "as if in head" code clone
6775            $script_start_tag->();
6776            next B;
6777          } elsif ($token->{tag_name} eq 'style') {
6778            !!!cp ('t333');
6779            ## NOTE: This is an "as if in head" code clone
6780            $parse_rcdata->(CDATA_CONTENT_MODEL);
6781            next B;
6782          } elsif ({
6783                    base => 1, link => 1,
6784                   }->{$token->{tag_name}}) {
6785            !!!cp ('t334');
6786            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6787            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6788            pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6789            !!!ack ('t334.1');
6790            !!!next-token;
6791            next B;
6792          } elsif ($token->{tag_name} eq 'meta') {
6793            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6794            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6795            my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
6796    
6797            unless ($self->{confident}) {
6798              if ($token->{attributes}->{charset}) {
6799                !!!cp ('t335');
6800                ## NOTE: Whether the encoding is supported or not is handled
6801                ## in the {change_encoding} callback.
6802                $self->{change_encoding}
6803                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6804                
6805                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6806                    ->set_user_data (manakai_has_reference =>
6807                                         $token->{attributes}->{charset}
6808                                             ->{has_reference});
6809              } elsif ($token->{attributes}->{content}) {
6810                if ($token->{attributes}->{content}->{value}
6811                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6812                        [\x09\x0A\x0C\x0D\x20]*=
6813                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6814                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6815                       /x) {
6816                  !!!cp ('t336');
6817                  ## NOTE: Whether the encoding is supported or not is handled
6818                  ## in the {change_encoding} callback.
6819                  $self->{change_encoding}
6820                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6821                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6822                      ->set_user_data (manakai_has_reference =>
6823                                           $token->{attributes}->{content}
6824                                                 ->{has_reference});
6825              }              }
6826              }
6827            } else {
6828              if ($token->{attributes}->{charset}) {
6829                !!!cp ('t337');
6830                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6831                    ->set_user_data (manakai_has_reference =>
6832                                         $token->{attributes}->{charset}
6833                                             ->{has_reference});
6834              }
6835              if ($token->{attributes}->{content}) {
6836                !!!cp ('t338');
6837                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6838                    ->set_user_data (manakai_has_reference =>
6839                                         $token->{attributes}->{content}
6840                                             ->{has_reference});
6841              }
6842            }
6843    
6844              #          !!!ack ('t338.1');
6845            } elsif ($token->{type} eq 'comment') {          !!!next-token;
6846              my $comment = $self->{document}->create_comment ($token->{data});          next B;
6847              $self->{open_elements}->[-1]->[0]->append_child ($comment);        } elsif ($token->{tag_name} eq 'title') {
6848              !!!next-token;          !!!cp ('t341');
6849              redo B;          ## NOTE: This is an "as if in head" code clone
6850            } elsif ($token->{type} eq 'start tag') {          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6851              if ($token->{tag_name} eq 'noframes') {          next B;
6852                $in_body->($insert_to_current);        } elsif ($token->{tag_name} eq 'body') {
6853                redo B;          !!!parse-error (type => 'in body', text => 'body', token => $token);
6854              } else {                
6855                #          if (@{$self->{open_elements}} == 1 or
6856                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6857              !!!cp ('t342');
6858              ## Ignore the token
6859            } else {
6860              my $body_el = $self->{open_elements}->[1]->[0];
6861              for my $attr_name (keys %{$token->{attributes}}) {
6862                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6863                  !!!cp ('t343');
6864                  $body_el->set_attribute_ns
6865                    (undef, [undef, $attr_name],
6866                     $token->{attributes}->{$attr_name}->{value});
6867              }              }
6868            } elsif ($token->{type} eq 'end tag') {            }
6869              if ($token->{tag_name} eq 'html') {          }
6870                $phase = 'trailing end';          !!!nack ('t343.1');
6871            !!!next-token;
6872            next B;
6873          } elsif ({
6874                    address => 1, blockquote => 1, center => 1, dir => 1,
6875                    div => 1, dl => 1, fieldset => 1,
6876                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
6877                    menu => 1, ol => 1, p => 1, ul => 1,
6878                    pre => 1, listing => 1,
6879                    form => 1,
6880                    table => 1,
6881                    hr => 1,
6882                   }->{$token->{tag_name}}) {
6883            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6884              !!!cp ('t350');
6885              !!!parse-error (type => 'in form:form', token => $token);
6886              ## Ignore the token
6887              !!!nack ('t350.1');
6888              !!!next-token;
6889              next B;
6890            }
6891    
6892            ## has a p element in scope
6893            INSCOPE: for (reverse @{$self->{open_elements}}) {
6894              if ($_->[1] & P_EL) {
6895                !!!cp ('t344');
6896                !!!back-token; # <form>
6897                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6898                          line => $token->{line}, column => $token->{column}};
6899                next B;
6900              } elsif ($_->[1] & SCOPING_EL) {
6901                !!!cp ('t345');
6902                last INSCOPE;
6903              }
6904            } # INSCOPE
6905              
6906            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6907            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6908              !!!nack ('t346.1');
6909              !!!next-token;
6910              if ($token->{type} == CHARACTER_TOKEN) {
6911                $token->{data} =~ s/^\x0A//;
6912                unless (length $token->{data}) {
6913                  !!!cp ('t346');
6914                !!!next-token;                !!!next-token;
               redo B;  
6915              } else {              } else {
6916                #                !!!cp ('t349');
6917              }              }
6918            } else {            } else {
6919              #              !!!cp ('t348');
6920              }
6921            } elsif ($token->{tag_name} eq 'form') {
6922              !!!cp ('t347.1');
6923              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6924    
6925              !!!nack ('t347.2');
6926              !!!next-token;
6927            } elsif ($token->{tag_name} eq 'table') {
6928              !!!cp ('t382');
6929              push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6930              
6931              $self->{insertion_mode} = IN_TABLE_IM;
6932    
6933              !!!nack ('t382.1');
6934              !!!next-token;
6935            } elsif ($token->{tag_name} eq 'hr') {
6936              !!!cp ('t386');
6937              pop @{$self->{open_elements}};
6938            
6939              !!!nack ('t386.1');
6940              !!!next-token;
6941            } else {
6942              !!!nack ('t347.1');
6943              !!!next-token;
6944            }
6945            next B;
6946          } elsif ({li => 1, dt => 1, dd => 1}->{$token->{tag_name}}) {
6947            ## has a p element in scope
6948            INSCOPE: for (reverse @{$self->{open_elements}}) {
6949              if ($_->[1] & P_EL) {
6950                !!!cp ('t353');
6951                !!!back-token; # <x>
6952                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6953                          line => $token->{line}, column => $token->{column}};
6954                next B;
6955              } elsif ($_->[1] & SCOPING_EL) {
6956                !!!cp ('t354');
6957                last INSCOPE;
6958            }            }
6959            } # INSCOPE
6960                        
6961            if (defined $token->{tag_name}) {          ## Step 1
6962              !!!parse-error (type => 'after frameset:'.$token->{tag_name});          my $i = -1;
6963            my $node = $self->{open_elements}->[$i];
6964            my $li_or_dtdd = {li => {li => 1},
6965                              dt => {dt => 1, dd => 1},
6966                              dd => {dt => 1, dd => 1}}->{$token->{tag_name}};
6967            LI: {
6968              ## Step 2
6969              if ($li_or_dtdd->{$node->[0]->manakai_local_name}) {
6970                if ($i != -1) {
6971                  !!!cp ('t355');
6972                  !!!parse-error (type => 'not closed',
6973                                  text => $self->{open_elements}->[-1]->[0]
6974                                      ->manakai_local_name,
6975                                  token => $token);
6976                } else {
6977                  !!!cp ('t356');
6978                }
6979                splice @{$self->{open_elements}}, $i;
6980                last LI;
6981            } else {            } else {
6982              !!!parse-error (type => 'after frameset:#'.$token->{type});              !!!cp ('t357');
6983              }
6984              
6985              ## Step 3
6986              if (not ($node->[1] & FORMATTING_EL) and
6987                  #not $phrasing_category->{$node->[1]} and
6988                  ($node->[1] & SPECIAL_EL or
6989                   $node->[1] & SCOPING_EL) and
6990                  not ($node->[1] & ADDRESS_EL) and
6991                  not ($node->[1] & DIV_EL)) {
6992                !!!cp ('t358');
6993                last LI;
6994              }
6995              
6996              !!!cp ('t359');
6997              ## Step 4
6998              $i--;
6999              $node = $self->{open_elements}->[$i];
7000              redo LI;
7001            } # LI
7002              
7003            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7004            !!!nack ('t359.1');
7005            !!!next-token;
7006            next B;
7007          } elsif ($token->{tag_name} eq 'plaintext') {
7008            ## has a p element in scope
7009            INSCOPE: for (reverse @{$self->{open_elements}}) {
7010              if ($_->[1] & P_EL) {
7011                !!!cp ('t367');
7012                !!!back-token; # <plaintext>
7013                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7014                          line => $token->{line}, column => $token->{column}};
7015                next B;
7016              } elsif ($_->[1] & SCOPING_EL) {
7017                !!!cp ('t368');
7018                last INSCOPE;
7019              }
7020            } # INSCOPE
7021              
7022            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7023              
7024            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7025              
7026            !!!nack ('t368.1');
7027            !!!next-token;
7028            next B;
7029          } elsif ($token->{tag_name} eq 'a') {
7030            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7031              my $node = $active_formatting_elements->[$i];
7032              if ($node->[1] & A_EL) {
7033                !!!cp ('t371');
7034                !!!parse-error (type => 'in a:a', token => $token);
7035                
7036                !!!back-token; # <a>
7037                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7038                          line => $token->{line}, column => $token->{column}};
7039                $formatting_end_tag->($token);
7040                
7041                AFE2: for (reverse 0..$#$active_formatting_elements) {
7042                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7043                    !!!cp ('t372');
7044                    splice @$active_formatting_elements, $_, 1;
7045                    last AFE2;
7046                  }
7047                } # AFE2
7048                OE: for (reverse 0..$#{$self->{open_elements}}) {
7049                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7050                    !!!cp ('t373');
7051                    splice @{$self->{open_elements}}, $_, 1;
7052                    last OE;
7053                  }
7054                } # OE
7055                last AFE;
7056              } elsif ($node->[0] eq '#marker') {
7057                !!!cp ('t374');
7058                last AFE;
7059              }
7060            } # AFE
7061              
7062            $reconstruct_active_formatting_elements->($insert_to_current);
7063    
7064            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7065            push @$active_formatting_elements, $self->{open_elements}->[-1];
7066    
7067            !!!nack ('t374.1');
7068            !!!next-token;
7069            next B;
7070          } elsif ($token->{tag_name} eq 'nobr') {
7071            $reconstruct_active_formatting_elements->($insert_to_current);
7072    
7073            ## has a |nobr| element in scope
7074            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7075              my $node = $self->{open_elements}->[$_];
7076              if ($node->[1] & NOBR_EL) {
7077                !!!cp ('t376');
7078                !!!parse-error (type => 'in nobr:nobr', token => $token);
7079                !!!back-token; # <nobr>
7080                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7081                          line => $token->{line}, column => $token->{column}};
7082                next B;
7083              } elsif ($node->[1] & SCOPING_EL) {
7084                !!!cp ('t377');
7085                last INSCOPE;
7086              }
7087            } # INSCOPE
7088            
7089            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7090            push @$active_formatting_elements, $self->{open_elements}->[-1];
7091            
7092            !!!nack ('t377.1');
7093            !!!next-token;
7094            next B;
7095          } elsif ($token->{tag_name} eq 'button') {
7096            ## has a button element in scope
7097            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7098              my $node = $self->{open_elements}->[$_];
7099              if ($node->[1] & BUTTON_EL) {
7100                !!!cp ('t378');
7101                !!!parse-error (type => 'in button:button', token => $token);
7102                !!!back-token; # <button>
7103                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7104                          line => $token->{line}, column => $token->{column}};
7105                next B;
7106              } elsif ($node->[1] & SCOPING_EL) {
7107                !!!cp ('t379');
7108                last INSCOPE;
7109            }            }
7110            } # INSCOPE
7111              
7112            $reconstruct_active_formatting_elements->($insert_to_current);
7113              
7114            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7115    
7116            ## TODO: associate with $self->{form_element} if defined
7117    
7118            push @$active_formatting_elements, ['#marker', ''];
7119    
7120            !!!nack ('t379.1');
7121            !!!next-token;
7122            next B;
7123          } elsif ({
7124                    xmp => 1,
7125                    iframe => 1,
7126                    noembed => 1,
7127                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7128                    noscript => 0, ## TODO: 1 if scripting is enabled
7129                   }->{$token->{tag_name}}) {
7130            if ($token->{tag_name} eq 'xmp') {
7131              !!!cp ('t381');
7132              $reconstruct_active_formatting_elements->($insert_to_current);
7133            } else {
7134              !!!cp ('t399');
7135            }
7136            ## NOTE: There is an "as if in body" code clone.
7137            $parse_rcdata->(CDATA_CONTENT_MODEL);
7138            next B;
7139          } elsif ($token->{tag_name} eq 'isindex') {
7140            !!!parse-error (type => 'isindex', token => $token);
7141            
7142            if (defined $self->{form_element}) {
7143              !!!cp ('t389');
7144            ## Ignore the token            ## Ignore the token
7145              !!!nack ('t389'); ## NOTE: Not acknowledged.
7146            !!!next-token;            !!!next-token;
7147            redo B;            next B;
7148            } else {
7149              !!!ack ('t391.1');
7150    
7151            ## ISSUE: An issue in spec there            my $at = $token->{attributes};
7152              my $form_attrs;
7153              $form_attrs->{action} = $at->{action} if $at->{action};
7154              my $prompt_attr = $at->{prompt};
7155              $at->{name} = {name => 'name', value => 'isindex'};
7156              delete $at->{action};
7157              delete $at->{prompt};
7158              my @tokens = (
7159                            {type => START_TAG_TOKEN, tag_name => 'form',
7160                             attributes => $form_attrs,
7161                             line => $token->{line}, column => $token->{column}},
7162                            {type => START_TAG_TOKEN, tag_name => 'hr',
7163                             line => $token->{line}, column => $token->{column}},
7164                            {type => START_TAG_TOKEN, tag_name => 'p',
7165                             line => $token->{line}, column => $token->{column}},
7166                            {type => START_TAG_TOKEN, tag_name => 'label',
7167                             line => $token->{line}, column => $token->{column}},
7168                           );
7169              if ($prompt_attr) {
7170                !!!cp ('t390');
7171                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7172                               #line => $token->{line}, column => $token->{column},
7173                              };
7174              } else {
7175                !!!cp ('t391');
7176                push @tokens, {type => CHARACTER_TOKEN,
7177                               data => 'This is a searchable index. Insert your search keywords here: ',
7178                               #line => $token->{line}, column => $token->{column},
7179                              }; # SHOULD
7180                ## TODO: make this configurable
7181              }
7182              push @tokens,
7183                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7184                             line => $token->{line}, column => $token->{column}},
7185                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7186                            {type => END_TAG_TOKEN, tag_name => 'label',
7187                             line => $token->{line}, column => $token->{column}},
7188                            {type => END_TAG_TOKEN, tag_name => 'p',
7189                             line => $token->{line}, column => $token->{column}},
7190                            {type => START_TAG_TOKEN, tag_name => 'hr',
7191                             line => $token->{line}, column => $token->{column}},
7192                            {type => END_TAG_TOKEN, tag_name => 'form',
7193                             line => $token->{line}, column => $token->{column}};
7194              !!!back-token (@tokens);
7195              !!!next-token;
7196              next B;
7197            }
7198          } elsif ($token->{tag_name} eq 'textarea') {
7199            my $tag_name = $token->{tag_name};
7200            my $el;
7201            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7202            
7203            ## TODO: $self->{form_element} if defined
7204            $self->{content_model} = RCDATA_CONTENT_MODEL;
7205            delete $self->{escape}; # MUST
7206            
7207            $insert->($el);
7208            
7209            my $text = '';
7210            !!!nack ('t392.1');
7211            !!!next-token;
7212            if ($token->{type} == CHARACTER_TOKEN) {
7213              $token->{data} =~ s/^\x0A//;
7214              unless (length $token->{data}) {
7215                !!!cp ('t392');
7216                !!!next-token;
7217              } else {
7218                !!!cp ('t393');
7219              }
7220          } else {          } else {
7221            die "$0: $self->{insertion_mode}: Unknown insertion mode";            !!!cp ('t394');
7222            }
7223            while ($token->{type} == CHARACTER_TOKEN) {
7224              !!!cp ('t395');
7225              $text .= $token->{data};
7226              !!!next-token;
7227            }
7228            if (length $text) {
7229              !!!cp ('t396');
7230              $el->manakai_append_text ($text);
7231            }
7232            
7233            $self->{content_model} = PCDATA_CONTENT_MODEL;
7234            
7235            if ($token->{type} == END_TAG_TOKEN and
7236                $token->{tag_name} eq $tag_name) {
7237              !!!cp ('t397');
7238              ## Ignore the token
7239            } else {
7240              !!!cp ('t398');
7241              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7242          }          }
       }  
     } 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  
7243          !!!next-token;          !!!next-token;
7244          redo B;          next B;
7245        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{tag_name} eq 'rt' or
7246          my $comment = $self->{document}->create_comment ($token->{data});                 $token->{tag_name} eq 'rp') {
7247          $self->{document}->append_child ($comment);          ## has a |ruby| element in scope
7248            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7249              my $node = $self->{open_elements}->[$_];
7250              if ($node->[1] & RUBY_EL) {
7251                !!!cp ('t398.1');
7252                ## generate implied end tags
7253                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7254                  !!!cp ('t398.2');
7255                  pop @{$self->{open_elements}};
7256                }
7257                unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7258                  !!!cp ('t398.3');
7259                  !!!parse-error (type => 'not closed',
7260                                  text => $self->{open_elements}->[-1]->[0]
7261                                      ->manakai_local_name,
7262                                  token => $token);
7263                  pop @{$self->{open_elements}}
7264                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7265                }
7266                last INSCOPE;
7267              } elsif ($node->[1] & SCOPING_EL) {
7268                !!!cp ('t398.4');
7269                last INSCOPE;
7270              }
7271            } # INSCOPE
7272    
7273            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7274    
7275            !!!nack ('t398.5');
7276          !!!next-token;          !!!next-token;
7277          redo B;          redo B;
7278        } elsif ($token->{type} eq 'character') {        } elsif ($token->{tag_name} eq 'math' or
7279          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                 $token->{tag_name} eq 'svg') {
7280            my $data = $1;          $reconstruct_active_formatting_elements->($insert_to_current);
7281            ## As if in the main phase.  
7282            ## NOTE: The insertion mode in the main phase          ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7283            ## just before the phase has been changed to the trailing  
7284            ## end phase is either "after body" or "after frameset".          ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7285            $reconstruct_active_formatting_elements->($insert_to_current)  
7286              if $phase eq 'main';          ## "adjust foreign attributes" - done in insert-element-f
7287            
7288            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7289            
7290            if ($self->{self_closing}) {
7291              pop @{$self->{open_elements}};
7292              !!!ack ('t398.1');
7293            } else {
7294              !!!cp ('t398.2');
7295              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7296              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7297              ## mode, "in body" (not "in foreign content") secondary insertion
7298              ## mode, maybe.
7299            }
7300    
7301            !!!next-token;
7302            next B;
7303          } elsif ({
7304                    caption => 1, col => 1, colgroup => 1, frame => 1,
7305                    frameset => 1, head => 1, option => 1, optgroup => 1,
7306                    tbody => 1, td => 1, tfoot => 1, th => 1,
7307                    thead => 1, tr => 1,
7308                   }->{$token->{tag_name}}) {
7309            !!!cp ('t401');
7310            !!!parse-error (type => 'in body',
7311                            text => $token->{tag_name}, token => $token);
7312            ## Ignore the token
7313            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7314            !!!next-token;
7315            next B;
7316            
7317            ## ISSUE: An issue on HTML5 new elements in the spec.
7318          } else {
7319            if ($token->{tag_name} eq 'image') {
7320              !!!cp ('t384');
7321              !!!parse-error (type => 'image', token => $token);
7322              $token->{tag_name} = 'img';
7323            } else {
7324              !!!cp ('t385');
7325            }
7326    
7327            ## NOTE: There is an "as if <br>" code clone.
7328            $reconstruct_active_formatting_elements->($insert_to_current);
7329            
7330            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7331    
7332            if ({
7333                 applet => 1, marquee => 1, object => 1,
7334                }->{$token->{tag_name}}) {
7335              !!!cp ('t380');
7336              push @$active_formatting_elements, ['#marker', ''];
7337              !!!nack ('t380.1');
7338            } elsif ({
7339                      b => 1, big => 1, em => 1, font => 1, i => 1,
7340                      s => 1, small => 1, strile => 1,
7341                      strong => 1, tt => 1, u => 1,
7342                     }->{$token->{tag_name}}) {
7343              !!!cp ('t375');
7344              push @$active_formatting_elements, $self->{open_elements}->[-1];
7345              !!!nack ('t375.1');
7346            } elsif ($token->{tag_name} eq 'input') {
7347              !!!cp ('t388');
7348              ## TODO: associate with $self->{form_element} if defined
7349              pop @{$self->{open_elements}};
7350              !!!ack ('t388.2');
7351            } elsif ({
7352                      area => 1, basefont => 1, bgsound => 1, br => 1,
7353                      embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,
7354                      #image => 1,
7355                     }->{$token->{tag_name}}) {
7356              !!!cp ('t388.1');
7357              pop @{$self->{open_elements}};
7358              !!!ack ('t388.3');
7359            } elsif ($token->{tag_name} eq 'select') {
7360              ## TODO: associate with $self->{form_element} if defined
7361            
7362              if ($self->{insertion_mode} & TABLE_IMS or
7363                  $self->{insertion_mode} & BODY_TABLE_IMS or
7364                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7365                !!!cp ('t400.1');
7366                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7367              } else {
7368                !!!cp ('t400.2');
7369                $self->{insertion_mode} = IN_SELECT_IM;
7370              }
7371              !!!nack ('t400.3');
7372            } else {
7373              !!!nack ('t402');
7374            }
7375            
7376            !!!next-token;
7377            next B;
7378          }
7379        } elsif ($token->{type} == END_TAG_TOKEN) {
7380          if ($token->{tag_name} eq 'body') {
7381            ## has a |body| element in scope
7382            my $i;
7383            INSCOPE: {
7384              for (reverse @{$self->{open_elements}}) {
7385                if ($_->[1] & BODY_EL) {
7386                  !!!cp ('t405');
7387                  $i = $_;
7388                  last INSCOPE;
7389                } elsif ($_->[1] & SCOPING_EL) {
7390                  !!!cp ('t405.1');
7391                  last;
7392                }
7393              }
7394    
7395              !!!parse-error (type => 'start tag not allowed',
7396                              text => $token->{tag_name}, token => $token);
7397              ## NOTE: Ignore the token.
7398              !!!next-token;
7399              next B;
7400            } # INSCOPE
7401    
7402            for (@{$self->{open_elements}}) {
7403              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7404                !!!cp ('t403');
7405                !!!parse-error (type => 'not closed',
7406                                text => $_->[0]->manakai_local_name,
7407                                token => $token);
7408                last;
7409              } else {
7410                !!!cp ('t404');
7411              }
7412            }
7413    
7414            $self->{insertion_mode} = AFTER_BODY_IM;
7415            !!!next-token;
7416            next B;
7417          } elsif ($token->{tag_name} eq 'html') {
7418            ## TODO: Update this code.  It seems that the code below is not
7419            ## up-to-date, though it has same effect as speced.
7420            if (@{$self->{open_elements}} > 1 and
7421                $self->{open_elements}->[1]->[1] & BODY_EL) {
7422              ## ISSUE: There is an issue in the spec.
7423              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7424                !!!cp ('t406');
7425                !!!parse-error (type => 'not closed',
7426                                text => $self->{open_elements}->[1]->[0]
7427                                    ->manakai_local_name,
7428                                token => $token);
7429              } else {
7430                !!!cp ('t407');
7431              }
7432              $self->{insertion_mode} = AFTER_BODY_IM;
7433              ## reprocess
7434              next B;
7435            } else {
7436              !!!cp ('t408');
7437              !!!parse-error (type => 'unmatched end tag',
7438                              text => $token->{tag_name}, token => $token);
7439              ## Ignore the token
7440              !!!next-token;
7441              next B;
7442            }
7443          } elsif ({
7444                    address => 1, blockquote => 1, center => 1, dir => 1,
7445                    div => 1, dl => 1, fieldset => 1, listing => 1,
7446                    menu => 1, ol => 1, pre => 1, ul => 1,
7447                    dd => 1, dt => 1, li => 1,
7448                    applet => 1, button => 1, marquee => 1, object => 1,
7449                   }->{$token->{tag_name}}) {
7450            ## has an element in scope
7451            my $i;
7452            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7453              my $node = $self->{open_elements}->[$_];
7454              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7455                !!!cp ('t410');
7456                $i = $_;
7457                last INSCOPE;
7458              } elsif ($node->[1] & SCOPING_EL) {
7459                !!!cp ('t411');
7460                last INSCOPE;
7461              }
7462            } # INSCOPE
7463    
7464            unless (defined $i) { # has an element in scope
7465              !!!cp ('t413');
7466              !!!parse-error (type => 'unmatched end tag',
7467                              text => $token->{tag_name}, token => $token);
7468              ## NOTE: Ignore the token.
7469            } else {
7470              ## Step 1. generate implied end tags
7471              while ({
7472                      ## END_TAG_OPTIONAL_EL
7473                      dd => ($token->{tag_name} ne 'dd'),
7474                      dt => ($token->{tag_name} ne 'dt'),
7475                      li => ($token->{tag_name} ne 'li'),
7476                      p => 1,
7477                      rt => 1,
7478                      rp => 1,
7479                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7480                !!!cp ('t409');
7481                pop @{$self->{open_elements}};
7482              }
7483    
7484              ## Step 2.
7485              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7486                      ne $token->{tag_name}) {
7487                !!!cp ('t412');
7488                !!!parse-error (type => 'not closed',
7489                                text => $self->{open_elements}->[-1]->[0]
7490                                    ->manakai_local_name,
7491                                token => $token);
7492              } else {
7493                !!!cp ('t414');
7494              }
7495    
7496              ## Step 3.
7497              splice @{$self->{open_elements}}, $i;
7498    
7499              ## Step 4.
7500              $clear_up_to_marker->()
7501                  if {
7502                    applet => 1, button => 1, marquee => 1, object => 1,
7503                  }->{$token->{tag_name}};
7504            }
7505            !!!next-token;
7506            next B;
7507          } elsif ($token->{tag_name} eq 'form') {
7508            undef $self->{form_element};
7509    
7510            ## has an element in scope
7511            my $i;
7512            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7513              my $node = $self->{open_elements}->[$_];
7514              if ($node->[1] & FORM_EL) {
7515                !!!cp ('t418');
7516                $i = $_;
7517                last INSCOPE;
7518              } elsif ($node->[1] & SCOPING_EL) {
7519                !!!cp ('t419');
7520                last INSCOPE;
7521              }
7522            } # INSCOPE
7523    
7524            unless (defined $i) { # has an element in scope
7525              !!!cp ('t421');
7526              !!!parse-error (type => 'unmatched end tag',
7527                              text => $token->{tag_name}, token => $token);
7528              ## NOTE: Ignore the token.
7529            } else {
7530              ## Step 1. generate implied end tags
7531              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7532                !!!cp ('t417');
7533                pop @{$self->{open_elements}};
7534              }
7535                        
7536            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 2.
7537              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7538                      ne $token->{tag_name}) {
7539                !!!cp ('t417.1');
7540                !!!parse-error (type => 'not closed',
7541                                text => $self->{open_elements}->[-1]->[0]
7542                                    ->manakai_local_name,
7543                                token => $token);
7544              } else {
7545                !!!cp ('t420');
7546              }  
7547                        
7548            unless (length $token->{data}) {            ## Step 3.
7549              !!!next-token;            splice @{$self->{open_elements}}, $i;
7550              redo B;          }
7551    
7552            !!!next-token;
7553            next B;
7554          } elsif ({
7555                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7556                   }->{$token->{tag_name}}) {
7557            ## has an element in scope
7558            my $i;
7559            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7560              my $node = $self->{open_elements}->[$_];
7561              if ($node->[1] & HEADING_EL) {
7562                !!!cp ('t423');
7563                $i = $_;
7564                last INSCOPE;
7565              } elsif ($node->[1] & SCOPING_EL) {
7566                !!!cp ('t424');
7567                last INSCOPE;
7568              }
7569            } # INSCOPE
7570    
7571            unless (defined $i) { # has an element in scope
7572              !!!cp ('t425.1');
7573              !!!parse-error (type => 'unmatched end tag',
7574                              text => $token->{tag_name}, token => $token);
7575              ## NOTE: Ignore the token.
7576            } else {
7577              ## Step 1. generate implied end tags
7578              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7579                !!!cp ('t422');
7580                pop @{$self->{open_elements}};
7581              }
7582              
7583              ## Step 2.
7584              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7585                      ne $token->{tag_name}) {
7586                !!!cp ('t425');
7587                !!!parse-error (type => 'unmatched end tag',
7588                                text => $token->{tag_name}, token => $token);
7589              } else {
7590                !!!cp ('t426');
7591              }
7592    
7593              ## Step 3.
7594              splice @{$self->{open_elements}}, $i;
7595            }
7596            
7597            !!!next-token;
7598            next B;
7599          } elsif ($token->{tag_name} eq 'p') {
7600            ## has an element in scope
7601            my $i;
7602            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7603              my $node = $self->{open_elements}->[$_];
7604              if ($node->[1] & P_EL) {
7605                !!!cp ('t410.1');
7606                $i = $_;
7607                last INSCOPE;
7608              } elsif ($node->[1] & SCOPING_EL) {
7609                !!!cp ('t411.1');
7610                last INSCOPE;
7611            }            }
7612            } # INSCOPE
7613    
7614            if (defined $i) {
7615              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7616                      ne $token->{tag_name}) {
7617                !!!cp ('t412.1');
7618                !!!parse-error (type => 'not closed',
7619                                text => $self->{open_elements}->[-1]->[0]
7620                                    ->manakai_local_name,
7621                                token => $token);
7622              } else {
7623                !!!cp ('t414.1');
7624              }
7625    
7626              splice @{$self->{open_elements}}, $i;
7627            } else {
7628              !!!cp ('t413.1');
7629              !!!parse-error (type => 'unmatched end tag',
7630                              text => $token->{tag_name}, token => $token);
7631    
7632              !!!cp ('t415.1');
7633              ## As if <p>, then reprocess the current token
7634              my $el;
7635              !!!create-element ($el, $HTML_NS, 'p',, $token);
7636              $insert->($el);
7637              ## NOTE: Not inserted into |$self->{open_elements}|.
7638          }          }
7639    
7640          !!!parse-error (type => 'after html:#character');          !!!next-token;
7641          $phase = 'main';          next B;
7642          ## reprocess        } elsif ({
7643          redo B;                  a => 1,
7644        } elsif ($token->{type} eq 'start tag' or                  b => 1, big => 1, em => 1, font => 1, i => 1,
7645                 $token->{type} eq 'end tag') {                  nobr => 1, s => 1, small => 1, strile => 1,
7646          !!!parse-error (type => 'after html:'.$token->{tag_name});                  strong => 1, tt => 1, u => 1,
7647          $phase = 'main';                 }->{$token->{tag_name}}) {
7648          ## reprocess          !!!cp ('t427');
7649          redo B;          $formatting_end_tag->($token);
7650        } elsif ($token->{type} eq 'end-of-file') {          next B;
7651          ## Stop parsing        } elsif ($token->{tag_name} eq 'br') {
7652          last B;          !!!cp ('t428');
7653            !!!parse-error (type => 'unmatched end tag',
7654                            text => 'br', token => $token);
7655    
7656            ## As if <br>
7657            $reconstruct_active_formatting_elements->($insert_to_current);
7658            
7659            my $el;
7660            !!!create-element ($el, $HTML_NS, 'br',, $token);
7661            $insert->($el);
7662            
7663            ## Ignore the token.
7664            !!!next-token;
7665            next B;
7666          } elsif ({
7667                    caption => 1, col => 1, colgroup => 1, frame => 1,
7668                    frameset => 1, head => 1, option => 1, optgroup => 1,
7669                    tbody => 1, td => 1, tfoot => 1, th => 1,
7670                    thead => 1, tr => 1,
7671                    area => 1, basefont => 1, bgsound => 1,
7672                    embed => 1, hr => 1, iframe => 1, image => 1,
7673                    img => 1, input => 1, isindex => 1, noembed => 1,
7674                    noframes => 1, param => 1, select => 1, spacer => 1,
7675                    table => 1, textarea => 1, wbr => 1,
7676                    noscript => 0, ## TODO: if scripting is enabled
7677                   }->{$token->{tag_name}}) {
7678            !!!cp ('t429');
7679            !!!parse-error (type => 'unmatched end tag',
7680                            text => $token->{tag_name}, token => $token);
7681            ## Ignore the token
7682            !!!next-token;
7683            next B;
7684            
7685            ## ISSUE: Issue on HTML5 new elements in spec
7686            
7687        } else {        } else {
7688          die "$0: $token->{type}: Unknown token";          ## Step 1
7689            my $node_i = -1;
7690            my $node = $self->{open_elements}->[$node_i];
7691    
7692            ## Step 2
7693            S2: {
7694              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7695                ## Step 1
7696                ## generate implied end tags
7697                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7698                  !!!cp ('t430');
7699                  ## NOTE: |<ruby><rt></ruby>|.
7700                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7701                  ## which seems wrong.
7702                  pop @{$self->{open_elements}};
7703                  $node_i++;
7704                }
7705            
7706                ## Step 2
7707                if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7708                        ne $token->{tag_name}) {
7709                  !!!cp ('t431');
7710                  ## NOTE: <x><y></x>
7711                  !!!parse-error (type => 'not closed',
7712                                  text => $self->{open_elements}->[-1]->[0]
7713                                      ->manakai_local_name,
7714                                  token => $token);
7715                } else {
7716                  !!!cp ('t432');
7717                }
7718                
7719                ## Step 3
7720                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7721    
7722                !!!next-token;
7723                last S2;
7724              } else {
7725                ## Step 3
7726                if (not ($node->[1] & FORMATTING_EL) and
7727                    #not $phrasing_category->{$node->[1]} and
7728                    ($node->[1] & SPECIAL_EL or
7729                     $node->[1] & SCOPING_EL)) {
7730                  !!!cp ('t433');
7731                  !!!parse-error (type => 'unmatched end tag',
7732                                  text => $token->{tag_name}, token => $token);
7733                  ## Ignore the token
7734                  !!!next-token;
7735                  last S2;
7736                }
7737    
7738                !!!cp ('t434');
7739              }
7740              
7741              ## Step 4
7742              $node_i--;
7743              $node = $self->{open_elements}->[$node_i];
7744              
7745              ## Step 5;
7746              redo S2;
7747            } # S2
7748            next B;
7749        }        }
7750      }      }
7751        next B;
7752      } continue { # B
7753        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7754          ## NOTE: The code below is executed in cases where it does not have
7755          ## to be, but it it is harmless even in those cases.
7756          ## has an element in scope
7757          INSCOPE: {
7758            for (reverse 0..$#{$self->{open_elements}}) {
7759              my $node = $self->{open_elements}->[$_];
7760              if ($node->[1] & FOREIGN_EL) {
7761                last INSCOPE;
7762              } elsif ($node->[1] & SCOPING_EL) {
7763                last;
7764              }
7765            }
7766            
7767            ## NOTE: No foreign element in scope.
7768            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7769          } # INSCOPE
7770        }
7771    } # B    } # B
7772    
7773    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 5088  sub _tree_construction_main ($) { Line 7775  sub _tree_construction_main ($) {
7775    ## TODO: script stuffs    ## TODO: script stuffs
7776  } # _tree_construct_main  } # _tree_construct_main
7777    
7778  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7779    my $class = shift;    my $class = shift;
7780    my $node = shift;    my $node = shift;
7781    my $s = \$_[0];    #my $s = \$_[0];
7782    my $onerror = $_[1];    my $onerror = $_[1];
7783      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7784    
7785      ## ISSUE: Should {confident} be true?
7786    
7787    my $nt = $node->node_type;    my $nt = $node->node_type;
7788    if ($nt == 9) {    if ($nt == 9) {
# Line 5109  sub set_inner_html ($$$) { Line 7799  sub set_inner_html ($$$) {
7799      }      }
7800    
7801      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7802      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7803    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7804      ## TODO: If non-html element      ## TODO: If non-html element
7805    
7806      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7807    
7808    ## TODO: Support for $get_wrapper
7809    
7810      ## Step 1 # MUST      ## Step 1 # MUST
7811      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7812      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5122  sub set_inner_html ($$$) { Line 7814  sub set_inner_html ($$$) {
7814      my $p = $class->new;      my $p = $class->new;
7815      $p->{document} = $doc;      $p->{document} = $doc;
7816    
7817      ## Step 9 # MUST      ## Step 8 # MUST
7818      my $i = 0;      my $i = 0;
7819      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
7820      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
7821      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
7822        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
7823        $input = $get_wrapper->($input);
7824        $p->{set_nc} = sub {
7825        my $self = shift;        my $self = shift;
7826    
7827        pop @{$self->{prev_input_character}};        my $char = '';
7828        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
7829            $char = $self->{next_nc};
7830            delete $self->{next_nc};
7831            $self->{nc} = ord $char;
7832          } else {
7833            $self->{char_buffer} = '';
7834            $self->{char_buffer_pos} = 0;
7835            
7836            my $count = $input->manakai_read_until
7837                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
7838                 $self->{char_buffer_pos});
7839            if ($count) {
7840              $self->{line_prev} = $self->{line};
7841              $self->{column_prev} = $self->{column};
7842              $self->{column}++;
7843              $self->{nc}
7844                  = ord substr ($self->{char_buffer},
7845                                $self->{char_buffer_pos}++, 1);
7846              return;
7847            }
7848            
7849            if ($input->read ($char, 1)) {
7850              $self->{nc} = ord $char;
7851            } else {
7852              $self->{nc} = -1;
7853              return;
7854            }
7855          }
7856    
7857        $self->{next_input_character} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7858        $self->{next_input_character} = ord substr $$s, $i++, 1;        $p->{column}++;
7859        $column++;  
7860          if ($self->{nc} == 0x000A) { # LF
7861        if ($self->{next_input_character} == 0x000A) { # LF          $p->{line}++;
7862          $line++;          $p->{column} = 0;
7863          $column = 0;          !!!cp ('i1');
7864        } elsif ($self->{next_input_character} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
7865          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
7866          $self->{next_input_character} = 0x000A; # LF # MUST          my $next = '';
7867          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
7868          $column = 0;            $self->{next_nc} = $next;
7869        } elsif ($self->{next_input_character} > 0x10FFFF) {          }
7870          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
7871        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $p->{line}++;
7872            $p->{column} = 0;
7873            !!!cp ('i2');
7874          } elsif ($self->{nc} == 0x0000) { # NULL
7875            !!!cp ('i4');
7876          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
7877          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
7878        }        }
7879      };      };
7880      $p->{prev_input_character} = [-1, -1, -1];  
7881      $p->{next_input_character} = -1;      $p->{read_until} = sub {
7882              #my ($scalar, $specials_range, $offset) = @_;
7883          return 0 if defined $p->{next_nc};
7884    
7885          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
7886          my $offset = $_[2] || 0;
7887          
7888          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
7889            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
7890            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
7891              substr ($_[0], $offset)
7892                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
7893              my $count = $+[0] - $-[0];
7894              if ($count) {
7895                $p->{column} += $count;
7896                $p->{char_buffer_pos} += $count;
7897                $p->{line_prev} = $p->{line};
7898                $p->{column_prev} = $p->{column} - 1;
7899                $p->{nc} = -1;
7900              }
7901              return $count;
7902            } else {
7903              return 0;
7904            }
7905          } else {
7906            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
7907            if ($count) {
7908              $p->{column} += $count;
7909              $p->{column_prev} += $count;
7910              $p->{nc} = -1;
7911            }
7912            return $count;
7913          }
7914        }; # $p->{read_until}
7915    
7916      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
7917        my (%opt) = @_;        my (%opt) = @_;
7918        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
7919          my $column = $opt{column};
7920          if (defined $opt{token} and defined $opt{token}->{line}) {
7921            $line = $opt{token}->{line};
7922            $column = $opt{token}->{column};
7923          }
7924          warn "Parse error ($opt{type}) at line $line column $column\n";
7925      };      };
7926      $p->{parse_error} = sub {      $p->{parse_error} = sub {
7927        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
7928      };      };
7929            
7930        my $char_onerror = sub {
7931          my (undef, $type, %opt) = @_;
7932          $ponerror->(layer => 'encode',
7933                      line => $p->{line}, column => $p->{column} + 1,
7934                      %opt, type => $type);
7935        }; # $char_onerror
7936        $input->onerror ($char_onerror);
7937    
7938      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
7939      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
7940    
7941      ## Step 2      ## Step 2
7942      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
7943      $p->{content_model_flag} = {      $p->{content_model} = {
7944        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
7945        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
7946        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
7947        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
7948        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
7949        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
7950        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
7951        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
7952        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
7953        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
7954      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
7955         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
7956            unless defined $p->{content_model};
7957            ## ISSUE: What is "the name of the element"? local name?
7958    
7959      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
7960          ## TODO: Foreign element OK?
7961    
7962      ## Step 4      ## Step 3
7963      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
7964        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
7965    
7966      ## Step 5 # MUST      ## Step 4 # MUST
7967      $doc->append_child ($root);      $doc->append_child ($root);
7968    
7969      ## Step 6 # MUST      ## Step 5 # MUST
7970      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
7971    
7972      undef $p->{head_element};      undef $p->{head_element};
7973    
7974      ## Step 7 # MUST      ## Step 6 # MUST
7975      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
7976    
7977      ## Step 8 # MUST      ## Step 7 # MUST
7978      my $anode = $node;      my $anode = $node;
7979      AN: while (defined $anode) {      AN: while (defined $anode) {
7980        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
7981          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
7982          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
7983            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
7984                !!!cp ('i5');
7985              $p->{form_element} = $anode;              $p->{form_element} = $anode;
7986              last AN;              last AN;
7987            }            }
# Line 5213  sub set_inner_html ($$$) { Line 7990  sub set_inner_html ($$$) {
7990        $anode = $anode->parent_node;        $anode = $anode->parent_node;
7991      } # AN      } # AN
7992            
7993      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
7994      {      {
7995        my $self = $p;        my $self = $p;
7996        !!!next-token;        !!!next-token;
7997      }      }
7998      $p->_tree_construction_main;      $p->_tree_construction_main;
7999    
8000      ## Step 11 # MUST      ## Step 10 # MUST
8001      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8002      for (@cn) {      for (@cn) {
8003        $node->remove_child ($_);        $node->remove_child ($_);
8004      }      }
8005      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8006    
8007      ## Step 12 # MUST      ## Step 11 # MUST
8008      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8009      for (@cn) {      for (@cn) {
8010        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5237  sub set_inner_html ($$$) { Line 8013  sub set_inner_html ($$$) {
8013      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8014    
8015      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8016    
8017        delete $p->{parse_error}; # delete loop
8018    } else {    } else {
8019      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";
8020    }    }
# Line 5244  sub set_inner_html ($$$) { Line 8022  sub set_inner_html ($$$) {
8022    
8023  } # tree construction stage  } # tree construction stage
8024    
8025  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8026    my (undef, $node, $on_error) = @_;  push our @ISA, 'Error';
   
   ## Step 1  
   my $s = '';  
   
   my $in_cdata;  
   my $parent = $node;  
   while (defined $parent) {  
     if ($parent->node_type == 1 and  
         $parent->namespace_uri eq 'http://www.w3.org/1999/xhtml' and  
         {  
           style => 1, script => 1, xmp => 1, iframe => 1,  
           noembed => 1, noframes => 1, noscript => 1,  
         }->{$parent->local_name}) { ## TODO: case thingy  
       $in_cdata = 1;  
     }  
     $parent = $parent->parent_node;  
   }  
   
   ## Step 2  
   my @node = @{$node->child_nodes};  
   C: while (@node) {  
     my $child = shift @node;  
     unless (ref $child) {  
       if ($child eq 'cdata-out') {  
         $in_cdata = 0;  
       } else {  
         $s .= $child; # end tag  
       }  
       next C;  
     }  
       
     my $nt = $child->node_type;  
     if ($nt == 1) { # Element  
       my $tag_name = lc $child->tag_name; ## ISSUE: Definition of "lowercase"  
       $s .= '<' . $tag_name;  
   
       ## ISSUE: Non-html elements  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = lc $attr->name; ## ISSUE: Definition of "lowercase"  
         $s .= ' ' . $attr_name . '="';  
         my $attr_value = $attr->value;  
         ## escape  
         $attr_value =~ s/&/&amp;/g;  
         $attr_value =~ s/</&lt;/g;  
         $attr_value =~ s/>/&gt;/g;  
         $attr_value =~ s/"/&quot;/g;  
         $s .= $attr_value . '"';  
       }  
       $s .= '>';  
         
       next C if {  
         area => 1, base => 1, basefont => 1, bgsound => 1,  
         br => 1, col => 1, embed => 1, frame => 1, hr => 1,  
         img => 1, input => 1, link => 1, meta => 1, param => 1,  
         spacer => 1, wbr => 1,  
       }->{$tag_name};  
   
       $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,  
       }->{$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  
8027    
8028  1;  1;
8029  # $Date$  # $Date$

Legend:
Removed from v.1.25  
changed lines
  Added in v.1.191

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24