/[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.43 by wakaba, Sat Jul 21 07:21:44 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  ## ISSUE: HTML5 revision 967 says that the encoding layer MUST NOT  require IO::Handle;
23  ## strip BOM and the HTML layer MUST ignore it.  Whether we can do it  
24  ## is not yet clear.  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25  ## "{U+FEFF}..." in UTF-16BE/UTF-16LE is three or four characters?  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  ## "{U+FEFF}..." in GB18030?  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28  my $permitted_slash_tag_name = {  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    base => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    link => 1,  
31    meta => 1,  sub A_EL () { 0b1 }
32    hr => 1,  sub ADDRESS_EL () { 0b10 }
33    br => 1,  sub BODY_EL () { 0b100 }
34    img=> 1,  sub BUTTON_EL () { 0b1000 }
35    embed => 1,  sub CAPTION_EL () { 0b10000 }
36    param => 1,  sub DD_EL () { 0b100000 }
37    area => 1,  sub DIV_EL () { 0b1000000 }
38    col => 1,  sub DT_EL () { 0b10000000 }
39    input => 1,  sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of END_TAG_OPTIONAL_EL
70    ## is used in "generate implied end tags" implementation (search for the
71    ## function mae).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      P_EL |
77      RUBY_COMPONENT_EL
78    }
79    
80    ## NOTE: Used in </body> and EOF algorithms.
81    sub ALL_END_TAG_OPTIONAL_EL () {
82      DD_EL |
83      DT_EL |
84      LI_EL |
85      P_EL |
86    
87      BODY_EL |
88      HTML_EL |
89      TABLE_CELL_EL |
90      TABLE_ROW_EL |
91      TABLE_ROW_GROUP_EL
92    }
93    
94    sub SCOPING_EL () {
95      BUTTON_EL |
96      CAPTION_EL |
97      HTML_EL |
98      TABLE_EL |
99      TABLE_CELL_EL |
100      MISC_SCOPING_EL
101    }
102    
103    sub TABLE_SCOPING_EL () {
104      HTML_EL |
105      TABLE_EL
106    }
107    
108    sub TABLE_ROWS_SCOPING_EL () {
109      HTML_EL |
110      TABLE_ROW_GROUP_EL
111    }
112    
113    sub TABLE_ROW_SCOPING_EL () {
114      HTML_EL |
115      TABLE_ROW_EL
116    }
117    
118    sub SPECIAL_EL () {
119      ADDRESS_EL |
120      BODY_EL |
121      DIV_EL |
122    
123      DD_EL |
124      DT_EL |
125      LI_EL |
126      P_EL |
127    
128      FORM_EL |
129      FRAMESET_EL |
130      HEADING_EL |
131      OPTION_EL |
132      OPTGROUP_EL |
133      SELECT_EL |
134      TABLE_ROW_EL |
135      TABLE_ROW_GROUP_EL |
136      MISC_SPECIAL_EL
137    }
138    
139    my $el_category = {
140      a => A_EL | FORMATTING_EL,
141      address => ADDRESS_EL,
142      applet => MISC_SCOPING_EL,
143      area => MISC_SPECIAL_EL,
144      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 60  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
   s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,  
 };  
 # $phrasing_category: all other elements  
388    
389  sub parse_string ($$$;$) {    my $get_wrapper = $_[3] || sub ($) {
390    my $self = shift->new;      return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
391    my $s = \$_[0];    };
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_char_string ($$$;$$) {
642      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
643      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    
# Line 159  sub CDATA_CONTENT_MODEL () { CM_LIMITED_ Line 826  sub CDATA_CONTENT_MODEL () { CM_LIMITED_
826  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
827  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  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->{s_kwd}; # state keyword - initialized when used
936      #$self->{entity__value}; # initialized when used
937      #$self->{entity__match}; # initialized when used
938    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
939    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
940    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
941    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
942    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
943    $self->{char} = [];    delete $self->{self_closing};
944    # $self->{next_input_character}    $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 194  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} & CM_ENTITY) { # PCDATA | RCDATA  
1010            $self->{state} = 'entity data';        if ($self->{nc} == 0x0026) { # &
1011            !!!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} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1069            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1070              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1071                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1072                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1073                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1074              }              $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 (length $self->{s_kwd}) {
1089              !!!cp (5.1);
1090              $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          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1100              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($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} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1114            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1115                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
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 (0);                                  length $token->{data})) {
1145            $self->{s_kwd} = '';
1146        $self->{state} = 'data';        }
1147        # next-input-character is already done  
1148          ## Stay in the data state.
1149        unless (defined $token) {        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1150          !!!emit ({type => 'character', data => '&'});          !!!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} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1161          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1162              !!!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    
1175            ## reconsume
1176            $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        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1183          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1184            $self->{state} = 'markup declaration open';            !!!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} in tag open";          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} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1260          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_input_character};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = 'data';  
1261    
1262                !!!emit ({type => 'character', data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1263            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1264                redo A;          if (defined $self->{last_stag_name}) {
1265              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1266            }            $self->{s_kwd} = '';
1267            push @next_char, $self->{next_input_character};            ## Reconsume.
1268                    redo A;
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = 'data';  
             !!!emit ({type => 'character', data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
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) {
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1398            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1399            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1400              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1401            }            #  !!! cp (36);
1402              #  !!! 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->{current_token}->{first_start_tag}            !!!cp (39);
1427                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1429            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1430            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1431              !!!parse-error (type => 'end tag attribute');            #  ## 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->{current_token}->{first_start_tag}            !!!cp (46);
1468                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1469            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1470            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1471            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}            !!!cp (52);
1505                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1506            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1507            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1508            if ($self->{current_token}->{attributes}) {            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 => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!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          } else {            ## Discard $self->{ca} # MUST
1549            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1550              = $self->{current_attribute};            !!!cp (58);
1551              $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->{current_token}->{first_start_tag}            !!!cp (61);
1572                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1573            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1574          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (62);
1575            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1576            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}            !!!cp (66);
1606                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1607            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1608            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1609            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}            !!!cp (73);
1652                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1653            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1654            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1655            if ($self->{current_token}->{attributes}) {            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');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $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->{current_token}->{first_start_tag}            !!!cp (79);
1690                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1691            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1692            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1693            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1749                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1750            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1751          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1752            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1753            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}            !!!cp (90);
1773                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1774            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1775            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1776            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}            !!!cp (97);
1825                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1826            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1827            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1828            if ($self->{current_token}->{attributes}) {            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          !!!next-input-character;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1859          redo A;          !!!next-input-character;
1860        } elsif ($self->{next_input_character} == 0x0026) { # &          redo A;
1861          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';        } elsif ($self->{nc} == 0x0026) { # &
1862          $self->{state} = 'entity in attribute value';          !!!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} == -1) {        } elsif ($self->{nc} == -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->{current_token}->{first_start_tag}            !!!cp (103);
1876                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1877            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1878            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1879            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}          redo A;
1923                = not defined $self->{last_emitted_start_tag_name};        } elsif ($self->{nc} == 0x003E) { # >
1924            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1925          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!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            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1929            if ($self->{current_token}->{attributes}) {            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->{current_token}->{first_start_tag}            !!!cp (112);
1949                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1950            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1951            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1952            if ($self->{current_token}->{attributes}) {            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 (1);        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');        !!!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          ## ASCII case-insensitive
2555          if ($self->{nc} == [
2556                undef,
2557                0x0055, # U
2558                0x0042, # B
2559                0x004C, # L
2560                0x0049, # I
2561              ]->[length $self->{s_kwd}] or
2562              $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        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2589        $self->{state} = 'bogus DOCTYPE';          ## Reconsume.
2590        # next-input-character is already done          redo A;
2591        redo A;        }
2592      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == SYSTEM_STATE) {
2593        if ({        ## ASCII case-insensitive
2594              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,        if ($self->{nc} == [
2595              #0x000D => 1, # HT, LF, VT, FF, SP, CR              undef,
2596            }->{$self->{next_input_character}}) {              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          ## reconsume          ## 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          ## reconsume          ## 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            !!!cp (206);
2845          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2846          $self->{state} = 'bogus DOCTYPE';          $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;          !!!next-input-character;
2857          redo A;          redo A;
2858        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2859            !!!cp (208);
2860          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2861    
2862          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2863            !!!next-input-character;
2864    
2865            $self->{ct}->{quirks} = 1;
2866            !!!emit ($self->{ct}); # DOCTYPE
2867    
2868            redo A;
2869          } elsif ($self->{nc} == -1) {
2870            !!!cp (209);
2871            !!!parse-error (type => 'unclosed SYSTEM literal');
2872    
2873            $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;          !!!next-input-character;
2896          redo A;          redo A;
2897        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2898            !!!cp (212);
2899          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2900    
2901          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2902            !!!next-input-character;
2903    
2904            $self->{ct}->{quirks} = 1;
2905            !!!emit ($self->{ct}); # DOCTYPE
2906    
2907            redo A;
2908          } elsif ($self->{nc} == -1) {
2909            !!!cp (213);
2910            !!!parse-error (type => 'unclosed SYSTEM literal');
2911    
2912            $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;
         $self->{state} = 'data';  
2948          ## reconsume          ## reconsume
2949    
2950          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2951          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # 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, $in_attr) = @_;          !!!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 $code;          ## 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            $code ||= 0;          !!!next-input-character;
3041            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3042            $code += $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  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
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          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3050            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3051            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3052          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
3053            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!next-input-character;
3054            $code = 0xFFFD;          redo A;
3055          } elsif ($code == 0x000D) {        } else {
3056            !!!parse-error (type => 'CR character reference');          !!!cp (221.11);
3057            $code = 0x000A;          $self->{ct}->{data} .= ']]'; # character
3058          } elsif (0x80 <= $code and $code <= 0x9F) {          $self->{state} = CDATA_SECTION_STATE;
3059            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          ## Reconsume.
3060            $code = $c1_entity_char->{$code};          redo A;
3061          }        }
3062        } elsif ($self->{state} == ENTITY_STATE) {
3063          return {type => 'character', data => chr $code};        if ($is_space->{$self->{nc}} or
3064        } # X            {
3065      } elsif (0x0030 <= $self->{next_input_character} and              0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3066               $self->{next_input_character} <= 0x0039) { # 0..9              $self->{entity_add} => 1,
3067        my $code = $self->{next_input_character} - 0x0030;            }->{$self->{nc}}) {
3068        !!!next-input-character;          !!!cp (1001);
3069                  ## Don't consume
3070        while (0x0030 <= $self->{next_input_character} and          ## No error
3071                  $self->{next_input_character} <= 0x0039) { # 0..9          ## Return nothing.
3072          $code *= 10;          #
3073          $code += $self->{next_input_character} - 0x0030;        } 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        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3184          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3185          my $c = $self->{column_prev};
3186          if ($charref_map->{$code}) {
3187            !!!cp (1015);
3188            !!!parse-error (type => 'invalid character reference',
3189                            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;          $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          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3255            # 0..9
3256            !!!cp (1002);
3257            $self->{s_kwd} *= 0x10;
3258            $self->{s_kwd} += $self->{nc} - 0x0030;
3259            ## Stay in the state.
3260            !!!next-input-character;
3261            redo A;
3262          } elsif (0x0061 <= $self->{nc} and
3263                   $self->{nc} <= 0x0066) { # a..f
3264            !!!cp (1003);
3265            $self->{s_kwd} *= 0x10;
3266            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3267            ## Stay in the state.
3268            !!!next-input-character;
3269            redo A;
3270          } elsif (0x0041 <= $self->{nc} and
3271                   $self->{nc} <= 0x0046) { # A..F
3272            !!!cp (1004);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif ($self->{nc} == 0x003B) { # ;
3279            !!!cp (1006);
3280            !!!next-input-character;
3281            #
3282          } else {
3283            !!!cp (1007);
3284            !!!parse-error (type => 'no refc',
3285                            line => $self->{line},
3286                            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) {        } elsif ($code > 0x10FFFF) {
3301          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3302            !!!parse-error (type => 'invalid character reference',
3303                            text => (sprintf 'U-%08X', $code),
3304                            line => $l, column => $c);
3305          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3306        }        }
3307          
3308        return {type => 'character', data => chr $code};        if ($self->{prev_state} == DATA_STATE) {
3309      } else {          !!!cp (988);
3310        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3311        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3312        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3313        return undef;                    line => $l, column => $c,
3314      }                   });
3315    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3316              $self->{next_input_character} <= 0x005A) or        } else {
3317             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3318              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3319      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3320      !!!next-input-character;          $self->{state} = $self->{prev_state};
3321            ## Reconsume.
3322      my $value = $entity_name;          redo A;
3323      my $match = 0;        }
3324      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3325      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3326              ## NOTE: Some number greater than the maximum length of entity name
3327      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3328             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3329             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3330               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3331              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3332               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3333              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3334               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3335              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3336        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3337        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3338          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3339            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3340            $match = 1;              $self->{entity__match} = 1;
3341            !!!next-input-character;              !!!next-input-character;
3342            last;              #
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 {          } else {
3352            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3353            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3354              $self->{entity__match} *= 2;
3355              ## Stay in the state.
3356            !!!next-input-character;            !!!next-input-character;
3357              redo A;
3358          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3359        }        }
3360      }  
3361              my $data;
3362      if ($match > 0) {        my $has_ref;
3363        return {type => 'character', data => $value};        if ($self->{entity__match} > 0) {
3364      } elsif ($match < 0) {          !!!cp (1023);
3365        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3366        if ($in_attr and $match < -1) {          $has_ref = 1;
3367          return {type => 'character', data => '&'.$entity_name};          #
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 {
3376              !!!cp (1025);
3377              $data = $self->{entity__value};
3378              $has_ref = 1;
3379              #
3380            }
3381        } else {        } else {
3382          return {type => 'character', data => $value};          !!!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        }        }
3418      } else {      } else {
3419        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       return {type => 'character', data => '&'.$value};  
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 1780  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 1806  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]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3704            ## Ignore the token.            ## Ignore the token.
3705    
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        ## reprocess      $self->{document}->append_child ($root_element);
3763        #redo B;      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3764        return; ## Go to the main phase.  
3765        $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 2043  sub _reset_insertion_mode ($) { Line 3786  sub _reset_insertion_mode ($) {
3786            
3787      ## Step 3      ## Step 3
3788      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
3789        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3790          $last = 1;          $last = 1;
3791          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3792            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3793                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3794              #          } else {
3795            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3796          }          }
3797        }        }
3798              
3799        ## Step 4..13        ## Step 4..14
3800        my $new_mode = {        my $new_mode;
3801                        select => 'in select',        if ($node->[1] & FOREIGN_EL) {
3802                        td => 'in cell',          !!!cp ('t28.1');
3803                        th => 'in cell',          ## NOTE: Strictly spaking, the line below only applies to MathML and
3804                        tr => 'in row',          ## SVG elements.  Currently the HTML syntax supports only MathML and
3805                        tbody => 'in table body',          ## SVG elements as foreigners.
3806                        thead => 'in table head',          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3807                        tfoot => 'in table foot',        } elsif ($node->[1] & TABLE_CELL_EL) {
3808                        caption => 'in caption',          if ($last) {
3809                        colgroup => 'in column group',            !!!cp ('t28.2');
3810                        table => 'in table',            #
3811                        head => 'in body', # not in head!          } else {
3812                        body => 'in body',            !!!cp ('t28.3');
3813                        frameset => 'in frameset',            $new_mode = IN_CELL_IM;
3814                       }->{$node->[1]};          }
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          }
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 $previous_insertion_mode;  
   
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 2121  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 2135  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 2169  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} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
# Line 2204  sub _tree_construction_main ($) { Line 3977  sub _tree_construction_main ($) {
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      }      }
# Line 2220  sub _tree_construction_main ($) { Line 3996  sub _tree_construction_main ($) {
3996      $self->{content_model} = PCDATA_CONTENT_MODEL;      $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
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4003      } else {      } else {
4004        die "$0: $content_model_flag in parse_rcdata";        ## 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} = CDATA_CONTENT_MODEL;      $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} = PCDATA_CONTENT_MODEL;      $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 2278  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 2307  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 2337  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 2362  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 2384  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 2397  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 2419  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 2436  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 2448  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 2461  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 2491  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_CONTENT_MODEL, $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,          !!!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 'meta') {          !!!cp ('t81');
4365          ## NOTE: This is an "as if in head" code clone, only "-t" differs        }
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
   
         unless ($self->{confident}) {  
           my $charset;  
           if ($token->{attributes}->{charset}) { ## TODO: And if supported  
             $charset = $token->{attributes}->{charset}->{value};  
           }  
           if ($token->{attributes}->{'http-equiv'}) {  
             ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.  
             if ($token->{attributes}->{'http-equiv'}->{value}  
                 =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=  
                     [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|  
                     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {  
               $charset = defined $1 ? $1 : defined $2 ? $2 : $3;  
             } ## TODO: And if supported  
           }  
           ## TODO: Change the encoding  
         }  
4366    
4367          !!!next-token;        !!!cp ('t82');
4368          return;        !!!parse-error (type => 'not first start tag', token => $token);
4369        } elsif ($token->{tag_name} eq 'title') {        my $top_el = $self->{open_elements}->[0]->[0];
4370          !!!parse-error (type => 'in body:title');        for my $attr_name (keys %{$token->{attributes}}) {
4371          ## NOTE: This is an "as if in head" code clone          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4372          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {            !!!cp ('t84');
4373            if (defined $self->{head_element}) {            $top_el->set_attribute_ns
4374              $self->{head_element}->append_child ($_[0]);              (undef, [undef, $attr_name],
4375            } else {               $token->{attributes}->{$attr_name}->{value});
             $insert->($_[0]);  
           }  
         });  
         return;  
       } elsif ($token->{tag_name} eq 'body') {  
         !!!parse-error (type => 'in body:body');  
                 
         if (@{$self->{open_elements}} == 1 or  
             $self->{open_elements}->[1]->[1] ne 'body') {  
           ## Ignore the token  
         } else {  
           my $body_el = $self->{open_elements}->[1]->[0];  
           for my $attr_name (keys %{$token->{attributes}}) {  
             unless ($body_el->has_attribute_ns (undef, $attr_name)) {  
               $body_el->set_attribute_ns  
                 (undef, [undef, $attr_name],  
                  $token->{attributes}->{$attr_name}->{value});  
             }  
           }  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, p => 1, ul => 1,  
                 pre => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         if ($token->{tag_name} eq 'pre') {  
           !!!next-token;  
           if ($token->{type} eq 'character') {  
             $token->{data} =~ s/^\x0A//;  
             unless (length $token->{data}) {  
               !!!next-token;  
             }  
           }  
         } else {  
           !!!next-token;  
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           ## has a p element in scope  
           INSCOPE: for (reverse @{$self->{open_elements}}) {  
             if ($_->[1] eq 'p') {  
               !!!back-token;  
               $token = {type => 'end tag', tag_name => 'p'};  
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
           !!!next-token;  
           return;  
4376          }          }
4377        } elsif ($token->{tag_name} eq 'li') {        }
4378          ## has a p element in scope        !!!nack ('t84.1');
4379          INSCOPE: for (reverse @{$self->{open_elements}}) {        !!!next-token;
4380            if ($_->[1] eq 'p') {        next B;
4381              !!!back-token;      } elsif ($token->{type} == COMMENT_TOKEN) {
4382              $token = {type => 'end tag', tag_name => 'p'};        my $comment = $self->{document}->create_comment ($token->{data});
4383              return;        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4384            } elsif ({          !!!cp ('t85');
4385                      table => 1, caption => 1, td => 1, th => 1,          $self->{document}->append_child ($comment);
4386                      button => 1, marquee => 1, object => 1, html => 1,        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4387                     }->{$_->[1]}) {          !!!cp ('t86');
4388              last INSCOPE;          $self->{open_elements}->[0]->[0]->append_child ($comment);
4389            }        } else {
4390          } # INSCOPE          !!!cp ('t87');
4391                      $self->{open_elements}->[-1]->[0]->append_child ($comment);
4392          ## Step 1        }
4393          my $i = -1;        !!!next-token;
4394          my $node = $self->{open_elements}->[$i];        next B;
4395          LI: {      } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4396            ## Step 2        if ($token->{type} == CHARACTER_TOKEN) {
4397            if ($node->[1] eq 'li') {          !!!cp ('t87.1');
4398              if ($i != -1) {          $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model} = PLAINTEXT_CONTENT_MODEL;  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
4399          !!!next-token;          !!!next-token;
4400          return;          next B;
4401        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4402          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4403            my $node = $active_formatting_elements->[$i];               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4404            if ($node->[1] eq 'a') {              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4405              !!!parse-error (type => 'in a:a');              ($token->{tag_name} eq 'svg' and
4406                             $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4407              !!!back-token;            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4408              $token = {type => 'end tag', tag_name => 'a'};            !!!cp ('t87.2');
4409              $formatting_end_tag->($token->{tag_name});            #
4410                        } elsif ({
4411              AFE2: for (reverse 0..$#$active_formatting_elements) {                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4412                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4413                  splice @$active_formatting_elements, $_, 1;                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4414                  last AFE2;                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4415                }                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4416              } # AFE2                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4417              OE: for (reverse 0..$#{$self->{open_elements}}) {                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4418                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4419                  splice @{$self->{open_elements}}, $_, 1;                   }->{$token->{tag_name}}) {
4420                  last OE;            !!!cp ('t87.2');
4421                }            !!!parse-error (type => 'not closed',
4422              } # OE                            text => $self->{open_elements}->[-1]->[0]
4423              last AFE;                                ->manakai_local_name,
4424            } elsif ($node->[0] eq '#marker') {                            token => $token);
4425              last AFE;  
4426              pop @{$self->{open_elements}}
4427                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4428    
4429              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4430              ## Reprocess.
4431              next B;
4432            } else {
4433              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4434              my $tag_name = $token->{tag_name};
4435              if ($nsuri eq $SVG_NS) {
4436                $tag_name = {
4437                   altglyph => 'altGlyph',
4438                   altglyphdef => 'altGlyphDef',
4439                   altglyphitem => 'altGlyphItem',
4440                   animatecolor => 'animateColor',
4441                   animatemotion => 'animateMotion',
4442                   animatetransform => 'animateTransform',
4443                   clippath => 'clipPath',
4444                   feblend => 'feBlend',
4445                   fecolormatrix => 'feColorMatrix',
4446                   fecomponenttransfer => 'feComponentTransfer',
4447                   fecomposite => 'feComposite',
4448                   feconvolvematrix => 'feConvolveMatrix',
4449                   fediffuselighting => 'feDiffuseLighting',
4450                   fedisplacementmap => 'feDisplacementMap',
4451                   fedistantlight => 'feDistantLight',
4452                   feflood => 'feFlood',
4453                   fefunca => 'feFuncA',
4454                   fefuncb => 'feFuncB',
4455                   fefuncg => 'feFuncG',
4456                   fefuncr => 'feFuncR',
4457                   fegaussianblur => 'feGaussianBlur',
4458                   feimage => 'feImage',
4459                   femerge => 'feMerge',
4460                   femergenode => 'feMergeNode',
4461                   femorphology => 'feMorphology',
4462                   feoffset => 'feOffset',
4463                   fepointlight => 'fePointLight',
4464                   fespecularlighting => 'feSpecularLighting',
4465                   fespotlight => 'feSpotLight',
4466                   fetile => 'feTile',
4467                   feturbulence => 'feTurbulence',
4468                   foreignobject => 'foreignObject',
4469                   glyphref => 'glyphRef',
4470                   lineargradient => 'linearGradient',
4471                   radialgradient => 'radialGradient',
4472                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4473                   textpath => 'textPath',  
4474                }->{$tag_name} || $tag_name;
4475            }            }
         } # 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') {  
             !!!parse-error (type => 'not closed:nobr');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'nobr'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
   
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'marquee' or  
                $token->{tag_name} eq 'object') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = 'in table';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,  
                 image => 1,  
                }->{$token->{tag_name}}) {  
         if ($token->{tag_name} eq 'image') {  
           !!!parse-error (type => 'image');  
           $token->{tag_name} = 'img';  
         }  
4482    
4483          ## NOTE: There is an "as if <br>" code clone.            if ($self->{self_closing}) {
4484          $reconstruct_active_formatting_elements->($insert_to_current);              pop @{$self->{open_elements}};
4485                        !!!ack ('t87.3');
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           my $form_attrs;  
           $form_attrs->{action} = $at->{action} if $at->{action};  
           my $prompt_attr = $at->{prompt};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           delete $at->{action};  
           delete $at->{prompt};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form',  
                          attributes => $form_attrs},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                        );  
           if ($prompt_attr) {  
             push @tokens, {type => 'character', data => $prompt_attr->{value}};  
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  
4488            }            }
4489            push @tokens,  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'};  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'textarea') {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         ## TODO: $self->{form_element} if defined  
         $self->{content_model} = RCDATA_CONTENT_MODEL;  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         if ($token->{type} eq 'character') {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!next-token;  
           }  
         }  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
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} = PCDATA_CONTENT_MODEL;        } 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_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = 'in select';  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'in body:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
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,  
                      tbody => 1, tfoot => 1, thead => 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;              #
           return;  
         }  
       } elsif ($token->{tag_name} eq 'html') {  
         if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {  
           ## ISSUE: There is an issue in the spec.  
           if ($self->{open_elements}->[-1]->[1] ne 'body') {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);  
           }  
           $self->{insertion_mode} = 'after body';  
           ## reprocess  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           if (defined $i) {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4522            } else {            } else {
4523              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.1');
4524            }              ## Ignore the token.
4525          }              #
           
         if (defined $i) {  
           splice @{$self->{open_elements}}, $i;  
         } elsif ($token->{tag_name} eq 'p') {  
           ## As if <p>, then reprocess the current token  
           my $el;  
           !!!create-element ($el, 'p');  
           $insert->($el);  
         }  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         ## has an element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4526            }            }
4527          } # INSCOPE            unless (length $token->{data}) {
4528                        !!!cp ('t88');
4529          if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {              !!!next-token;
4530            pop @{$self->{open_elements}};              next B;
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         undef $self->{form_element};  
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
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          }          }
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ($token->{tag_name} eq 'br') {  
         !!!parse-error (type => 'unmatched end tag:br');  
4534    
4535          ## As if <br>          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4536          $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t89');
4537                      ## As if <head>
4538          my $el;            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4539          !!!create-element ($el, 'br');            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4540          $insert->($el);            push @{$self->{open_elements}},
4541                          [$self->{head_element}, $el_category->{head}];
         ## Ignore the token.  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
4542    
4543          ## Step 2            ## Reprocess in the "in head" insertion mode...
4544          S2: {            pop @{$self->{open_elements}};
           if ($node->[1] eq $token->{tag_name}) {  
             ## Step 1  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
           
             ## Step 2  
             if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
             }  
               
             ## Step 3  
             splice @{$self->{open_elements}}, $node_i;  
4545    
4546              !!!next-token;            ## Reprocess in the "after head" insertion mode...
4547              last S2;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4548            } else {            !!!cp ('t90');
4549              ## Step 3            ## As if </noscript>
4550              if (not $formatting_category->{$node->[1]} and            pop @{$self->{open_elements}};
4551                  #not $phrasing_category->{$node->[1]} and            !!!parse-error (type => 'in noscript:#text', token => $token);
                 ($special_category->{$node->[1]} or  
                  $scoping_category->{$node->[1]})) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
           }  
             
           ## Step 4  
           $node_i--;  
           $node = $self->{open_elements}->[$node_i];  
4552                        
4553            ## Step 5;            ## Reprocess in the "in head" insertion mode...
4554            redo S2;            ## As if </head>
4555          } # S2            pop @{$self->{open_elements}};
         return;  
       }  
     }  
   }; # $in_body  
   
   B: {  
     if ($token->{type} eq 'DOCTYPE') {  
       !!!parse-error (type => 'DOCTYPE in the middle');  
       ## Ignore the token  
       ## Stay in the phase  
       !!!next-token;  
       redo B;  
     } elsif ($token->{type} eq 'end-of-file') {  
       if ($token->{insertion_mode} ne 'trailing end') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
              tbody => 1, tfoot=> 1, thead => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
4556    
4557        ## Stop parsing            ## Reprocess in the "after head" insertion mode...
4558        last B;          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4559      } elsif ($token->{type} eq 'start tag' and            !!!cp ('t91');
4560               $token->{tag_name} eq 'html') {            pop @{$self->{open_elements}};
       if ($self->{insertion_mode} eq 'trailing end') {  
         ## Turn into the main phase  
         !!!parse-error (type => 'after html:html');  
         $self->{insertion_mode} = $previous_insertion_mode;  
       }  
4561    
4562  ## ISSUE: "aa<html>" is not a parse error.            ## Reprocess in the "after head" insertion mode...
4563  ## ISSUE: "<html>" in fragment is not a parse error.          } else {
4564        unless ($token->{first_start_tag}) {            !!!cp ('t92');
         !!!parse-error (type => 'not first start tag');  
       }  
       my $top_el = $self->{open_elements}->[0]->[0];  
       for my $attr_name (keys %{$token->{attributes}}) {  
         unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
           $top_el->set_attribute_ns  
             (undef, [undef, $attr_name],  
              $token->{attributes}->{$attr_name}->{value});  
4565          }          }
4566        }  
4567        !!!next-token;          ## "after head" insertion mode
4568        redo B;          ## As if <body>
4569      } elsif ($token->{type} eq 'comment') {          !!!insert-element ('body',, $token);
4570        my $comment = $self->{document}->create_comment ($token->{data});          $self->{insertion_mode} = IN_BODY_IM;
4571        if ($self->{insertion_mode} eq 'trailing end') {          ## reprocess
4572          $self->{document}->append_child ($comment);          next B;
4573        } elsif ($self->{insertion_mode} eq 'after body') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4574          $self->{open_elements}->[0]->[0]->append_child ($comment);          if ($token->{tag_name} eq 'head') {
4575        } else {            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4576          $self->{open_elements}->[-1]->[0]->append_child ($comment);              !!!cp ('t93');
4577        }              !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4578        !!!next-token;              $self->{open_elements}->[-1]->[0]->append_child
4579        redo B;                  ($self->{head_element});
4580      } elsif ($self->{insertion_mode} eq 'before head') {              push @{$self->{open_elements}},
4581            if ($token->{type} eq 'character') {                  [$self->{head_element}, $el_category->{head}];
4582              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {              $self->{insertion_mode} = IN_HEAD_IM;
4583                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              !!!nack ('t93.1');
4584                unless (length $token->{data}) {              !!!next-token;
4585                  !!!next-token;              next B;
4586                  redo B;            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4587                }              !!!cp ('t93.2');
4588              }              !!!parse-error (type => 'after head', text => 'head',
4589              ## As if <head>                              token => $token);
4590              !!!create-element ($self->{head_element}, 'head');              ## Ignore the token
4591              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});              !!!nack ('t93.3');
4592              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!next-token;
4593              $self->{insertion_mode} = 'in head';              next B;
             ## reprocess  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};  
             !!!create-element ($self->{head_element}, 'head', $attr);  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             if ($token->{tag_name} eq 'head') {  
               !!!next-token;  
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ({  
                  head => 1, body => 1, html => 1,  
                  p => 1, br => 1,  
                 }->{$token->{tag_name}}) {  
               ## As if <head>  
               !!!create-element ($self->{head_element}, 'head');  
               $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
               push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token ## ISSUE: An issue in the spec.  
               !!!next-token;  
               redo B;  
             }  
4594            } else {            } else {
4595              die "$0: $token->{type}: Unknown type";              !!!cp ('t95');
4596                !!!parse-error (type => 'in head:head',
4597                                token => $token); # or in head noscript
4598                ## Ignore the token
4599                !!!nack ('t95.1');
4600                !!!next-token;
4601                next B;
4602            }            }
4603          } elsif ($self->{insertion_mode} eq 'in head' or          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4604                   $self->{insertion_mode} eq 'in head noscript' or            !!!cp ('t96');
4605                   $self->{insertion_mode} eq 'after head') {            ## As if <head>
4606            if ($token->{type} eq 'character') {            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4607              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4608                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            push @{$self->{open_elements}},
4609                unless (length $token->{data}) {                [$self->{head_element}, $el_category->{head}];
4610                  !!!next-token;  
4611                  redo B;            $self->{insertion_mode} = IN_HEAD_IM;
4612              ## Reprocess in the "in head" insertion mode...
4613            } else {
4614              !!!cp ('t97');
4615            }
4616    
4617                if ($token->{tag_name} eq 'base') {
4618                  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              }  
               
             #  
           } elsif ($token->{type} eq 'start tag') {  
             if ({base => ($self->{insertion_mode} eq 'in head' or  
                           $self->{insertion_mode} eq 'after head'),  
                  link => 1}->{$token->{tag_name}}) {  
4631                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4632                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4633                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t100');
4634                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4635                                    text => $token->{tag_name}, token => $token);
4636                    push @{$self->{open_elements}},
4637                        [$self->{head_element}, $el_category->{head}];
4638                  } else {
4639                    !!!cp ('t101');
4640                }                }
4641                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4642                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4643                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4644                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4645                  !!!nack ('t101.1');
4646                !!!next-token;                !!!next-token;
4647                redo B;                next B;
4648              } elsif ($token->{tag_name} eq 'meta') {              } elsif ($token->{tag_name} eq 'link') {
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}} # <head>
4662                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4663                  !!!ack ('t103.1');
4664                  !!!next-token;
4665                  next B;
4666                } elsif ($token->{tag_name} eq 'meta') {
4667                  ## NOTE: There is a "as if in head" code clone.
4668                  if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4669                    !!!cp ('t104');
4670                    !!!parse-error (type => 'after head',
4671                                    text => $token->{tag_name}, token => $token);
4672                    push @{$self->{open_elements}},
4673                        [$self->{head_element}, $el_category->{head}];
4674                  } else {
4675                    !!!cp ('t105');
4676                  }
4677                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4678                  my $meta_el = pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.
4679    
4680                unless ($self->{confident}) {                unless ($self->{confident}) {
4681                  my $charset;                  if ($token->{attributes}->{charset}) {
4682                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                    !!!cp ('t106');
4683                    $charset = $token->{attributes}->{charset}->{value};                    ## 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                  if ($token->{attributes}->{'http-equiv'}) {                } else {
4715                    ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.                  if ($token->{attributes}->{charset}) {
4716                    if ($token->{attributes}->{'http-equiv'}->{value}                    !!!cp ('t109');
4717                        =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4718                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                        ->set_user_data (manakai_has_reference =>
4719                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                                             $token->{attributes}->{charset}
4720                      $charset = defined $1 ? $1 : defined $2 ? $2 : $3;                                                 ->{has_reference});
4721                    } ## TODO: And if supported                  }
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                  }                  }
                 ## TODO: Change the encoding  
4729                }                }
4730    
4731                ## TODO: Extracting |charset| from |meta|.                pop @{$self->{open_elements}} # <head>
4732                pop @{$self->{open_elements}}                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4733                    if $self->{insertion_mode} eq 'after head';                !!!ack ('t110.1');
4734                !!!next-token;                !!!next-token;
4735                redo B;                next B;
4736              } elsif ($token->{tag_name} eq 'title' and              } elsif ($token->{tag_name} eq 'title') {
4737                       $self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4738                ## NOTE: There is a "as if in head" code clone.                  !!!cp ('t111');
4739                if ($self->{insertion_mode} eq 'after head') {                  ## As if </noscript>
4740                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  pop @{$self->{open_elements}};
4741                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!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}                my $parent = defined $self->{head_element} ? $self->{head_element}
4758                    : $self->{open_elements}->[-1]->[0];                    : $self->{open_elements}->[-1]->[0];
4759                $parse_rcdata->(RCDATA_CONTENT_MODEL,                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4760                                sub { $parent->append_child ($_[0]) });                pop @{$self->{open_elements}} # <head>
4761                pop @{$self->{open_elements}}                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4762                    if $self->{insertion_mode} eq 'after head';                next B;
4763                redo B;              } elsif ($token->{tag_name} eq 'style' or
4764              } elsif ($token->{tag_name} eq 'style') {                       $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_CONTENT_MODEL, $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 => 'in noscript:noscript');                  !!!cp ('t117');
4792                    !!!parse-error (type => 'in noscript', text => 'noscript',
4793                                    token => $token);
4794                  ## Ignore the token                  ## Ignore the token
4795                    !!!nack ('t117.1');
4796                  !!!next-token;                  !!!next-token;
4797                  redo B;                  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}};
4879                $self->{insertion_mode} = 'in head';  
4880                !!!next-token;                ## Reprocess in the "after head" insertion mode...
4881                redo B;              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4882              } elsif ($self->{insertion_mode} eq 'in head' and                !!!cp ('t130');
4883                       {                ## As if </head>
4884                  pop @{$self->{open_elements}};
4885    
4886                  ## Reprocess in the "after head" insertion mode...
4887                } else {
4888                  !!!cp ('t131');
4889                }
4890    
4891                ## "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                    ## Reprocess in the "in head" insertion mode...
4909                    pop @{$self->{open_elements}};
4910                    $self->{insertion_mode} = AFTER_HEAD_IM;
4911                    !!!next-token;
4912                    next B;
4913                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4914                    !!!cp ('t133');
4915                    ## As if </noscript>
4916                    pop @{$self->{open_elements}};
4917                    !!!parse-error (type => 'in noscript:/',
4918                                    text => 'head', token => $token);
4919                    
4920                    ## Reprocess in the "in head" insertion mode...
4921                    pop @{$self->{open_elements}};
4922                    $self->{insertion_mode} = AFTER_HEAD_IM;
4923                    !!!next-token;
4924                    next B;
4925                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4926                    !!!cp ('t134');
4927                    pop @{$self->{open_elements}};
4928                    $self->{insertion_mode} = AFTER_HEAD_IM;
4929                    !!!next-token;
4930                    next B;
4931                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4932                    !!!cp ('t134.1');
4933                    !!!parse-error (type => 'unmatched end tag', text => 'head',
4934                                    token => $token);
4935                    ## Ignore the token
4936                    !!!next-token;
4937                    next B;
4938                  } else {
4939                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
4940                  }
4941                } elsif ($token->{tag_name} eq 'noscript') {
4942                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4943                    !!!cp ('t136');
4944                    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                  }
4960                } elsif ({
4961                        body => 1, html => 1,                        body => 1, html => 1,
                       p => 1, br => 1,  
4962                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
4963                #                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
4964              } elsif ($self->{insertion_mode} eq 'in head noscript' and                    $self->{insertion_mode} == IN_HEAD_IM or
4965                       {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4966                        p => 1, br => 1,                  !!!cp ('t140');
4967                       }->{$token->{tag_name}}) {                  !!!parse-error (type => 'unmatched end tag',
4968                #                                  text => $token->{tag_name}, token => $token);
4969              } elsif ($self->{insertion_mode} ne 'after head') {                  ## Ignore the token
4970                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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                ## Ignore the token
4987                !!!next-token;                !!!next-token;
4988                redo B;                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                    ## Reprocess in the "in head" insertion mode...
5015                    ## As if </head>
5016                    pop @{$self->{open_elements}};
5017                    $self->{insertion_mode} = AFTER_HEAD_IM;
5018    
5019                    ## Reprocess in the "after head" insertion mode...
5020                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5021                    !!!cp ('t143.4');
5022                    #
5023                  } else {
5024                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5025                  }
5026    
5027                  ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5028                  !!!parse-error (type => 'unmatched end tag',
5029                                  text => 'br', token => $token);
5030                  ## Ignore the token
5031                  !!!next-token;
5032                  next B;
5033              } else {              } 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              }              }
           } else {  
             #  
           }  
5041    
5042            ## As if </head> or </noscript> or <body>              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5043            if ($self->{insertion_mode} eq 'in head') {                !!!cp ('t146');
5044              pop @{$self->{open_elements}};                ## As if </noscript>
5045              $self->{insertion_mode} = 'after head';                pop @{$self->{open_elements}};
5046            } elsif ($self->{insertion_mode} eq 'in head noscript') {                !!!parse-error (type => 'in noscript:/',
5047              pop @{$self->{open_elements}};                                text => $token->{tag_name}, token => $token);
5048              !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));                
5049              $self->{insertion_mode} = 'in head';                ## Reprocess in the "in head" insertion mode...
5050            } else { # 'after head'                ## As if </head>
5051              !!!insert-element ('body');                pop @{$self->{open_elements}};
5052              $self->{insertion_mode} = 'in body';  
5053            }                ## Reprocess in the "after head" insertion mode...
5054            ## reprocess              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5055            redo B;                !!!cp ('t147');
5056                  ## As if </head>
5057                  pop @{$self->{open_elements}};
5058    
5059                  ## Reprocess in the "after head" insertion mode...
5060                } 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 {
5069                  !!!cp ('t149');
5070                }
5071    
5072                ## "after head" insertion mode
5073                ## 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              ## NOTE: As if </head>
5101              pop @{$self->{open_elements}};
5102              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5103              ## NOTE: Reprocess.
5104    
5105              #
5106            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5107              !!!cp ('t149.3');
5108    
5109              !!!parse-error (type => 'in noscript:#eof', token => $token);
5110    
5111              ## As if </noscript>
5112              pop @{$self->{open_elements}};
5113              #$self->{insertion_mode} = IN_HEAD_IM;
5114              ## NOTE: Reprocess.
5115    
5116              ## NOTE: As if </head>
5117              pop @{$self->{open_elements}};
5118              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5119              ## NOTE: Reprocess.
5120    
5121              #
5122            } else {
5123              !!!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            ## ISSUE: An issue in the spec.            ## ISSUE: An issue in the spec.
5137          } elsif ($self->{insertion_mode} eq 'in body' or      } elsif ($self->{insertion_mode} & BODY_IMS) {
5138                   $self->{insertion_mode} eq 'in cell' or            if ($token->{type} == CHARACTER_TOKEN) {
5139                   $self->{insertion_mode} eq 'in caption') {              !!!cp ('t150');
           if ($token->{type} eq 'character') {  
5140              ## NOTE: There is a code clone of "character in body".              ## NOTE: There 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 'start tag') {            } elsif ($token->{type} == START_TAG_TOKEN) {
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                if ($self->{insertion_mode} eq 'in cell') {                if ($self->{insertion_mode} == IN_CELL_IM) {
5153                  ## have an element in table scope                  ## have an element in table scope
5154                  my $tn;                  for (reverse 0..$#{$self->{open_elements}}) {
                 INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5155                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5156                    if ($node->[1] eq 'td' or $node->[1] eq 'th') {                    if ($node->[1] & TABLE_CELL_EL) {
5157                      $tn = $node->[1];                      !!!cp ('t151');
5158                      last INSCOPE;  
5159                    } elsif ({                      ## Close the cell
5160                              table => 1, html => 1,                      !!!back-token; # <x>
5161                             }->{$node->[1]}) {                      $token = {type => END_TAG_TOKEN,
5162                      last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5163                    }                                line => $token->{line},
5164                  } # INSCOPE                                column => $token->{column}};
5165                    unless (defined $tn) {                      next B;
5166                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5167                      ## Ignore the token                      !!!cp ('t152');
5168                      !!!next-token;                      ## ISSUE: This case can never be reached, maybe.
5169                      redo B;                      last;
5170                    }                    }
5171                    }
5172    
5173                    !!!cp ('t153');
5174                    !!!parse-error (type => 'start tag not allowed',
5175                        text => $token->{tag_name}, token => $token);
5176                    ## Ignore the token
5177                    !!!nack ('t153.1');
5178                    !!!next-token;
5179                    next B;
5180                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5181                    !!!parse-error (type => 'not closed', text => 'caption',
5182                                    token => $token);
5183                                    
5184                  ## Close the cell                  ## NOTE: As if </caption>.
                 !!!back-token; # <?>  
                 $token = {type => 'end tag', tag_name => $tn};  
                 redo B;  
               } elsif ($self->{insertion_mode} eq 'in caption') {  
                 !!!parse-error (type => 'not closed:caption');  
                   
                 ## As if </caption>  
5185                  ## have a table element in table scope                  ## have a table element in table scope
5186                  my $i;                  my $i;
5187                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5188                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5189                    if ($node->[1] eq 'caption') {                      my $node = $self->{open_elements}->[$_];
5190                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5191                      last INSCOPE;                        !!!cp ('t155');
5192                    } elsif ({                        $i = $_;
5193                              table => 1, html => 1,                        last INSCOPE;
5194                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5195                      last INSCOPE;                        !!!cp ('t156');
5196                          last;
5197                        }
5198                    }                    }
5199    
5200                      !!!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                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:caption');  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5208                                    
5209                  ## generate implied end tags                  ## generate implied end tags
5210                  if ({                  while ($self->{open_elements}->[-1]->[1]
5211                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5212                       td => 1, th => 1, tr => 1,                    !!!cp ('t158');
5213                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token; # <?>  
                   $token = {type => 'end tag', tag_name => 'caption'};  
                   !!!back-token;  
                   $token = {type => 'end tag',  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5214                  }                  }
5215    
5216                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5217                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!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;                  splice @{$self->{open_elements}}, $i;
5227                                    
5228                  $clear_up_to_marker->();                  $clear_up_to_marker->();
5229                                    
5230                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
5231                                    
5232                  ## reprocess                  ## reprocess
5233                  redo B;                  !!!ack-later;
5234                    next B;
5235                } else {                } 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 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5245                if ($self->{insertion_mode} eq 'in cell') {                if ($self->{insertion_mode} == IN_CELL_IM) {
5246                  ## have an element in table scope                  ## have an element in table scope
5247                  my $i;                  my $i;
5248                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5249                    my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5250                    if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5251                        !!!cp ('t163');
5252                      $i = $_;                      $i = $_;
5253                      last INSCOPE;                      last INSCOPE;
5254                    } elsif ({                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5255                              table => 1, html => 1,                      !!!cp ('t164');
                            }->{$node->[1]}) {  
5256                      last INSCOPE;                      last INSCOPE;
5257                    }                    }
5258                  } # INSCOPE                  } # INSCOPE
5259                    unless (defined $i) {                    unless (defined $i) {
5260                      !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      !!!cp ('t165');
5261                        !!!parse-error (type => 'unmatched end tag',
5262                                        text => $token->{tag_name},
5263                                        token => $token);
5264                      ## Ignore the token                      ## Ignore the token
5265                      !!!next-token;                      !!!next-token;
5266                      redo B;                      next B;
5267                    }                    }
5268                                    
5269                  ## generate implied end tags                  ## generate implied end tags
5270                  if ({                  while ($self->{open_elements}->[-1]->[1]
5271                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5272                       td => ($token->{tag_name} eq 'th'),                    !!!cp ('t166');
5273                       th => ($token->{tag_name} eq 'td'),                    pop @{$self->{open_elements}};
                      tr => 1,  
                      tbody => 1, tfoot=> 1, thead => 1,  
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => 'end tag',  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5274                  }                  }
5275                    
5276                  if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5277                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                          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;                  splice @{$self->{open_elements}}, $i;
5288                                    
5289                  $clear_up_to_marker->();                  $clear_up_to_marker->();
5290                                    
5291                  $self->{insertion_mode} = 'in row';                  $self->{insertion_mode} = IN_ROW_IM;
5292                                    
5293                  !!!next-token;                  !!!next-token;
5294                  redo B;                  next B;
5295                } elsif ($self->{insertion_mode} eq 'in caption') {                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5296                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!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 {                } else {
5303                    !!!cp ('t170');
5304                  #                  #
5305                }                }
5306              } elsif ($token->{tag_name} eq 'caption') {              } elsif ($token->{tag_name} eq 'caption') {
5307                if ($self->{insertion_mode} eq 'in caption') {                if ($self->{insertion_mode} == IN_CAPTION_IM) {
5308                  ## have a table element in table scope                  ## have a table element in table scope
5309                  my $i;                  my $i;
5310                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: {
5311                    my $node = $self->{open_elements}->[$_];                    for (reverse 0..$#{$self->{open_elements}}) {
5312                    if ($node->[1] eq $token->{tag_name}) {                      my $node = $self->{open_elements}->[$_];
5313                      $i = $_;                      if ($node->[1] & CAPTION_EL) {
5314                      last INSCOPE;                        !!!cp ('t171');
5315                    } elsif ({                        $i = $_;
5316                              table => 1, html => 1,                        last INSCOPE;
5317                             }->{$node->[1]}) {                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5318                      last INSCOPE;                        !!!cp ('t172');
5319                          last;
5320                        }
5321                    }                    }
5322    
5323                      !!!cp ('t173');
5324                      !!!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                  } # INSCOPE
                   unless (defined $i) {  
                     !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                     ## Ignore the token  
                     !!!next-token;  
                     redo B;  
                   }  
5330                                    
5331                  ## generate implied end tags                  ## generate implied end tags
5332                  if ({                  while ($self->{open_elements}->[-1]->[1]
5333                       dd => 1, dt => 1, li => 1, p => 1,                             & END_TAG_OPTIONAL_EL) {
5334                       td => 1, th => 1, tr => 1,                    !!!cp ('t174');
5335                       tbody => 1, tfoot=> 1, thead => 1,                    pop @{$self->{open_elements}};
                     }->{$self->{open_elements}->[-1]->[1]}) {  
                   !!!back-token;  
                   $token = {type => 'end tag',  
                             tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                   redo B;  
5336                  }                  }
5337                                    
5338                  if ($self->{open_elements}->[-1]->[1] ne 'caption') {                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5339                    !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!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;                  splice @{$self->{open_elements}}, $i;
5349                                    
5350                  $clear_up_to_marker->();                  $clear_up_to_marker->();
5351                                    
5352                  $self->{insertion_mode} = 'in table';                  $self->{insertion_mode} = IN_TABLE_IM;
5353                                    
5354                  !!!next-token;                  !!!next-token;
5355                  redo B;                  next B;
5356                } elsif ($self->{insertion_mode} eq 'in cell') {                } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5357                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t177');
5358                    !!!parse-error (type => 'unmatched end tag',
5359                                    text => $token->{tag_name}, token => $token);
5360                  ## Ignore the token                  ## Ignore the token
5361                  !!!next-token;                  !!!next-token;
5362                  redo B;                  next B;
5363                } else {                } else {
5364                    !!!cp ('t178');
5365                  #                  #
5366                }                }
5367              } elsif ({              } elsif ({
5368                        table => 1, tbody => 1, tfoot => 1,                        table => 1, tbody => 1, tfoot => 1,
5369                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5370                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5371                       $self->{insertion_mode} eq 'in cell') {                       $self->{insertion_mode} == IN_CELL_IM) {
5372                ## have an element in table scope                ## have an element in table scope
5373                my $i;                my $i;
5374                my $tn;                my $tn;
5375                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: {
5376                  my $node = $self->{open_elements}->[$_];                  for (reverse 0..$#{$self->{open_elements}}) {
5377                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5378                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5379                    last INSCOPE;                      !!!cp ('t179');
5380                  } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {                      $i = $_;
5381                    $tn = $node->[1];  
5382                    ## NOTE: There is exactly one |td| or |th| element                      ## Close the cell
5383                    ## in scope in the stack of open elements by definition.                      !!!back-token; # </x>
5384                  } elsif ({                      $token = {type => END_TAG_TOKEN, tag_name => $tn,
5385                            table => 1, html => 1,                                line => $token->{line},
5386                           }->{$node->[1]}) {                                column => $token->{column}};
5387                    last INSCOPE;                      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                } # INSCOPE  
5400                unless (defined $i) {                  !!!cp ('t182');
5401                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5402                        text => $token->{tag_name}, token => $token);
5403                  ## Ignore the token                  ## Ignore the token
5404                  !!!next-token;                  !!!next-token;
5405                  redo B;                  next B;
5406                }                } # INSCOPE
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
5407              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5408                       $self->{insertion_mode} eq 'in caption') {                       $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}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
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, html => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5461                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5462                if ($self->{insertion_mode} eq 'in cell' or                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5463                    $self->{insertion_mode} eq 'in caption') {                  !!!cp ('t190');
5464                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!parse-error (type => 'unmatched end tag',
5465                                    text => $token->{tag_name}, token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  redo B;                  next B;
5469                } else {                } else {
5470                    !!!cp ('t191');
5471                  #                  #
5472                }                }
5473              } elsif ({              } elsif ({
5474                        tbody => 1, tfoot => 1,                        tbody => 1, tfoot => 1,
5475                        thead => 1, tr => 1,                        thead => 1, tr => 1,
5476                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5477                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5478                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!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            $in_body->($insert_to_current);  
5497            redo B;          ## Stop parsing.
5498          } elsif ($self->{insertion_mode} eq 'in table') {          last B;
5499            if ($token->{type} eq 'character') {        } else {
5500              ## NOTE: There are "character in table" code clones.          die "$0: $token->{type}: Unknown token type";
5501              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {        }
5502                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
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}) {            unless (length $token->{data}) {
5512                  !!!next-token;              !!!cp ('t194');
5513                  redo B;              !!!next-token;
5514                }              next B;
5515              }            } else {
5516                !!!cp ('t195');
5517              }
5518            }
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
# Line 3989  sub _tree_construction_main ($) { Line 5525  sub _tree_construction_main ($) {
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 4016  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;          !!!next-token;
5569              redo B;          next B;
5570            } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{type} == START_TAG_TOKEN) {
5571              if ({          if ({
5572                   caption => 1,               tr => ($self->{insertion_mode} != IN_ROW_IM),
5573                   colgroup => 1,               th => 1, td => 1,
5574                   tbody => 1, tfoot => 1, thead => 1,              }->{$token->{tag_name}}) {
5575                  }->{$token->{tag_name}}) {            if ($self->{insertion_mode} == IN_TABLE_IM) {
5576                ## Clear back to table context              ## Clear back to table context
5577                while ($self->{open_elements}->[-1]->[1] ne 'table' and              while (not ($self->{open_elements}->[-1]->[1]
5578                       $self->{open_elements}->[-1]->[1] ne 'html') {                              & TABLE_SCOPING_EL)) {
5579                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!cp ('t201');
5580                  pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               }  
   
               push @$active_formatting_elements, ['#marker', '']  
                 if $token->{tag_name} eq 'caption';  
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = {  
                                  caption => 'in caption',  
                                  colgroup => 'in column group',  
                                  tbody => 'in table body',  
                                  tfoot => 'in table body',  
                                  thead => 'in table body',  
                                 }->{$token->{tag_name}};  
               !!!next-token;  
               redo B;  
             } elsif ({  
                       col => 1,  
                       td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
   
               !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');  
               $self->{insertion_mode} = $token->{tag_name} eq 'col'  
                 ? 'in column group' : 'in table body';  
               ## reprocess  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: There are code clones for this "table in table"  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
   
               ## As if </table>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'table') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'table') {  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
5581              }              }
5582            } else {              
5583              #              !!!insert-element ('tbody',, $token);
5584                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5585                ## reprocess in the "in table body" insertion mode...
5586            }            }
5587              
5588            !!!parse-error (type => 'in table:'.$token->{tag_name});            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5589            $in_body->($insert_to_foster);              unless ($token->{tag_name} eq 'tr') {
5590            redo B;                !!!cp ('t202');
5591          } elsif ($self->{insertion_mode} eq 'in column group') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
5592              }              }
5593                                
5594              #              ## Clear back to table body context
5595            } elsif ($token->{type} eq 'start tag') {              while (not ($self->{open_elements}->[-1]->[1]
5596              if ($token->{tag_name} eq 'col') {                              & TABLE_ROWS_SCOPING_EL)) {
5597                !!!insert-element ($token->{tag_name}, $token->{attributes});                !!!cp ('t203');
5598                  ## ISSUE: Can this case be reached?
5599                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               !!!next-token;  
               redo B;  
             } else {  
               #  
5600              }              }
5601            } elsif ($token->{type} eq 'end tag') {                  
5602              if ($token->{tag_name} eq 'colgroup') {                  $self->{insertion_mode} = IN_ROW_IM;
5603                if ($self->{open_elements}->[-1]->[1] eq 'html') {                  if ($token->{tag_name} eq 'tr') {
5604                  !!!parse-error (type => 'unmatched end tag:colgroup');                    !!!cp ('t204');
5605                  ## Ignore the token                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5606                  !!!next-token;                    !!!nack ('t204');
5607                  redo B;                    !!!next-token;
5608                } else {                    next B;
5609                  pop @{$self->{open_elements}}; # colgroup                  } else {
5610                  $self->{insertion_mode} = 'in table';                    !!!cp ('t205');
5611                  !!!next-token;                    !!!insert-element ('tr',, $token);
5612                  redo B;                                ## reprocess in the "in row" insertion mode
               }  
             } elsif ($token->{tag_name} eq 'col') {  
               !!!parse-error (type => 'unmatched end tag:col');  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           ## As if </colgroup>  
           if ($self->{open_elements}->[-1]->[1] eq 'html') {  
             !!!parse-error (type => 'unmatched end tag:colgroup');  
             ## Ignore the token  
             !!!next-token;  
             redo B;  
           } else {  
             pop @{$self->{open_elements}}; # colgroup  
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table body') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
   
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
   
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
5613                  }                  }
               } # 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});  
5614                } else {                } else {
5615                  $foster_parent_element->insert_before                  !!!cp ('t206');
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({  
                  tr => 1,  
                  th => 1, td => 1,  
                 }->{$token->{tag_name}}) {  
               unless ($token->{tag_name} eq 'tr') {  
                 !!!parse-error (type => 'missing start tag:tr');  
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}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
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  
5867    
5868                ## Clear back to table body context                  !!!next-token;
5869                ## nop by definition                  !!!ack ('t227.2.1');
5870                    next B;
5871                pop @{$self->{open_elements}};                } else {
5872                $self->{insertion_mode} = 'in table';                  !!!cp ('t227.2');
5873                ## reprocess                  #
5874                redo B;                }
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
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 '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}};
5967                                  }
5968                ## generate implied end tags                  
5969                if ({                  pop @{$self->{open_elements}}; # tr
5970                     dd => 1, dt => 1, li => 1, p => 1,                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
5971                     td => 1, th => 1, tr => 1,                  ## reprocess in the "in table body" insertion mode...
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5972                }                }
5973    
5974                if ($self->{open_elements}->[-1]->[1] ne 'table') {                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5975                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  ## have an element in table scope
5976                    my $i;
5977                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5978                      my $node = $self->{open_elements}->[$_];
5979                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5980                        !!!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}};
6013                    $self->{insertion_mode} = IN_TABLE_IM;
6014                    ## reprocess in the "in table" insertion mode...
6015                }                }
6016    
6017                splice @{$self->{open_elements}}, $i;                ## NOTE: </table> in the "in table" insertion mode.
6018                  ## When you edit the code fragment below, please ensure that
6019                $self->_reset_insertion_mode;                ## the code for <table> in the "in table" insertion mode
6020                  ## is synced with it.
6021    
6022                ## reprocess                ## have a table element in table scope
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
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 $token->{tag_name}) {                  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->{tag_name});                  !!!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;
6043                }                }
6044                    
6045                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6046                while (not {                pop @{$open_tables};
6047                  tr => 1, html => 1,                
6048                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6049                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6050                  pop @{$self->{open_elements}};                !!!next-token;
6051                  next B;
6052                } elsif ({
6053                          tbody => 1, tfoot => 1, thead => 1,
6054                         }->{$token->{tag_name}} and
6055                         $self->{insertion_mode} & ROW_IMS) {
6056                  if ($self->{insertion_mode} == IN_ROW_IM) {
6057                    ## have an element in table scope
6058                    my $i;
6059                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6060                      my $node = $self->{open_elements}->[$_];
6061                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6062                        !!!cp ('t247');
6063                        $i = $_;
6064                        last INSCOPE;
6065                      } 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                    
6112                    pop @{$self->{open_elements}}; # tr
6113                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
6114                    ## reprocess in the "in table body" insertion mode...
6115                }                }
6116    
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## 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:'.$token->{type});                  !!!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                        tbody => 1, tfoot => 1, thead => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6155                          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                ## have an element in table scope            !!!cp ('t258');
6160                my $i;            !!!parse-error (type => 'unmatched end tag',
6161                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
6162                  my $node = $self->{open_elements}->[$_];            ## Ignore the token
6163                  if ($node->[1] eq $token->{tag_name}) {            !!!nack ('t258.1');
6164                    $i = $_;             !!!next-token;
6165                    last INSCOPE;            next B;
6166                  } elsif ({          } else {
6167                            table => 1, html => 1,            !!!cp ('t259');
6168                           }->{$node->[1]}) {            !!!parse-error (type => 'in table:/',
6169                    last INSCOPE;                            text => $token->{tag_name}, token => $token);
6170                  }  
6171                } # INSCOPE            $insert = $insert_to_foster;
6172                unless (defined $i) {            #
6173                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          }
6174                  ## Ignore the token        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6175            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6176                    @{$self->{open_elements}} == 1) { # redundant, maybe
6177              !!!parse-error (type => 'in body:#eof', token => $token);
6178              !!!cp ('t259.1');
6179              #
6180            } else {
6181              !!!cp ('t259.2');
6182              #
6183            }
6184    
6185            ## Stop parsing
6186            last B;
6187          } else {
6188            die "$0: $token->{type}: Unknown token type";
6189          }
6190        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6191              if ($token->{type} == CHARACTER_TOKEN) {
6192                if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6193                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6194                  unless (length $token->{data}) {
6195                    !!!cp ('t260');
6196                  !!!next-token;                  !!!next-token;
6197                  redo B;                  next B;
6198                }                }
6199                }
6200                ## As if </tr>              
6201                ## have an element in table scope              !!!cp ('t261');
6202                my $i;              #
6203                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6204                  my $node = $self->{open_elements}->[$_];              if ($token->{tag_name} eq 'col') {
6205                  if ($node->[1] eq 'tr') {                !!!cp ('t262');
6206                    $i = $_;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6207                    last INSCOPE;                pop @{$self->{open_elements}};
6208                  } elsif ({                !!!ack ('t262.1');
6209                            table => 1, html => 1,                !!!next-token;
6210                           }->{$node->[1]}) {                next B;
6211                    last INSCOPE;              } else {
6212                  }                !!!cp ('t263');
6213                } # INSCOPE                #
6214                unless (defined $i) {              }
6215                  !!!parse-error (type => 'unmatched end tag:tr');            } elsif ($token->{type} == END_TAG_TOKEN) {
6216                if ($token->{tag_name} eq 'colgroup') {
6217                  if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6218                    !!!cp ('t264');
6219                    !!!parse-error (type => 'unmatched end tag',
6220                                    text => 'colgroup', token => $token);
6221                  ## Ignore the token                  ## Ignore the token
6222                  !!!next-token;                  !!!next-token;
6223                  redo B;                  next B;
6224                }                } else {
6225                    !!!cp ('t265');
6226                ## Clear back to table row context                  pop @{$self->{open_elements}}; # colgroup
6227                while (not {                  $self->{insertion_mode} = IN_TABLE_IM;
6228                  tr => 1, html => 1,                  !!!next-token;
6229                }->{$self->{open_elements}->[-1]->[1]}) {                  next B;            
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
6230                }                }
6231                } elsif ($token->{tag_name} eq 'col') {
6232                pop @{$self->{open_elements}}; # tr                !!!cp ('t266');
6233                $self->{insertion_mode} = 'in table body';                !!!parse-error (type => 'unmatched end tag',
6234                ## reprocess                                text => 'col', token => $token);
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1, td => 1, th => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
6235                ## Ignore the token                ## Ignore the token
6236                !!!next-token;                !!!next-token;
6237                redo B;                next B;
6238              } else {              } else {
6239                #                !!!cp ('t267');
6240                  #
6241              }              }
6242            } else {        } 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 in table            ## As if </colgroup>
6261            !!!parse-error (type => 'in table:'.$token->{tag_name});            if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6262            $in_body->($insert_to_foster);              !!!cp ('t269');
6263            redo B;  ## TODO: Wrong error type?
6264          } elsif ($self->{insertion_mode} eq 'in select') {              !!!parse-error (type => 'unmatched end tag',
6265            if ($token->{type} eq 'character') {                              text => 'colgroup', token => $token);
6266              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              ## Ignore the token
6267                !!!nack ('t269.1');
6268              !!!next-token;              !!!next-token;
6269              redo B;              next B;
6270            } elsif ($token->{type} eq 'start tag') {            } else {
6271              if ($token->{tag_name} eq 'option') {              !!!cp ('t270');
6272                if ($self->{open_elements}->[-1]->[1] eq 'option') {              pop @{$self->{open_elements}}; # colgroup
6273                  ## As if </option>              $self->{insertion_mode} = IN_TABLE_IM;
6274                  pop @{$self->{open_elements}};              !!!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              }
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                my $data = $1;                  @{$self->{open_elements}} == 1) { # redundant, maybe
6520                ## As if in body            !!!cp ('t299.1');
6521                $reconstruct_active_formatting_elements->($insert_to_current);            !!!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 ($1);            $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:#character');                            text => $token->{tag_name}, token => $token);
6566            } elsif ($token->{type} eq 'start tag') {            #
6567              !!!parse-error (type => 'after body:'.$token->{tag_name});          } else {
6568              #            !!!cp ('t304');
6569            } elsif ($token->{type} eq 'end tag') {            ## "after body" insertion mode
6570              if ($token->{tag_name} eq 'html') {            !!!parse-error (type => 'after body',
6571                if (defined $self->{inner_html_node}) {                            text => $token->{tag_name}, token => $token);
6572                  !!!parse-error (type => 'unmatched end tag:html');            #
6573                  ## Ignore the token          }
6574                  !!!next-token;  
6575                  redo B;          $self->{insertion_mode} = IN_BODY_IM;
6576                } else {          !!!ack-later;
6577                  $previous_insertion_mode = $self->{insertion_mode};          ## reprocess
6578                  $self->{insertion_mode} = 'trailing end';          next B;
6579                  !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
6580                  redo B;          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6581                }            !!!cp ('t305');
6582              } else {            !!!parse-error (type => 'after html:/',
6583                !!!parse-error (type => 'after body:/'.$token->{tag_name});                            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;
6600                next B;
6601            } else {            } else {
6602              die "$0: $token->{type}: Unknown 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            ## Stop parsing
6619            last B;
6620          } else {
6621            die "$0: $token->{type}: Unknown token type";
6622          }
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);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6627              
6628            unless (length $token->{data}) {            unless (length $token->{data}) {
6629                !!!cp ('t310');
6630              !!!next-token;              !!!next-token;
6631              redo B;              next B;
6632            }            }
6633          }          }
6634            
6635          !!!parse-error (type => 'in frameset:#character');          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6636          ## Ignore the token            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6637          !!!next-token;              !!!cp ('t311');
6638          redo B;              !!!parse-error (type => 'in frameset:#text', token => $token);
6639        } elsif ($token->{type} eq 'start tag') {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6640          if ($token->{tag_name} eq 'frameset') {              !!!cp ('t312');
6641            !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!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;            !!!next-token;
6666            redo B;            next B;
6667          } elsif ($token->{tag_name} eq 'frame') {          } elsif ($token->{tag_name} eq 'frame' and
6668            !!!insert-element ($token->{tag_name}, $token->{attributes});                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6669              !!!cp ('t319');
6670              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6671            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6672              !!!ack ('t319.1');
6673            !!!next-token;            !!!next-token;
6674            redo B;            next B;
6675          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6676            $in_body->($insert_to_current);            !!!cp ('t320');
6677            redo B;            ## NOTE: As if in head.
6678          } else {            $parse_rcdata->(CDATA_CONTENT_MODEL);
6679            !!!parse-error (type => 'in frameset:'.$token->{tag_name});            next B;
6680    
6681              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6682              ## has no parse error.
6683            } else {
6684              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            ## Ignore the token
6698              !!!nack ('t322.1');
6699            !!!next-token;            !!!next-token;
6700            redo B;            next B;
6701          }          }
6702        } elsif ($token->{type} eq 'end tag') {        } elsif ($token->{type} == END_TAG_TOKEN) {
6703          if ($token->{tag_name} eq 'frameset') {          if ($token->{tag_name} eq 'frameset' and
6704            if ($self->{open_elements}->[-1]->[1] eq 'html' and              $self->{insertion_mode} == IN_FRAMESET_IM) {
6705              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6706                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6707              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6708                !!!parse-error (type => 'unmatched end tag',
6709                                text => $token->{tag_name}, token => $token);
6710              ## Ignore the token              ## Ignore the token
6711              !!!next-token;              !!!next-token;
6712            } else {            } else {
6713                !!!cp ('t326');
6714              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6715              !!!next-token;              !!!next-token;
6716            }            }
6717    
6718            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6719                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6720              $self->{insertion_mode} = 'after frameset';              !!!cp ('t327');
6721                $self->{insertion_mode} = AFTER_FRAMESET_IM;
6722              } else {
6723                !!!cp ('t328');
6724            }            }
6725            redo B;            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 {          } else {
6733            !!!parse-error (type => 'in frameset:/'.$token->{tag_name});            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          }          }
6750          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6751            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6752                    @{$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 {        } else {
6762          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6763        }        }
     } elsif ($self->{insertion_mode} eq 'after frameset') {  
       if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
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              if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {      ## "in body" insertion mode
6771                !!!parse-error (type => 'after frameset:#character');      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                ## Ignore the token.          unless ($self->{confident}) {
6798                if (length $token->{data}) {            if ($token->{attributes}->{charset}) {
6799                  ## reprocess the rest of characters              !!!cp ('t335');
6800                } else {              ## NOTE: Whether the encoding is supported or not is handled
6801                  !!!next-token;              ## in the {change_encoding} callback.
6802                }              $self->{change_encoding}
6803                redo B;                  ->($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          die qq[$0: Character "$token->{data}"];          !!!ack ('t338.1');
6845        } elsif ($token->{type} eq 'start tag') {          !!!next-token;
6846          if ($token->{tag_name} eq 'noframes') {          next B;
6847            $in_body->($insert_to_current);        } elsif ($token->{tag_name} eq 'title') {
6848            redo B;          !!!cp ('t341');
6849            ## NOTE: This is an "as if in head" code clone
6850            $parse_rcdata->(RCDATA_CONTENT_MODEL);
6851            next B;
6852          } elsif ($token->{tag_name} eq 'body') {
6853            !!!parse-error (type => 'in body', text => 'body', token => $token);
6854                  
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 {          } else {
6860            !!!parse-error (type => 'after frameset:'.$token->{tag_name});            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              }
6869            }
6870            !!!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            ## Ignore the token
6887              !!!nack ('t350.1');
6888            !!!next-token;            !!!next-token;
6889            redo B;            next B;
6890          }          }
6891        } elsif ($token->{type} eq 'end tag') {  
6892          if ($token->{tag_name} eq 'html') {          ## has a p element in scope
6893            $previous_insertion_mode = $self->{insertion_mode};          INSCOPE: for (reverse @{$self->{open_elements}}) {
6894            $self->{insertion_mode} = 'trailing end';            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;
6915                } else {
6916                  !!!cp ('t349');
6917                }
6918              } 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;            !!!next-token;
           redo B;  
6941          } else {          } else {
6942            !!!parse-error (type => 'after frameset:/'.$token->{tag_name});            !!!nack ('t347.1');
           ## Ignore the token  
6943            !!!next-token;            !!!next-token;
           redo B;  
6944          }          }
6945        } else {          next B;
6946          die "$0: $token->{type}: Unknown token type";        } 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            ## Step 1
6962            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 {
6982                !!!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        ## ISSUE: An issue in spec here          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7065      } elsif ($self->{insertion_mode} eq 'trailing end') {          push @$active_formatting_elements, $self->{open_elements}->[-1];
7066        ## states in the main stage is preserved yet # MUST  
7067                  !!!nack ('t374.1');
7068        if ($token->{type} eq 'character') {          !!!next-token;
7069          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          next B;
7070            my $data = $1;        } elsif ($token->{tag_name} eq 'nobr') {
7071            ## As if in the main phase.          $reconstruct_active_formatting_elements->($insert_to_current);
7072            ## NOTE: The insertion mode in the main phase  
7073            ## just before the phase has been changed to the trailing          ## has a |nobr| element in scope
7074            ## end phase is either "after body" or "after frameset".          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7075            $reconstruct_active_formatting_elements->($insert_to_current);            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            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);          $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
7145              !!!nack ('t389'); ## NOTE: Not acknowledged.
7146              !!!next-token;
7147              next B;
7148            } else {
7149              !!!ack ('t391.1');
7150    
7151              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}) {            unless (length $token->{data}) {
7215                !!!cp ('t392');
7216              !!!next-token;              !!!next-token;
7217              redo B;            } else {
7218                !!!cp ('t393');
7219            }            }
7220            } else {
7221              !!!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            }
7243            !!!next-token;
7244            next B;
7245          } elsif ($token->{tag_name} eq 'rt' or
7246                   $token->{tag_name} eq 'rp') {
7247            ## 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          !!!parse-error (type => 'after html:#character');          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7274          $self->{insertion_mode} = $previous_insertion_mode;  
7275          ## reprocess          !!!nack ('t398.5');
7276          redo B;          !!!next-token;
       } elsif ($token->{type} eq 'start tag') {  
         !!!parse-error (type => 'after html:'.$token->{tag_name});  
         $self->{insertion_mode} = $previous_insertion_mode;  
         ## reprocess  
         redo B;  
       } elsif ($token->{type} eq 'end tag') {  
         !!!parse-error (type => 'after html:/'.$token->{tag_name});  
         $self->{insertion_mode} = $previous_insertion_mode;  
         ## reprocess  
7277          redo B;          redo B;
7278          } elsif ($token->{tag_name} eq 'math' or
7279                   $token->{tag_name} eq 'svg') {
7280            $reconstruct_active_formatting_elements->($insert_to_current);
7281    
7282            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7283    
7284            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7285    
7286            ## "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 {        } else {
7319          die "$0: $token->{type}: Unknown token";          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              ## 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              ## Step 3.
7549              splice @{$self->{open_elements}}, $i;
7550            }
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            !!!next-token;
7641            next B;
7642          } elsif ({
7643                    a => 1,
7644                    b => 1, big => 1, em => 1, font => 1, i => 1,
7645                    nobr => 1, s => 1, small => 1, strile => 1,
7646                    strong => 1, tt => 1, u => 1,
7647                   }->{$token->{tag_name}}) {
7648            !!!cp ('t427');
7649            $formatting_end_tag->($token);
7650            next B;
7651          } elsif ($token->{tag_name} eq 'br') {
7652            !!!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 {
7688            ## 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      } else {      }
7751        die "$0: $self->{insertion_mode}: Unknown insertion mode";      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    
# Line 5205  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 5226  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 5239  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          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
7858          $p->{column}++;
7859    
7860        $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
7861        $self->{next_input_character} = ord substr $$s, $i++, 1;          $p->{line}++;
7862        $column++;          $p->{column} = 0;
7863            !!!cp ('i1');
7864        if ($self->{next_input_character} == 0x000A) { # LF        } elsif ($self->{nc} == 0x000D) { # CR
7865          $line++;  ## TODO: support for abort/streaming
7866          $column = 0;          my $next = '';
7867        } elsif ($self->{next_input_character} == 0x000D) { # CR          if ($input->read ($next, 1) and $next ne "\x0A") {
7868          $i++ if substr ($$s, $i, 1) eq "\x0A";            $self->{next_nc} = $next;
7869          $self->{next_input_character} = 0x000A; # LF # MUST          }
7870          $line++;          $self->{nc} = 0x000A; # LF # MUST
7871          $column = 0;          $p->{line}++;
7872        } elsif ($self->{next_input_character} > 0x10FFFF) {          $p->{column} = 0;
7873          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          !!!cp ('i2');
7874        } elsif ($self->{next_input_character} == 0x0000) { # NULL        } 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} = {      $p->{content_model} = {
7944        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
7945        textarea => RCDATA_CONTENT_MODEL,        textarea => RCDATA_CONTENT_MODEL,
# Line 5300  sub set_inner_html ($$$) { Line 7956  sub set_inner_html ($$$) {
7956          unless defined $p->{content_model};          unless defined $p->{content_model};
7957          ## ISSUE: What is "the name of the element"? local name?          ## 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 5332  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 5356  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 5363  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 = $child->tag_name; ## TODO: manakai_tag_name  
       $s .= '<' . $tag_name;  
       ## NOTE: Non-HTML case:  
       ## <http://permalink.gmane.org/gmane.org.w3c.whatwg.discuss/11191>  
   
       my @attrs = @{$child->attributes}; # sort order MUST be stable  
       for my $attr (@attrs) { # order is implementation dependent  
         my $attr_name = $attr->name; ## TODO: manakai_name  
         $s .= ' ' . $attr_name . '="';  
         my $attr_value = $attr->value;  
         ## escape  
         $attr_value =~ s/&/&amp;/g;  
         $attr_value =~ s/</&lt;/g;  
         $attr_value =~ s/>/&gt;/g;  
         $attr_value =~ s/"/&quot;/g;  
         $s .= $attr_value . '"';  
       }  
       $s .= '>';  
         
       next C if {  
         area => 1, base => 1, basefont => 1, bgsound => 1,  
         br => 1, col => 1, embed => 1, frame => 1, hr => 1,  
         img => 1, input => 1, link => 1, meta => 1, param => 1,  
         spacer => 1, wbr => 1,  
       }->{$tag_name};  
   
       $s .= "\x0A" if $tag_name eq 'pre' or $tag_name eq 'textarea';  
   
       if (not $in_cdata and {  
         style => 1, script => 1, xmp => 1, iframe => 1,  
         noembed => 1, noframes => 1, noscript => 1,  
         plaintext => 1,  
       }->{$tag_name}) {  
         unshift @node, 'cdata-out';  
         $in_cdata = 1;  
       }  
   
       unshift @node, @{$child->child_nodes}, '</' . $tag_name . '>';  
     } elsif ($nt == 3 or $nt == 4) {  
       if ($in_cdata) {  
         $s .= $child->data;  
       } else {  
         my $value = $child->data;  
         $value =~ s/&/&amp;/g;  
         $value =~ s/</&lt;/g;  
         $value =~ s/>/&gt;/g;  
         $value =~ s/"/&quot;/g;  
         $s .= $value;  
       }  
     } elsif ($nt == 8) {  
       $s .= '<!--' . $child->data . '-->';  
     } elsif ($nt == 10) {  
       $s .= '<!DOCTYPE ' . $child->name . '>';  
     } elsif ($nt == 5) { # entrefs  
       push @node, @{$child->child_nodes};  
     } else {  
       $on_error->($child) if defined $on_error;  
     }  
     ## ISSUE: This code does not support PIs.  
   } # C  
     
   ## Step 3  
   return \$s;  
 } # get_inner_html  
8027    
8028  1;  1;
8029  # $Date$  # $Date$

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24