/[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.27 by wakaba, Sun Jun 24 14:24:21 2007 UTC revision 1.200 by wakaba, Sat Oct 4 11:32:15 2008 UTC
# Line 1  Line 1 
1  package Whatpm::HTML;  package Whatpm::HTML;
2  use strict;  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4    use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  my $permitted_slash_tag_name = {  require IO::Handle;
23    base => 1,  
24    link => 1,  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25    meta => 1,  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26    hr => 1,  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    br => 1,  my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28    img=> 1,  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    embed => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    param => 1,  
31    area => 1,  sub A_EL () { 0b1 }
32    col => 1,  sub ADDRESS_EL () { 0b10 }
33    input => 1,  sub BODY_EL () { 0b100 }
34    sub BUTTON_EL () { 0b1000 }
35    sub CAPTION_EL () { 0b10000 }
36    sub DD_EL () { 0b100000 }
37    sub DIV_EL () { 0b1000000 }
38    sub DT_EL () { 0b10000000 }
39    sub FORM_EL () { 0b100000000 }
40    sub FORMATTING_EL () { 0b1000000000 }
41    sub FRAMESET_EL () { 0b10000000000 }
42    sub HEADING_EL () { 0b100000000000 }
43    sub HTML_EL () { 0b1000000000000 }
44    sub LI_EL () { 0b10000000000000 }
45    sub NOBR_EL () { 0b100000000000000 }
46    sub OPTION_EL () { 0b1000000000000000 }
47    sub OPTGROUP_EL () { 0b10000000000000000 }
48    sub P_EL () { 0b100000000000000000 }
49    sub SELECT_EL () { 0b1000000000000000000 }
50    sub TABLE_EL () { 0b10000000000000000000 }
51    sub TABLE_CELL_EL () { 0b100000000000000000000 }
52    sub TABLE_ROW_EL () { 0b1000000000000000000000 }
53    sub TABLE_ROW_GROUP_EL () { 0b10000000000000000000000 }
54    sub MISC_SCOPING_EL () { 0b100000000000000000000000 }
55    sub MISC_SPECIAL_EL () { 0b1000000000000000000000000 }
56    sub FOREIGN_EL () { 0b10000000000000000000000000 }
57    sub FOREIGN_FLOW_CONTENT_EL () { 0b100000000000000000000000000 }
58    sub MML_AXML_EL () { 0b1000000000000000000000000000 }
59    sub RUBY_EL () { 0b10000000000000000000000000000 }
60    sub RUBY_COMPONENT_EL () { 0b100000000000000000000000000000 }
61    
62    sub TABLE_ROWS_EL () {
63      TABLE_EL |
64      TABLE_ROW_EL |
65      TABLE_ROW_GROUP_EL
66    }
67    
68    ## NOTE: Used in "generate implied end tags" algorithm.
69    ## NOTE: There is a code where a modified version of
70    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
71    ## implementation (search for the algorithm name).
72    sub END_TAG_OPTIONAL_EL () {
73      DD_EL |
74      DT_EL |
75      LI_EL |
76      OPTION_EL |
77      OPTGROUP_EL |
78      P_EL |
79      RUBY_COMPONENT_EL
80    }
81    
82    ## NOTE: Used in </body> and EOF algorithms.
83    sub ALL_END_TAG_OPTIONAL_EL () {
84      DD_EL |
85      DT_EL |
86      LI_EL |
87      P_EL |
88    
89      BODY_EL |
90      HTML_EL |
91      TABLE_CELL_EL |
92      TABLE_ROW_EL |
93      TABLE_ROW_GROUP_EL
94    }
95    
96    sub SCOPING_EL () {
97      BUTTON_EL |
98      CAPTION_EL |
99      HTML_EL |
100      TABLE_EL |
101      TABLE_CELL_EL |
102      MISC_SCOPING_EL
103    }
104    
105    sub TABLE_SCOPING_EL () {
106      HTML_EL |
107      TABLE_EL
108    }
109    
110    sub TABLE_ROWS_SCOPING_EL () {
111      HTML_EL |
112      TABLE_ROW_GROUP_EL
113    }
114    
115    sub TABLE_ROW_SCOPING_EL () {
116      HTML_EL |
117      TABLE_ROW_EL
118    }
119    
120    sub SPECIAL_EL () {
121      ADDRESS_EL |
122      BODY_EL |
123      DIV_EL |
124    
125      DD_EL |
126      DT_EL |
127      LI_EL |
128      P_EL |
129    
130      FORM_EL |
131      FRAMESET_EL |
132      HEADING_EL |
133      OPTION_EL |
134      OPTGROUP_EL |
135      SELECT_EL |
136      TABLE_ROW_EL |
137      TABLE_ROW_GROUP_EL |
138      MISC_SPECIAL_EL
139    }
140    
141    my $el_category = {
142      a => A_EL | FORMATTING_EL,
143      address => ADDRESS_EL,
144      applet => MISC_SCOPING_EL,
145      area => MISC_SPECIAL_EL,
146      article => MISC_SPECIAL_EL,
147      aside => MISC_SPECIAL_EL,
148      b => FORMATTING_EL,
149      base => MISC_SPECIAL_EL,
150      basefont => MISC_SPECIAL_EL,
151      bgsound => MISC_SPECIAL_EL,
152      big => FORMATTING_EL,
153      blockquote => MISC_SPECIAL_EL,
154      body => BODY_EL,
155      br => MISC_SPECIAL_EL,
156      button => BUTTON_EL,
157      caption => CAPTION_EL,
158      center => MISC_SPECIAL_EL,
159      col => MISC_SPECIAL_EL,
160      colgroup => MISC_SPECIAL_EL,
161      command => MISC_SPECIAL_EL,
162      datagrid => MISC_SPECIAL_EL,
163      dd => DD_EL,
164      details => MISC_SPECIAL_EL,
165      dialog => MISC_SPECIAL_EL,
166      dir => MISC_SPECIAL_EL,
167      div => DIV_EL,
168      dl => MISC_SPECIAL_EL,
169      dt => DT_EL,
170      em => FORMATTING_EL,
171      embed => MISC_SPECIAL_EL,
172      eventsource => MISC_SPECIAL_EL,
173      fieldset => MISC_SPECIAL_EL,
174      figure => MISC_SPECIAL_EL,
175      font => FORMATTING_EL,
176      footer => MISC_SPECIAL_EL,
177      form => FORM_EL,
178      frame => MISC_SPECIAL_EL,
179      frameset => FRAMESET_EL,
180      h1 => HEADING_EL,
181      h2 => HEADING_EL,
182      h3 => HEADING_EL,
183      h4 => HEADING_EL,
184      h5 => HEADING_EL,
185      h6 => HEADING_EL,
186      head => MISC_SPECIAL_EL,
187      header => MISC_SPECIAL_EL,
188      hr => MISC_SPECIAL_EL,
189      html => HTML_EL,
190      i => FORMATTING_EL,
191      iframe => MISC_SPECIAL_EL,
192      img => MISC_SPECIAL_EL,
193      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
194      input => MISC_SPECIAL_EL,
195      isindex => MISC_SPECIAL_EL,
196      li => LI_EL,
197      link => MISC_SPECIAL_EL,
198      listing => MISC_SPECIAL_EL,
199      marquee => MISC_SCOPING_EL,
200      menu => MISC_SPECIAL_EL,
201      meta => MISC_SPECIAL_EL,
202      nav => MISC_SPECIAL_EL,
203      nobr => NOBR_EL | FORMATTING_EL,
204      noembed => MISC_SPECIAL_EL,
205      noframes => MISC_SPECIAL_EL,
206      noscript => MISC_SPECIAL_EL,
207      object => MISC_SCOPING_EL,
208      ol => MISC_SPECIAL_EL,
209      optgroup => OPTGROUP_EL,
210      option => OPTION_EL,
211      p => P_EL,
212      param => MISC_SPECIAL_EL,
213      plaintext => MISC_SPECIAL_EL,
214      pre => MISC_SPECIAL_EL,
215      rp => RUBY_COMPONENT_EL,
216      rt => RUBY_COMPONENT_EL,
217      ruby => RUBY_EL,
218      s => FORMATTING_EL,
219      script => MISC_SPECIAL_EL,
220      select => SELECT_EL,
221      section => MISC_SPECIAL_EL,
222      small => FORMATTING_EL,
223      spacer => MISC_SPECIAL_EL,
224      strike => FORMATTING_EL,
225      strong => FORMATTING_EL,
226      style => MISC_SPECIAL_EL,
227      table => TABLE_EL,
228      tbody => TABLE_ROW_GROUP_EL,
229      td => TABLE_CELL_EL,
230      textarea => MISC_SPECIAL_EL,
231      tfoot => TABLE_ROW_GROUP_EL,
232      th => TABLE_CELL_EL,
233      thead => TABLE_ROW_GROUP_EL,
234      title => MISC_SPECIAL_EL,
235      tr => TABLE_ROW_EL,
236      tt => FORMATTING_EL,
237      u => FORMATTING_EL,
238      ul => MISC_SPECIAL_EL,
239      wbr => MISC_SPECIAL_EL,
240    };
241    
242    my $el_category_f = {
243      $MML_NS => {
244        'annotation-xml' => MML_AXML_EL,
245        mi => FOREIGN_FLOW_CONTENT_EL,
246        mo => FOREIGN_FLOW_CONTENT_EL,
247        mn => FOREIGN_FLOW_CONTENT_EL,
248        ms => FOREIGN_FLOW_CONTENT_EL,
249        mtext => FOREIGN_FLOW_CONTENT_EL,
250      },
251      $SVG_NS => {
252        foreignObject => FOREIGN_FLOW_CONTENT_EL | MISC_SCOPING_EL,
253        desc => FOREIGN_FLOW_CONTENT_EL,
254        title => FOREIGN_FLOW_CONTENT_EL,
255      },
256      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
257    };
258    
259    my $svg_attr_name = {
260      attributename => 'attributeName',
261      attributetype => 'attributeType',
262      basefrequency => 'baseFrequency',
263      baseprofile => 'baseProfile',
264      calcmode => 'calcMode',
265      clippathunits => 'clipPathUnits',
266      contentscripttype => 'contentScriptType',
267      contentstyletype => 'contentStyleType',
268      diffuseconstant => 'diffuseConstant',
269      edgemode => 'edgeMode',
270      externalresourcesrequired => 'externalResourcesRequired',
271      filterres => 'filterRes',
272      filterunits => 'filterUnits',
273      glyphref => 'glyphRef',
274      gradienttransform => 'gradientTransform',
275      gradientunits => 'gradientUnits',
276      kernelmatrix => 'kernelMatrix',
277      kernelunitlength => 'kernelUnitLength',
278      keypoints => 'keyPoints',
279      keysplines => 'keySplines',
280      keytimes => 'keyTimes',
281      lengthadjust => 'lengthAdjust',
282      limitingconeangle => 'limitingConeAngle',
283      markerheight => 'markerHeight',
284      markerunits => 'markerUnits',
285      markerwidth => 'markerWidth',
286      maskcontentunits => 'maskContentUnits',
287      maskunits => 'maskUnits',
288      numoctaves => 'numOctaves',
289      pathlength => 'pathLength',
290      patterncontentunits => 'patternContentUnits',
291      patterntransform => 'patternTransform',
292      patternunits => 'patternUnits',
293      pointsatx => 'pointsAtX',
294      pointsaty => 'pointsAtY',
295      pointsatz => 'pointsAtZ',
296      preservealpha => 'preserveAlpha',
297      preserveaspectratio => 'preserveAspectRatio',
298      primitiveunits => 'primitiveUnits',
299      refx => 'refX',
300      refy => 'refY',
301      repeatcount => 'repeatCount',
302      repeatdur => 'repeatDur',
303      requiredextensions => 'requiredExtensions',
304      requiredfeatures => 'requiredFeatures',
305      specularconstant => 'specularConstant',
306      specularexponent => 'specularExponent',
307      spreadmethod => 'spreadMethod',
308      startoffset => 'startOffset',
309      stddeviation => 'stdDeviation',
310      stitchtiles => 'stitchTiles',
311      surfacescale => 'surfaceScale',
312      systemlanguage => 'systemLanguage',
313      tablevalues => 'tableValues',
314      targetx => 'targetX',
315      targety => 'targetY',
316      textlength => 'textLength',
317      viewbox => 'viewBox',
318      viewtarget => 'viewTarget',
319      xchannelselector => 'xChannelSelector',
320      ychannelselector => 'yChannelSelector',
321      zoomandpan => 'zoomAndPan',
322  };  };
323    
324  my $c1_entity_char = {  my $foreign_attr_xname = {
325      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
326      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
327      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
328      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
329      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
330      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
331      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
332      'xml:base' => [$XML_NS, ['xml', 'base']],
333      'xml:lang' => [$XML_NS, ['xml', 'lang']],
334      'xml:space' => [$XML_NS, ['xml', 'space']],
335      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
336      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
337    };
338    
339    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
340    
341    my $charref_map = {
342      0x0D => 0x000A,
343    0x80 => 0x20AC,    0x80 => 0x20AC,
344    0x81 => 0xFFFD,    0x81 => 0xFFFD,
345    0x82 => 0x201A,    0x82 => 0x201A,
# Line 54  my $c1_entity_char = { Line 372  my $c1_entity_char = {
372    0x9D => 0xFFFD,    0x9D => 0xFFFD,
373    0x9E => 0x017E,    0x9E => 0x017E,
374    0x9F => 0x0178,    0x9F => 0x0178,
375  }; # $c1_entity_char  }; # $charref_map
376    $charref_map->{$_} = 0xFFFD
377        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
378            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
379            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
380            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
381            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
382            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
383            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
384    
385  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
386    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
387    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
388    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  sub parse_byte_string ($$$$;$) {
389    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    my $self = shift;
390    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    my $charset_name = shift;
391    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
392    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
393    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  } # parse_byte_string
394    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
395    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  sub parse_byte_stream ($$$$;$$) {
396  };    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
397  my $scoping_category = {    my $self = ref $_[0] ? shift : shift->new;
398    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $charset_name = shift;
399    table => 1, td => 1, th => 1,    my $byte_stream = $_[0];
400  };  
401  my $formatting_category = {    my $onerror = $_[2] || sub {
402    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,      my (%opt) = @_;
403    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,      warn "Parse error ($opt{type})\n";
404  };    };
405  # $phrasing_category: all other elements    $self->{parse_error} = $onerror; # updated later by parse_char_string
406    
407      my $get_wrapper = $_[3] || sub ($) {
408        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
409      };
410    
411      ## HTML5 encoding sniffing algorithm
412      require Message::Charset::Info;
413      my $charset;
414      my $buffer;
415      my ($char_stream, $e_status);
416    
417      SNIFFING: {
418        ## NOTE: By setting |allow_fallback| option true when the
419        ## |get_decode_handle| method is invoked, we ignore what the HTML5
420        ## spec requires, i.e. unsupported encoding should be ignored.
421          ## TODO: We should not do this unless the parser is invoked
422          ## in the conformance checking mode, in which this behavior
423          ## would be useful.
424    
425        ## Step 1
426        if (defined $charset_name) {
427          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
428              ## TODO: Is this ok?  Transfer protocol's parameter should be
429              ## interpreted in its semantics?
430    
431          ($char_stream, $e_status) = $charset->get_decode_handle
432              ($byte_stream, allow_error_reporting => 1,
433               allow_fallback => 1);
434          if ($char_stream) {
435            $self->{confident} = 1;
436            last SNIFFING;
437          } else {
438            !!!parse-error (type => 'charset:not supported',
439                            layer => 'encode',
440                            line => 1, column => 1,
441                            value => $charset_name,
442                            level => $self->{level}->{uncertain});
443          }
444        }
445    
446        ## Step 2
447        my $byte_buffer = '';
448        for (1..1024) {
449          my $char = $byte_stream->getc;
450          last unless defined $char;
451          $byte_buffer .= $char;
452        } ## TODO: timeout
453    
454        ## Step 3
455        if ($byte_buffer =~ /^\xFE\xFF/) {
456          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
457          ($char_stream, $e_status) = $charset->get_decode_handle
458              ($byte_stream, allow_error_reporting => 1,
459               allow_fallback => 1, byte_buffer => \$byte_buffer);
460          $self->{confident} = 1;
461          last SNIFFING;
462        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
463          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
464          ($char_stream, $e_status) = $charset->get_decode_handle
465              ($byte_stream, allow_error_reporting => 1,
466               allow_fallback => 1, byte_buffer => \$byte_buffer);
467          $self->{confident} = 1;
468          last SNIFFING;
469        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
470          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
471          ($char_stream, $e_status) = $charset->get_decode_handle
472              ($byte_stream, allow_error_reporting => 1,
473               allow_fallback => 1, byte_buffer => \$byte_buffer);
474          $self->{confident} = 1;
475          last SNIFFING;
476        }
477    
478        ## Step 4
479        ## TODO: <meta charset>
480    
481        ## Step 5
482        ## TODO: from history
483    
484        ## Step 6
485        require Whatpm::Charset::UniversalCharDet;
486        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
487            ($byte_buffer);
488        if (defined $charset_name) {
489          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
490    
491          require Whatpm::Charset::DecodeHandle;
492          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
493              ($byte_stream);
494          ($char_stream, $e_status) = $charset->get_decode_handle
495              ($buffer, allow_error_reporting => 1,
496               allow_fallback => 1, byte_buffer => \$byte_buffer);
497          if ($char_stream) {
498            $buffer->{buffer} = $byte_buffer;
499            !!!parse-error (type => 'sniffing:chardet',
500                            text => $charset_name,
501                            level => $self->{level}->{info},
502                            layer => 'encode',
503                            line => 1, column => 1);
504            $self->{confident} = 0;
505            last SNIFFING;
506          }
507        }
508    
509        ## Step 7: default
510        ## TODO: Make this configurable.
511        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
512            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
513            ## detectable in the step 6.
514        require Whatpm::Charset::DecodeHandle;
515        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
516            ($byte_stream);
517        ($char_stream, $e_status)
518            = $charset->get_decode_handle ($buffer,
519                                           allow_error_reporting => 1,
520                                           allow_fallback => 1,
521                                           byte_buffer => \$byte_buffer);
522        $buffer->{buffer} = $byte_buffer;
523        !!!parse-error (type => 'sniffing:default',
524                        text => 'windows-1252',
525                        level => $self->{level}->{info},
526                        line => 1, column => 1,
527                        layer => 'encode');
528        $self->{confident} = 0;
529      } # SNIFFING
530    
531      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
532        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
533        !!!parse-error (type => 'chardecode:fallback',
534                        #text => $self->{input_encoding},
535                        level => $self->{level}->{uncertain},
536                        line => 1, column => 1,
537                        layer => 'encode');
538      } elsif (not ($e_status &
539                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
540        $self->{input_encoding} = $charset->get_iana_name;
541        !!!parse-error (type => 'chardecode:no error',
542                        text => $self->{input_encoding},
543                        level => $self->{level}->{uncertain},
544                        line => 1, column => 1,
545                        layer => 'encode');
546      } else {
547        $self->{input_encoding} = $charset->get_iana_name;
548      }
549    
550  sub parse_string ($$$;$) {    $self->{change_encoding} = sub {
551    my $self = shift->new;      my $self = shift;
552    my $s = \$_[0];      $charset_name = shift;
553        my $token = shift;
554    
555        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
556        ($char_stream, $e_status) = $charset->get_decode_handle
557            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
558             byte_buffer => \ $buffer->{buffer});
559        
560        if ($char_stream) { # if supported
561          ## "Change the encoding" algorithm:
562    
563          ## Step 1    
564          if ($charset->{category} &
565              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
566            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
567            ($char_stream, $e_status) = $charset->get_decode_handle
568                ($byte_stream,
569                 byte_buffer => \ $buffer->{buffer});
570          }
571          $charset_name = $charset->get_iana_name;
572          
573          ## Step 2
574          if (defined $self->{input_encoding} and
575              $self->{input_encoding} eq $charset_name) {
576            !!!parse-error (type => 'charset label:matching',
577                            text => $charset_name,
578                            level => $self->{level}->{info});
579            $self->{confident} = 1;
580            return;
581          }
582    
583          !!!parse-error (type => 'charset label detected',
584                          text => $self->{input_encoding},
585                          value => $charset_name,
586                          level => $self->{level}->{warn},
587                          token => $token);
588          
589          ## Step 3
590          # if (can) {
591            ## change the encoding on the fly.
592            #$self->{confident} = 1;
593            #return;
594          # }
595          
596          ## Step 4
597          throw Whatpm::HTML::RestartParser ();
598        }
599      }; # $self->{change_encoding}
600    
601      my $char_onerror = sub {
602        my (undef, $type, %opt) = @_;
603        !!!parse-error (layer => 'encode',
604                        line => $self->{line}, column => $self->{column} + 1,
605                        %opt, type => $type);
606        if ($opt{octets}) {
607          ${$opt{octets}} = "\x{FFFD}"; # relacement character
608        }
609      };
610    
611      my $wrapped_char_stream = $get_wrapper->($char_stream);
612      $wrapped_char_stream->onerror ($char_onerror);
613    
614      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
615      my $return;
616      try {
617        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
618      } catch Whatpm::HTML::RestartParser with {
619        ## NOTE: Invoked after {change_encoding}.
620    
621        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
622          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
623          !!!parse-error (type => 'chardecode:fallback',
624                          level => $self->{level}->{uncertain},
625                          #text => $self->{input_encoding},
626                          line => 1, column => 1,
627                          layer => 'encode');
628        } elsif (not ($e_status &
629                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
630          $self->{input_encoding} = $charset->get_iana_name;
631          !!!parse-error (type => 'chardecode:no error',
632                          text => $self->{input_encoding},
633                          level => $self->{level}->{uncertain},
634                          line => 1, column => 1,
635                          layer => 'encode');
636        } else {
637          $self->{input_encoding} = $charset->get_iana_name;
638        }
639        $self->{confident} = 1;
640    
641        $wrapped_char_stream = $get_wrapper->($char_stream);
642        $wrapped_char_stream->onerror ($char_onerror);
643    
644        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
645      };
646      return $return;
647    } # parse_byte_stream
648    
649    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
650    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
651    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
652    ## because the core part of our HTML parser expects a string of character,
653    ## not a string of bytes or code units or anything which might contain a BOM.
654    ## Therefore, any parser interface that accepts a string of bytes,
655    ## such as |parse_byte_string| in this module, must ensure that it does
656    ## strip the BOM and never strip any ZWNBSP.
657    
658    sub parse_char_string ($$$;$$) {
659      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
660      my $self = shift;
661      my $s = ref $_[0] ? $_[0] : \($_[0]);
662      require Whatpm::Charset::DecodeHandle;
663      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
664      return $self->parse_char_stream ($input, @_[1..$#_]);
665    } # parse_char_string
666    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
667    
668    sub parse_char_stream ($$$;$$) {
669      my $self = ref $_[0] ? shift : shift->new;
670      my $input = $_[0];
671    $self->{document} = $_[1];    $self->{document} = $_[1];
672      @{$self->{document}->child_nodes} = ();
673    
674    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
675    
676    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
677    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
678    my $column = 0;        if defined $self->{input_encoding};
679    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
680    
681      $self->{line_prev} = $self->{line} = 1;
682      $self->{column_prev} = -1;
683      $self->{column} = 0;
684      $self->{set_nc} = sub {
685      my $self = shift;      my $self = shift;
686    
687      pop @{$self->{prev_input_character}};      my $char = '';
688      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
689          $char = $self->{next_nc};
690          delete $self->{next_nc};
691          $self->{nc} = ord $char;
692        } else {
693          $self->{char_buffer} = '';
694          $self->{char_buffer_pos} = 0;
695    
696          my $count = $input->manakai_read_until
697             ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
698          if ($count) {
699            $self->{line_prev} = $self->{line};
700            $self->{column_prev} = $self->{column};
701            $self->{column}++;
702            $self->{nc}
703                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
704            return;
705          }
706    
707      $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($input->read ($char, 1)) {
708      $self->{next_input_character} = ord substr $$s, $i++, 1;          $self->{nc} = ord $char;
709      $column++;        } else {
710            $self->{nc} = -1;
711            return;
712          }
713        }
714    
715        ($self->{line_prev}, $self->{column_prev})
716            = ($self->{line}, $self->{column});
717        $self->{column}++;
718            
719      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
720        $line++;        !!!cp ('j1');
721        $column = 0;        $self->{line}++;
722      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
723        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
724        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
725        $line++;  ## TODO: support for abort/streaming
726        $column = 0;        my $next = '';
727      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
728        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
729      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
730          $self->{nc} = 0x000A; # LF # MUST
731          $self->{line}++;
732          $self->{column} = 0;
733        } elsif ($self->{nc} == 0x0000) { # NULL
734          !!!cp ('j4');
735        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
736        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
737      }      }
738    };    };
739    $self->{prev_input_character} = [-1, -1, -1];  
740    $self->{next_input_character} = -1;    $self->{read_until} = sub {
741        #my ($scalar, $specials_range, $offset) = @_;
742        return 0 if defined $self->{next_nc};
743    
744        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
745        my $offset = $_[2] || 0;
746    
747        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
748          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
749          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
750            substr ($_[0], $offset)
751                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
752            my $count = $+[0] - $-[0];
753            if ($count) {
754              $self->{column} += $count;
755              $self->{char_buffer_pos} += $count;
756              $self->{line_prev} = $self->{line};
757              $self->{column_prev} = $self->{column} - 1;
758              $self->{nc} = -1;
759            }
760            return $count;
761          } else {
762            return 0;
763          }
764        } else {
765          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
766          if ($count) {
767            $self->{column} += $count;
768            $self->{line_prev} = $self->{line};
769            $self->{column_prev} = $self->{column} - 1;
770            $self->{nc} = -1;
771          }
772          return $count;
773        }
774      }; # $self->{read_until}
775    
776    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
777      my (%opt) = @_;      my (%opt) = @_;
778      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
779        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
780        warn "Parse error ($opt{type}) at line $line column $column\n";
781    };    };
782    $self->{parse_error} = sub {    $self->{parse_error} = sub {
783      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
784    };    };
785    
786      my $char_onerror = sub {
787        my (undef, $type, %opt) = @_;
788        !!!parse-error (layer => 'encode',
789                        line => $self->{line}, column => $self->{column} + 1,
790                        %opt, type => $type);
791      }; # $char_onerror
792    
793      if ($_[3]) {
794        $input = $_[3]->($input);
795        $input->onerror ($char_onerror);
796      } else {
797        $input->onerror ($char_onerror) unless defined $input->onerror;
798      }
799    
800    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
801    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
802    $self->_construct_tree;    $self->_construct_tree;
803    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
804    
805      delete $self->{parse_error}; # remove loop
806    
807    return $self->{document};    return $self->{document};
808  } # parse_string  } # parse_char_stream
809    
810  sub new ($) {  sub new ($) {
811    my $class = shift;    my $class = shift;
812    my $self = bless {}, $class;    my $self = bless {
813    $self->{set_next_input_character} = sub {      level => {must => 'm',
814      $self->{next_input_character} = -1;                should => 's',
815                  warn => 'w',
816                  info => 'i',
817                  uncertain => 'u'},
818      }, $class;
819      $self->{set_nc} = sub {
820        $self->{nc} = -1;
821    };    };
822    $self->{parse_error} = sub {    $self->{parse_error} = sub {
823      #      #
824    };    };
825      $self->{change_encoding} = sub {
826        # if ($_[0] is a supported encoding) {
827        #   run "change the encoding" algorithm;
828        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
829        # }
830      };
831      $self->{application_cache_selection} = sub {
832        #
833      };
834    return $self;    return $self;
835  } # new  } # new
836    
837    sub CM_ENTITY () { 0b001 } # & markup in data
838    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
839    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
840    
841    sub PLAINTEXT_CONTENT_MODEL () { 0 }
842    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
843    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
844    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
845    
846    sub DATA_STATE () { 0 }
847    #sub ENTITY_DATA_STATE () { 1 }
848    sub TAG_OPEN_STATE () { 2 }
849    sub CLOSE_TAG_OPEN_STATE () { 3 }
850    sub TAG_NAME_STATE () { 4 }
851    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
852    sub ATTRIBUTE_NAME_STATE () { 6 }
853    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
854    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
855    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
856    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
857    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
858    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
859    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
860    sub COMMENT_START_STATE () { 14 }
861    sub COMMENT_START_DASH_STATE () { 15 }
862    sub COMMENT_STATE () { 16 }
863    sub COMMENT_END_STATE () { 17 }
864    sub COMMENT_END_DASH_STATE () { 18 }
865    sub BOGUS_COMMENT_STATE () { 19 }
866    sub DOCTYPE_STATE () { 20 }
867    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
868    sub DOCTYPE_NAME_STATE () { 22 }
869    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
870    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
871    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
872    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
873    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
874    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
875    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
876    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
877    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
878    sub BOGUS_DOCTYPE_STATE () { 32 }
879    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
880    sub SELF_CLOSING_START_TAG_STATE () { 34 }
881    sub CDATA_SECTION_STATE () { 35 }
882    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
883    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
884    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
885    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
886    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
887    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
888    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
889    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
890    ## NOTE: "Entity data state", "entity in attribute value state", and
891    ## "consume a character reference" algorithm are jointly implemented
892    ## using the following six states:
893    sub ENTITY_STATE () { 44 }
894    sub ENTITY_HASH_STATE () { 45 }
895    sub NCR_NUM_STATE () { 46 }
896    sub HEXREF_X_STATE () { 47 }
897    sub HEXREF_HEX_STATE () { 48 }
898    sub ENTITY_NAME_STATE () { 49 }
899    sub PCDATA_STATE () { 50 } # "data state" in the spec
900    
901    sub DOCTYPE_TOKEN () { 1 }
902    sub COMMENT_TOKEN () { 2 }
903    sub START_TAG_TOKEN () { 3 }
904    sub END_TAG_TOKEN () { 4 }
905    sub END_OF_FILE_TOKEN () { 5 }
906    sub CHARACTER_TOKEN () { 6 }
907    
908    sub AFTER_HTML_IMS () { 0b100 }
909    sub HEAD_IMS ()       { 0b1000 }
910    sub BODY_IMS ()       { 0b10000 }
911    sub BODY_TABLE_IMS () { 0b100000 }
912    sub TABLE_IMS ()      { 0b1000000 }
913    sub ROW_IMS ()        { 0b10000000 }
914    sub BODY_AFTER_IMS () { 0b100000000 }
915    sub FRAME_IMS ()      { 0b1000000000 }
916    sub SELECT_IMS ()     { 0b10000000000 }
917    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
918        ## NOTE: "in foreign content" insertion mode is special; it is combined
919        ## with the secondary insertion mode.  In this parser, they are stored
920        ## together in the bit-or'ed form.
921    
922    ## NOTE: "initial" and "before html" insertion modes have no constants.
923    
924    ## NOTE: "after after body" insertion mode.
925    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
926    
927    ## NOTE: "after after frameset" insertion mode.
928    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
929    
930    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
931    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
932    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
933    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
934    sub IN_BODY_IM () { BODY_IMS }
935    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
936    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
937    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
938    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
939    sub IN_TABLE_IM () { TABLE_IMS }
940    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
941    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
942    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
943    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
944    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
945    sub IN_COLUMN_GROUP_IM () { 0b10 }
946    
947  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
948    
949  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
950    my $self = shift;    my $self = shift;
951    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
952    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{s_kwd}; # state keyword - initialized when used
953    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    #$self->{entity__value}; # initialized when used
954    undef $self->{current_attribute};    #$self->{entity__match}; # initialized when used
955    undef $self->{last_emitted_start_tag_name};    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
956    undef $self->{last_attribute_value_state};    undef $self->{ct}; # current token
957    $self->{char} = [];    undef $self->{ca}; # current attribute
958    # $self->{next_input_character}    undef $self->{last_stag_name}; # last emitted start tag name
959      #$self->{prev_state}; # initialized when used
960      delete $self->{self_closing};
961      $self->{char_buffer} = '';
962      $self->{char_buffer_pos} = 0;
963      $self->{nc} = -1; # next input character
964      #$self->{next_nc}
965    !!!next-input-character;    !!!next-input-character;
966    $self->{token} = [];    $self->{token} = [];
967    # $self->{escape}    # $self->{escape}
968  } # _initialize_tokenizer  } # _initialize_tokenizer
969    
970  ## A token has:  ## A token has:
971  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
972  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
973  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
974  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
975  ##   ->{system_identifier} (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
976  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{sysid} (DOCTYPE_TOKEN)
977  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
978  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
979    ##        ->{name}
980    ##        ->{value}
981    ##        ->{has_reference} == 1 or 0
982    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
983    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
984    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
985    ##     while the token is pushed back to the stack.
986    
987  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
988    
# Line 179  sub _initialize_tokenizer ($) { Line 992  sub _initialize_tokenizer ($) {
992  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
993  ## and removed from the list.  ## and removed from the list.
994    
995    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
996    ## (This requirement was dropped from HTML5 spec, unfortunately.)
997    
998    my $is_space = {
999      0x0009 => 1, # CHARACTER TABULATION (HT)
1000      0x000A => 1, # LINE FEED (LF)
1001      #0x000B => 0, # LINE TABULATION (VT)
1002      0x000C => 1, # FORM FEED (FF)
1003      #0x000D => 1, # CARRIAGE RETURN (CR)
1004      0x0020 => 1, # SPACE (SP)
1005    };
1006    
1007  sub _get_next_token ($) {  sub _get_next_token ($) {
1008    my $self = shift;    my $self = shift;
1009    
1010      if ($self->{self_closing}) {
1011        !!!parse-error (type => 'nestc', token => $self->{ct});
1012        ## NOTE: The |self_closing| flag is only set by start tag token.
1013        ## In addition, when a start tag token is emitted, it is always set to
1014        ## |ct|.
1015        delete $self->{self_closing};
1016      }
1017    
1018    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1019        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1020      return shift @{$self->{token}};      return shift @{$self->{token}};
1021    }    }
1022    
1023    A: {    A: {
1024      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1025        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1026          if ($self->{content_model_flag} eq 'PCDATA' or  
1027              $self->{content_model_flag} eq 'RCDATA') {        if ($self->{nc} == 0x0026) { # &
1028            $self->{state} = 'entity data';          !!!cp (0.1);
1029            ## NOTE: In the spec, the tokenizer is switched to the
1030            ## "entity data state".  In this implementation, the tokenizer
1031            ## is switched to the |ENTITY_STATE|, which is an implementation
1032            ## of the "consume a character reference" algorithm.
1033            $self->{entity_add} = -1;
1034            $self->{prev_state} = DATA_STATE;
1035            $self->{state} = ENTITY_STATE;
1036            !!!next-input-character;
1037            redo A;
1038          } elsif ($self->{nc} == 0x003C) { # <
1039            !!!cp (0.2);
1040            $self->{state} = TAG_OPEN_STATE;
1041            !!!next-input-character;
1042            redo A;
1043          } elsif ($self->{nc} == -1) {
1044            !!!cp (0.3);
1045            !!!emit ({type => END_OF_FILE_TOKEN,
1046                      line => $self->{line}, column => $self->{column}});
1047            last A; ## TODO: ok?
1048          } else {
1049            !!!cp (0.4);
1050            #
1051          }
1052    
1053          # Anything else
1054          my $token = {type => CHARACTER_TOKEN,
1055                       data => chr $self->{nc},
1056                       line => $self->{line}, column => $self->{column},
1057                      };
1058          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1059    
1060          ## Stay in the state.
1061          !!!next-input-character;
1062          !!!emit ($token);
1063          redo A;
1064        } elsif ($self->{state} == DATA_STATE) {
1065          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1066          if ($self->{nc} == 0x0026) { # &
1067            $self->{s_kwd} = '';
1068            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1069                not $self->{escape}) {
1070              !!!cp (1);
1071              ## NOTE: In the spec, the tokenizer is switched to the
1072              ## "entity data state".  In this implementation, the tokenizer
1073              ## is switched to the |ENTITY_STATE|, which is an implementation
1074              ## of the "consume a character reference" algorithm.
1075              $self->{entity_add} = -1;
1076              $self->{prev_state} = DATA_STATE;
1077              $self->{state} = ENTITY_STATE;
1078            !!!next-input-character;            !!!next-input-character;
1079            redo A;            redo A;
1080          } else {          } else {
1081              !!!cp (2);
1082            #            #
1083          }          }
1084        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1085          if ($self->{content_model_flag} eq 'RCDATA' or          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1086              $self->{content_model_flag} eq 'CDATA') {            $self->{s_kwd} .= '-';
1087            unless ($self->{escape}) {            
1088              if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '<!--') {
1089                  $self->{prev_input_character}->[1] == 0x0021 and # !              !!!cp (3);
1090                  $self->{prev_input_character}->[2] == 0x003C) { # <              $self->{escape} = 1; # unless $self->{escape};
1091                $self->{escape} = 1;              $self->{s_kwd} = '--';
1092              }              #
1093              } elsif ($self->{s_kwd} eq '---') {
1094                !!!cp (4);
1095                $self->{s_kwd} = '--';
1096                #
1097              } else {
1098                !!!cp (5);
1099                #
1100            }            }
1101          }          }
1102                    
1103          #          #
1104        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1105          if ($self->{content_model_flag} eq 'PCDATA' or          if (length $self->{s_kwd}) {
1106              (($self->{content_model_flag} eq 'CDATA' or            !!!cp (5.1);
1107                $self->{content_model_flag} eq 'RCDATA') and            $self->{s_kwd} .= '!';
1108              #
1109            } else {
1110              !!!cp (5.2);
1111              #$self->{s_kwd} = '';
1112              #
1113            }
1114            #
1115          } elsif ($self->{nc} == 0x003C) { # <
1116            if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1117                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1118               not $self->{escape})) {               not $self->{escape})) {
1119            $self->{state} = 'tag open';            !!!cp (6);
1120              $self->{state} = TAG_OPEN_STATE;
1121            !!!next-input-character;            !!!next-input-character;
1122            redo A;            redo A;
1123          } else {          } else {
1124              !!!cp (7);
1125              $self->{s_kwd} = '';
1126            #            #
1127          }          }
1128        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1129          if ($self->{escape} and          if ($self->{escape} and
1130              ($self->{content_model_flag} eq 'RCDATA' or              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1131               $self->{content_model_flag} eq 'CDATA')) {            if ($self->{s_kwd} eq '--') {
1132            if ($self->{prev_input_character}->[0] == 0x002D and # -              !!!cp (8);
               $self->{prev_input_character}->[1] == 0x002D) { # -  
1133              delete $self->{escape};              delete $self->{escape};
1134              } else {
1135                !!!cp (9);
1136            }            }
1137            } else {
1138              !!!cp (10);
1139          }          }
1140                    
1141            $self->{s_kwd} = '';
1142          #          #
1143        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1144          !!!emit ({type => 'end-of-file'});          !!!cp (11);
1145            $self->{s_kwd} = '';
1146            !!!emit ({type => END_OF_FILE_TOKEN,
1147                      line => $self->{line}, column => $self->{column}});
1148          last A; ## TODO: ok?          last A; ## TODO: ok?
1149          } else {
1150            !!!cp (12);
1151            $self->{s_kwd} = '';
1152            #
1153        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
   
       !!!emit ($token);  
   
       redo A;  
     } elsif ($self->{state} eq 'entity data') {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0);  
1154    
1155        $self->{state} = 'data';        # Anything else
1156        # next-input-character is already done        my $token = {type => CHARACTER_TOKEN,
1157                       data => chr $self->{nc},
1158                       line => $self->{line}, column => $self->{column},
1159                      };
1160          if ($self->{read_until}->($token->{data}, q[-!<>&],
1161                                    length $token->{data})) {
1162            $self->{s_kwd} = '';
1163          }
1164    
1165        unless (defined $token) {        ## Stay in the data state.
1166          !!!emit ({type => 'character', data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1167            !!!cp (13);
1168            $self->{state} = PCDATA_STATE;
1169        } else {        } else {
1170          !!!emit ($token);          !!!cp (14);
1171            ## Stay in the state.
1172        }        }
1173          !!!next-input-character;
1174          !!!emit ($token);
1175        redo A;        redo A;
1176      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1177        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1178            $self->{content_model_flag} eq 'CDATA') {          if ($self->{nc} == 0x002F) { # /
1179          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1180            !!!next-input-character;            !!!next-input-character;
1181            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1182            redo A;            redo A;
1183            } elsif ($self->{nc} == 0x0021) { # !
1184              !!!cp (15.1);
1185              $self->{s_kwd} = '<' unless $self->{escape};
1186              #
1187          } else {          } else {
1188            ## reconsume            !!!cp (16);
1189            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1190          }          }
1191        } elsif ($self->{content_model_flag} eq 'PCDATA') {  
1192          if ($self->{next_input_character} == 0x0021) { # !          ## reconsume
1193            $self->{state} = 'markup declaration open';          $self->{state} = DATA_STATE;
1194            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1195                      line => $self->{line_prev},
1196                      column => $self->{column_prev},
1197                     });
1198            redo A;
1199          } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1200            if ($self->{nc} == 0x0021) { # !
1201              !!!cp (17);
1202              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1203            !!!next-input-character;            !!!next-input-character;
1204            redo A;            redo A;
1205          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1206            $self->{state} = 'close tag open';            !!!cp (18);
1207              $self->{state} = CLOSE_TAG_OPEN_STATE;
1208            !!!next-input-character;            !!!next-input-character;
1209            redo A;            redo A;
1210          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1211                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1212            $self->{current_token}            !!!cp (19);
1213              = {type => 'start tag',            $self->{ct}
1214                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1215            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1216                   line => $self->{line_prev},
1217                   column => $self->{column_prev}};
1218              $self->{state} = TAG_NAME_STATE;
1219            !!!next-input-character;            !!!next-input-character;
1220            redo A;            redo A;
1221          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1222                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1223            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1224                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1225            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1226                                        line => $self->{line_prev},
1227                                        column => $self->{column_prev}};
1228              $self->{state} = TAG_NAME_STATE;
1229            !!!next-input-character;            !!!next-input-character;
1230            redo A;            redo A;
1231          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1232            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1233            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1234                              line => $self->{line_prev},
1235                              column => $self->{column_prev});
1236              $self->{state} = DATA_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238    
1239            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1240                        line => $self->{line_prev},
1241                        column => $self->{column_prev},
1242                       });
1243    
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1246            !!!parse-error (type => 'pio');            !!!cp (22);
1247            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1248            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250              $self->{state} = BOGUS_COMMENT_STATE;
1251              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1252                                        line => $self->{line_prev},
1253                                        column => $self->{column_prev},
1254                                       };
1255              ## $self->{nc} is intentionally left as is
1256            redo A;            redo A;
1257          } else {          } else {
1258            !!!parse-error (type => 'bare stago');            !!!cp (23);
1259            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1260                              line => $self->{line_prev},
1261                              column => $self->{column_prev});
1262              $self->{state} = DATA_STATE;
1263            ## reconsume            ## reconsume
1264    
1265            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1266                        line => $self->{line_prev},
1267                        column => $self->{column_prev},
1268                       });
1269    
1270            redo A;            redo A;
1271          }          }
1272        } else {        } else {
1273          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1274        }        }
1275      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1276        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1277            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1278          if (defined $self->{last_emitted_start_tag_name}) {  
1279            my @next_char;        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1280            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1281              push @next_char, $self->{next_input_character};          if (defined $self->{last_stag_name}) {
1282              my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1283              my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            $self->{s_kwd} = '';
1284              if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {            ## Reconsume.
1285                !!!next-input-character;            redo A;
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = 'data';  
   
               !!!emit ({type => 'character', data => '</'});  
     
               redo A;  
             }  
           }  
           push @next_char, $self->{next_input_character};  
         
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = 'data';  
             !!!emit ({type => 'character', data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1286          } else {          } else {
1287            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1288            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1289            $self->{state} = 'data';            !!!cp (28);
1290            !!!emit ({type => 'character', data => '</'});            $self->{state} = DATA_STATE;
1291              ## Reconsume.
1292              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1293                        line => $l, column => $c,
1294                       });
1295            redo A;            redo A;
1296          }          }
1297        }        }
1298          
1299        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1300            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1301          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1302                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1303          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1304          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1305          redo A;                 line => $l, column => $c};
1306        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1307                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1308          $self->{current_token} = {type => 'end tag',          redo A;
1309                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1310          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1311          !!!next-input-character;          !!!cp (30);
1312          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1313        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1314          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1315          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1316            !!!next-input-character;
1317            redo A;
1318          } elsif ($self->{nc} == 0x003E) { # >
1319            !!!cp (31);
1320            !!!parse-error (type => 'empty end tag',
1321                            line => $self->{line_prev}, ## "<" in "</>"
1322                            column => $self->{column_prev} - 1);
1323            $self->{state} = DATA_STATE;
1324          !!!next-input-character;          !!!next-input-character;
1325          redo A;          redo A;
1326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1327            !!!cp (32);
1328          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1329          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1330          # reconsume          # reconsume
1331    
1332          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1333                      line => $l, column => $c,
1334                     });
1335    
1336          redo A;          redo A;
1337        } else {        } else {
1338            !!!cp (33);
1339          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1340          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1341          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1342          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1343                                      column => $self->{column_prev} - 1,
1344                                     };
1345            ## NOTE: $self->{nc} is intentionally left as is.
1346            ## Although the "anything else" case of the spec not explicitly
1347            ## states that the next input character is to be reconsumed,
1348            ## it will be included to the |data| of the comment token
1349            ## generated from the bogus end tag, as defined in the
1350            ## "bogus comment state" entry.
1351            redo A;
1352          }
1353        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1354          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1355          if (length $ch) {
1356            my $CH = $ch;
1357            $ch =~ tr/a-z/A-Z/;
1358            my $nch = chr $self->{nc};
1359            if ($nch eq $ch or $nch eq $CH) {
1360              !!!cp (24);
1361              ## Stay in the state.
1362              $self->{s_kwd} .= $nch;
1363              !!!next-input-character;
1364              redo A;
1365            } else {
1366              !!!cp (25);
1367              $self->{state} = DATA_STATE;
1368              ## Reconsume.
1369              !!!emit ({type => CHARACTER_TOKEN,
1370                        data => '</' . $self->{s_kwd},
1371                        line => $self->{line_prev},
1372                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1373                       });
1374              redo A;
1375            }
1376          } else { # after "<{tag-name}"
1377            unless ($is_space->{$self->{nc}} or
1378                    {
1379                     0x003E => 1, # >
1380                     0x002F => 1, # /
1381                     -1 => 1, # EOF
1382                    }->{$self->{nc}}) {
1383              !!!cp (26);
1384              ## Reconsume.
1385              $self->{state} = DATA_STATE;
1386              !!!emit ({type => CHARACTER_TOKEN,
1387                        data => '</' . $self->{s_kwd},
1388                        line => $self->{line_prev},
1389                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1390                       });
1391              redo A;
1392            } else {
1393              !!!cp (27);
1394              $self->{ct}
1395                  = {type => END_TAG_TOKEN,
1396                     tag_name => $self->{last_stag_name},
1397                     line => $self->{line_prev},
1398                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1399              $self->{state} = TAG_NAME_STATE;
1400              ## Reconsume.
1401              redo A;
1402            }
1403        }        }
1404      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1405        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1406            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
1407            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1408            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1409            $self->{next_input_character} == 0x0020) { # SP          redo A;
1410          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
1411          !!!next-input-character;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1412          redo A;            !!!cp (35);
1413        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{last_stag_name} = $self->{ct}->{tag_name};
1414          if ($self->{current_token}->{type} eq 'start tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1415            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1416          } elsif ($self->{current_token}->{type} eq 'end tag') {            #if ($self->{ct}->{attributes}) {
1417            $self->{content_model_flag} = 'PCDATA'; # MUST            #  ## NOTE: This should never be reached.
1418            if ($self->{current_token}->{attributes}) {            #  !!! cp (36);
1419              !!!parse-error (type => 'end tag attribute');            #  !!! parse-error (type => 'end tag attribute');
1420            }            #} else {
1421                !!!cp (37);
1422              #}
1423          } else {          } else {
1424            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1425          }          }
1426          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1427          !!!next-input-character;          !!!next-input-character;
1428    
1429          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1430    
1431          redo A;          redo A;
1432        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1433                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1434          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1435            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1436            # start tag or end tag            # start tag or end tag
1437          ## Stay in this state          ## Stay in this state
1438          !!!next-input-character;          !!!next-input-character;
1439          redo A;          redo A;
1440        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1441          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1442          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1443            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (39);
1444          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1445            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1446            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1447              !!!parse-error (type => 'end tag attribute');            #if ($self->{ct}->{attributes}) {
1448            }            #  ## NOTE: This state should never be reached.
1449              #  !!! cp (40);
1450              #  !!! parse-error (type => 'end tag attribute');
1451              #} else {
1452                !!!cp (41);
1453              #}
1454          } else {          } else {
1455            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1456          }          }
1457          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1458          # reconsume          # reconsume
1459    
1460          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1461    
1462          redo A;          redo A;
1463        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1464            !!!cp (42);
1465            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1466          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1467          redo A;          redo A;
1468        } else {        } else {
1469          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1470            $self->{ct}->{tag_name} .= chr $self->{nc};
1471            # start tag or end tag            # start tag or end tag
1472          ## Stay in the state          ## Stay in the state
1473          !!!next-input-character;          !!!next-input-character;
1474          redo A;          redo A;
1475        }        }
1476      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1477        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1478            $self->{next_input_character} == 0x000A or # LF          !!!cp (45);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1479          ## Stay in the state          ## Stay in the state
1480          !!!next-input-character;          !!!next-input-character;
1481          redo A;          redo A;
1482        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1483          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1484            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (46);
1485          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1486            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1487            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1488              if ($self->{ct}->{attributes}) {
1489                !!!cp (47);
1490              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1491              } else {
1492                !!!cp (48);
1493            }            }
1494          } else {          } else {
1495            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1496          }          }
1497          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1498          !!!next-input-character;          !!!next-input-character;
1499    
1500          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1501    
1502          redo A;          redo A;
1503        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1504                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1505          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1506                                value => ''};          $self->{ca}
1507          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1508                   value => '',
1509                   line => $self->{line}, column => $self->{column}};
1510            $self->{state} = ATTRIBUTE_NAME_STATE;
1511          !!!next-input-character;          !!!next-input-character;
1512          redo A;          redo A;
1513        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1514            !!!cp (50);
1515            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1516          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         ## Stay in the state  
         # next-input-character is already done  
1517          redo A;          redo A;
1518        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1519          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1520          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1521            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (52);
1522          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1523            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1524            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1525              if ($self->{ct}->{attributes}) {
1526                !!!cp (53);
1527              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1528              } else {
1529                !!!cp (54);
1530            }            }
1531          } else {          } else {
1532            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1533          }          }
1534          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1535          # reconsume          # reconsume
1536    
1537          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1538    
1539          redo A;          redo A;
1540        } else {        } else {
1541          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1542                                value => ''};               0x0022 => 1, # "
1543          $self->{state} = 'attribute name';               0x0027 => 1, # '
1544                 0x003D => 1, # =
1545                }->{$self->{nc}}) {
1546              !!!cp (55);
1547              !!!parse-error (type => 'bad attribute name');
1548            } else {
1549              !!!cp (56);
1550            }
1551            $self->{ca}
1552                = {name => chr ($self->{nc}),
1553                   value => '',
1554                   line => $self->{line}, column => $self->{column}};
1555            $self->{state} = ATTRIBUTE_NAME_STATE;
1556          !!!next-input-character;          !!!next-input-character;
1557          redo A;          redo A;
1558        }        }
1559      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1560        my $before_leave = sub {        my $before_leave = sub {
1561          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1562              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1563            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1564            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1565              ## Discard $self->{ca} # MUST
1566          } else {          } else {
1567            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            !!!cp (58);
1568              = $self->{current_attribute};            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1569                = $self->{ca};
1570          }          }
1571        }; # $before_leave        }; # $before_leave
1572    
1573        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1574            $self->{next_input_character} == 0x000A or # LF          !!!cp (59);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1575          $before_leave->();          $before_leave->();
1576          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1577          !!!next-input-character;          !!!next-input-character;
1578          redo A;          redo A;
1579        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1580            !!!cp (60);
1581          $before_leave->();          $before_leave->();
1582          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1583          !!!next-input-character;          !!!next-input-character;
1584          redo A;          redo A;
1585        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1586          $before_leave->();          $before_leave->();
1587          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1588            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (61);
1589          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1590            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1591            if ($self->{current_token}->{attributes}) {            !!!cp (62);
1592              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1593              if ($self->{ct}->{attributes}) {
1594              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1595            }            }
1596          } else {          } else {
1597            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1598          }          }
1599          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1600          !!!next-input-character;          !!!next-input-character;
1601    
1602          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1603    
1604          redo A;          redo A;
1605        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1606                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1607          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1608            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1609          ## Stay in the state          ## Stay in the state
1610          !!!next-input-character;          !!!next-input-character;
1611          redo A;          redo A;
1612        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1613            !!!cp (64);
1614          $before_leave->();          $before_leave->();
1615            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1616          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1617          redo A;          redo A;
1618        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1619          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1620          $before_leave->();          $before_leave->();
1621          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1622            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (66);
1623          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1624            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1625            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1626              if ($self->{ct}->{attributes}) {
1627                !!!cp (67);
1628              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1629              } else {
1630                ## NOTE: This state should never be reached.
1631                !!!cp (68);
1632            }            }
1633          } else {          } else {
1634            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1635          }          }
1636          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1637          # reconsume          # reconsume
1638    
1639          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1640    
1641          redo A;          redo A;
1642        } else {        } else {
1643          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1644                $self->{nc} == 0x0027) { # '
1645              !!!cp (69);
1646              !!!parse-error (type => 'bad attribute name');
1647            } else {
1648              !!!cp (70);
1649            }
1650            $self->{ca}->{name} .= chr ($self->{nc});
1651          ## Stay in the state          ## Stay in the state
1652          !!!next-input-character;          !!!next-input-character;
1653          redo A;          redo A;
1654        }        }
1655      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1656        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1657            $self->{next_input_character} == 0x000A or # LF          !!!cp (71);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
1658          ## Stay in the state          ## Stay in the state
1659          !!!next-input-character;          !!!next-input-character;
1660          redo A;          redo A;
1661        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1662          $self->{state} = 'before attribute value';          !!!cp (72);
1663            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1664          !!!next-input-character;          !!!next-input-character;
1665          redo A;          redo A;
1666        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1667          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1668            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (73);
1669          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1670            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1671            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1672              if ($self->{ct}->{attributes}) {
1673                !!!cp (74);
1674              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1675              } else {
1676                ## NOTE: This state should never be reached.
1677                !!!cp (75);
1678            }            }
1679          } else {          } else {
1680            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1681          }          }
1682          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1683          !!!next-input-character;          !!!next-input-character;
1684    
1685          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1686    
1687          redo A;          redo A;
1688        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1689                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1690          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1691                                value => ''};          $self->{ca}
1692          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1693                   value => '',
1694                   line => $self->{line}, column => $self->{column}};
1695            $self->{state} = ATTRIBUTE_NAME_STATE;
1696          !!!next-input-character;          !!!next-input-character;
1697          redo A;          redo A;
1698        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1699            !!!cp (77);
1700            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1701          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1702          redo A;          redo A;
1703        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1704          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1705          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1706            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (79);
1707          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1708            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1709            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1710              if ($self->{ct}->{attributes}) {
1711                !!!cp (80);
1712              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1713              } else {
1714                ## NOTE: This state should never be reached.
1715                !!!cp (81);
1716            }            }
1717          } else {          } else {
1718            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1719          }          }
1720          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1721          # reconsume          # reconsume
1722    
1723          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1724    
1725          redo A;          redo A;
1726        } else {        } else {
1727          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1728                                value => ''};              $self->{nc} == 0x0027) { # '
1729          $self->{state} = 'attribute name';            !!!cp (78);
1730              !!!parse-error (type => 'bad attribute name');
1731            } else {
1732              !!!cp (82);
1733            }
1734            $self->{ca}
1735                = {name => chr ($self->{nc}),
1736                   value => '',
1737                   line => $self->{line}, column => $self->{column}};
1738            $self->{state} = ATTRIBUTE_NAME_STATE;
1739          !!!next-input-character;          !!!next-input-character;
1740          redo A;                  redo A;        
1741        }        }
1742      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1743        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1744            $self->{next_input_character} == 0x000A or # LF          !!!cp (83);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP        
1745          ## Stay in the state          ## Stay in the state
1746          !!!next-input-character;          !!!next-input-character;
1747          redo A;          redo A;
1748        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1749          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1750            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1751          !!!next-input-character;          !!!next-input-character;
1752          redo A;          redo A;
1753        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1754          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1755            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1756          ## reconsume          ## reconsume
1757          redo A;          redo A;
1758        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1759          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1760            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1761          !!!next-input-character;          !!!next-input-character;
1762          redo A;          redo A;
1763        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1764          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1765            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1766          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (87);
1767            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
1768            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1769              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1770              if ($self->{ct}->{attributes}) {
1771                !!!cp (88);
1772              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1773              } else {
1774                ## NOTE: This state should never be reached.
1775                !!!cp (89);
1776            }            }
1777          } else {          } else {
1778            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1779          }          }
1780          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1781          !!!next-input-character;          !!!next-input-character;
1782    
1783          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1784    
1785          redo A;          redo A;
1786        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1787          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1788          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1789            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (90);
1790          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1791            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1792            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1793              if ($self->{ct}->{attributes}) {
1794                !!!cp (91);
1795              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1796              } else {
1797                ## NOTE: This state should never be reached.
1798                !!!cp (92);
1799            }            }
1800          } else {          } else {
1801            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1802          }          }
1803          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1804          ## reconsume          ## reconsume
1805    
1806          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1807    
1808          redo A;          redo A;
1809        } else {        } else {
1810          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1811          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1812              !!!parse-error (type => 'bad attribute value');
1813            } else {
1814              !!!cp (94);
1815            }
1816            $self->{ca}->{value} .= chr ($self->{nc});
1817            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1818          !!!next-input-character;          !!!next-input-character;
1819          redo A;          redo A;
1820        }        }
1821      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1822        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1823          $self->{state} = 'before attribute name';          !!!cp (95);
1824            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1825          !!!next-input-character;          !!!next-input-character;
1826          redo A;          redo A;
1827        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1828          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1829          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1830            ## "entity in attribute value state".  In this implementation, the
1831            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1832            ## implementation of the "consume a character reference" algorithm.
1833            $self->{prev_state} = $self->{state};
1834            $self->{entity_add} = 0x0022; # "
1835            $self->{state} = ENTITY_STATE;
1836          !!!next-input-character;          !!!next-input-character;
1837          redo A;          redo A;
1838        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1839          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1840          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1841            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (97);
1842          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1843            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1844            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1845              if ($self->{ct}->{attributes}) {
1846                !!!cp (98);
1847              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1848              } else {
1849                ## NOTE: This state should never be reached.
1850                !!!cp (99);
1851            }            }
1852          } else {          } else {
1853            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1854          }          }
1855          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1856          ## reconsume          ## reconsume
1857    
1858          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1859    
1860          redo A;          redo A;
1861        } else {        } else {
1862          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1863            $self->{ca}->{value} .= chr ($self->{nc});
1864            $self->{read_until}->($self->{ca}->{value},
1865                                  q["&],
1866                                  length $self->{ca}->{value});
1867    
1868          ## Stay in the state          ## Stay in the state
1869          !!!next-input-character;          !!!next-input-character;
1870          redo A;          redo A;
1871        }        }
1872      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1873        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1874          $self->{state} = 'before attribute name';          !!!cp (101);
1875            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1876            !!!next-input-character;
1877            redo A;
1878          } elsif ($self->{nc} == 0x0026) { # &
1879            !!!cp (102);
1880            ## NOTE: In the spec, the tokenizer is switched to the
1881            ## "entity in attribute value state".  In this implementation, the
1882            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1883            ## implementation of the "consume a character reference" algorithm.
1884            $self->{entity_add} = 0x0027; # '
1885            $self->{prev_state} = $self->{state};
1886            $self->{state} = ENTITY_STATE;
1887          !!!next-input-character;          !!!next-input-character;
1888          redo A;          redo A;
1889        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == -1) {
         $self->{last_attribute_value_state} = 'attribute value (single-quoted)';  
         $self->{state} = 'entity in attribute value';  
         !!!next-input-character;  
         redo A;  
       } elsif ($self->{next_input_character} == -1) {  
1890          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1891          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1892            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (103);
1893          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1894            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1895            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1896              if ($self->{ct}->{attributes}) {
1897                !!!cp (104);
1898              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1899              } else {
1900                ## NOTE: This state should never be reached.
1901                !!!cp (105);
1902            }            }
1903          } else {          } else {
1904            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1905          }          }
1906          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1907          ## reconsume          ## reconsume
1908    
1909          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1910    
1911          redo A;          redo A;
1912        } else {        } else {
1913          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1914            $self->{ca}->{value} .= chr ($self->{nc});
1915            $self->{read_until}->($self->{ca}->{value},
1916                                  q['&],
1917                                  length $self->{ca}->{value});
1918    
1919          ## Stay in the state          ## Stay in the state
1920          !!!next-input-character;          !!!next-input-character;
1921          redo A;          redo A;
1922        }        }
1923      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1924        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1925            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1926            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1927            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1928            $self->{next_input_character} == 0x0020) { # SP          redo A;
1929          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1930          !!!next-input-character;          !!!cp (108);
1931          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1932        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1933          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1934          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1935          !!!next-input-character;          $self->{entity_add} = -1;
1936          redo A;          $self->{prev_state} = $self->{state};
1937        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1938          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1939            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          redo A;
1940          } elsif ($self->{current_token}->{type} eq 'end tag') {        } elsif ($self->{nc} == 0x003E) { # >
1941            $self->{content_model_flag} = 'PCDATA'; # MUST          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1942            if ($self->{current_token}->{attributes}) {            !!!cp (109);
1943              $self->{last_stag_name} = $self->{ct}->{tag_name};
1944            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1945              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1946              if ($self->{ct}->{attributes}) {
1947                !!!cp (110);
1948              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1949              } else {
1950                ## NOTE: This state should never be reached.
1951                !!!cp (111);
1952            }            }
1953          } else {          } else {
1954            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1955          }          }
1956          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1957          !!!next-input-character;          !!!next-input-character;
1958    
1959          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1960    
1961          redo A;          redo A;
1962        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1963          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1964          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1965            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            !!!cp (112);
1966          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{last_stag_name} = $self->{ct}->{tag_name};
1967            $self->{content_model_flag} = 'PCDATA'; # MUST          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1968            if ($self->{current_token}->{attributes}) {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1969              if ($self->{ct}->{attributes}) {
1970                !!!cp (113);
1971              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1972              } else {
1973                ## NOTE: This state should never be reached.
1974                !!!cp (114);
1975            }            }
1976          } else {          } else {
1977            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1978          }          }
1979          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1980          ## reconsume          ## reconsume
1981    
1982          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1983    
1984          redo A;          redo A;
1985        } else {        } else {
1986          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1987                 0x0022 => 1, # "
1988                 0x0027 => 1, # '
1989                 0x003D => 1, # =
1990                }->{$self->{nc}}) {
1991              !!!cp (115);
1992              !!!parse-error (type => 'bad attribute value');
1993            } else {
1994              !!!cp (116);
1995            }
1996            $self->{ca}->{value} .= chr ($self->{nc});
1997            $self->{read_until}->($self->{ca}->{value},
1998                                  q["'=& >],
1999                                  length $self->{ca}->{value});
2000    
2001          ## Stay in the state          ## Stay in the state
2002          !!!next-input-character;          !!!next-input-character;
2003          redo A;          redo A;
2004        }        }
2005      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2006        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        if ($is_space->{$self->{nc}}) {
2007            !!!cp (118);
2008            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2009            !!!next-input-character;
2010            redo A;
2011          } elsif ($self->{nc} == 0x003E) { # >
2012            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2013              !!!cp (119);
2014              $self->{last_stag_name} = $self->{ct}->{tag_name};
2015            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2016              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2017              if ($self->{ct}->{attributes}) {
2018                !!!cp (120);
2019                !!!parse-error (type => 'end tag attribute');
2020              } else {
2021                ## NOTE: This state should never be reached.
2022                !!!cp (121);
2023              }
2024            } else {
2025              die "$0: $self->{ct}->{type}: Unknown token type";
2026            }
2027            $self->{state} = DATA_STATE;
2028            !!!next-input-character;
2029    
2030            !!!emit ($self->{ct}); # start tag or end tag
2031    
2032        unless (defined $token) {          redo A;
2033          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2034            !!!cp (122);
2035            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2036            !!!next-input-character;
2037            redo A;
2038          } elsif ($self->{nc} == -1) {
2039            !!!parse-error (type => 'unclosed tag');
2040            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2041              !!!cp (122.3);
2042              $self->{last_stag_name} = $self->{ct}->{tag_name};
2043            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2044              if ($self->{ct}->{attributes}) {
2045                !!!cp (122.1);
2046                !!!parse-error (type => 'end tag attribute');
2047              } else {
2048                ## NOTE: This state should never be reached.
2049                !!!cp (122.2);
2050              }
2051            } else {
2052              die "$0: $self->{ct}->{type}: Unknown token type";
2053            }
2054            $self->{state} = DATA_STATE;
2055            ## Reconsume.
2056            !!!emit ($self->{ct}); # start tag or end tag
2057            redo A;
2058        } else {        } else {
2059          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2060          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2061            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2062            ## reconsume
2063            redo A;
2064        }        }
2065        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2066          if ($self->{nc} == 0x003E) { # >
2067            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2068              !!!cp ('124.2');
2069              !!!parse-error (type => 'nestc', token => $self->{ct});
2070              ## TODO: Different type than slash in start tag
2071              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2072              if ($self->{ct}->{attributes}) {
2073                !!!cp ('124.4');
2074                !!!parse-error (type => 'end tag attribute');
2075              } else {
2076                !!!cp ('124.5');
2077              }
2078              ## TODO: Test |<title></title/>|
2079            } else {
2080              !!!cp ('124.3');
2081              $self->{self_closing} = 1;
2082            }
2083    
2084        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2085        # next-input-character is already done          !!!next-input-character;
       redo A;  
     } elsif ($self->{state} eq 'bogus comment') {  
       ## (only happen if PCDATA state)  
         
       my $token = {type => 'comment', data => ''};  
   
       BC: {  
         if ($self->{next_input_character} == 0x003E) { # >  
           $self->{state} = 'data';  
           !!!next-input-character;  
   
           !!!emit ($token);  
   
           redo A;  
         } elsif ($self->{next_input_character} == -1) {  
           $self->{state} = 'data';  
           ## reconsume  
2086    
2087            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2088    
2089            redo A;          redo A;
2090          } elsif ($self->{nc} == -1) {
2091            !!!parse-error (type => 'unclosed tag');
2092            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2093              !!!cp (124.7);
2094              $self->{last_stag_name} = $self->{ct}->{tag_name};
2095            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2096              if ($self->{ct}->{attributes}) {
2097                !!!cp (124.5);
2098                !!!parse-error (type => 'end tag attribute');
2099              } else {
2100                ## NOTE: This state should never be reached.
2101                !!!cp (124.6);
2102              }
2103          } else {          } else {
2104            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2105          }          }
2106        } # BC          $self->{state} = DATA_STATE;
2107      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2108            !!!emit ($self->{ct}); # start tag or end tag
2109            redo A;
2110          } else {
2111            !!!cp ('124.4');
2112            !!!parse-error (type => 'nestc');
2113            ## TODO: This error type is wrong.
2114            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2115            ## Reconsume.
2116            redo A;
2117          }
2118        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2119        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2120    
2121        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2122        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2123                
2124        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2125            !!!cp (124);
2126            $self->{state} = DATA_STATE;
2127          !!!next-input-character;          !!!next-input-character;
2128          push @next_char, $self->{next_input_character};  
2129          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2130            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2131            $self->{state} = 'comment start';        } elsif ($self->{nc} == -1) {
2132            !!!next-input-character;          !!!cp (125);
2133            redo A;          $self->{state} = DATA_STATE;
2134          }          ## reconsume
2135        } elsif ($self->{next_input_character} == 0x0044 or # D  
2136                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2137            redo A;
2138          } else {
2139            !!!cp (126);
2140            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2141            $self->{read_until}->($self->{ct}->{data},
2142                                  q[>],
2143                                  length $self->{ct}->{data});
2144    
2145            ## Stay in the state.
2146          !!!next-input-character;          !!!next-input-character;
2147          push @next_char, $self->{next_input_character};          redo A;
2148          if ($self->{next_input_character} == 0x004F or # O        }
2149              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2150            !!!next-input-character;        ## (only happen if PCDATA state)
2151            push @next_char, $self->{next_input_character};        
2152            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2153                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2154              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2155              push @next_char, $self->{next_input_character};          !!!next-input-character;
2156              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2157                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2158                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2159                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2160                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2161                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2162                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2163                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2164                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2165                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2166                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2167                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2168                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2169                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2170                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2171                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2172                      !!!next-input-character;          redo A;
2173                      redo A;        } else {
2174                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2175        }        }
2176    
2177        !!!parse-error (type => 'bogus comment open');        !!!parse-error (type => 'bogus comment',
2178        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2179        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2180        $self->{state} = 'bogus comment';        ## Reconsume.
2181          $self->{state} = BOGUS_COMMENT_STATE;
2182          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2183                                    line => $self->{line_prev},
2184                                    column => $self->{column_prev} - 1,
2185                                   };
2186        redo A;        redo A;
2187              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2188        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2189        ## ISSUE: spec is somewhat unclear on "is the first character that will be in the comment"; what is "that will be in the comment" is what the algorithm defines, isn't it?          !!!cp (127);
2190      } elsif ($self->{state} eq 'comment start') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2192          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2193                                     };
2194            $self->{state} = COMMENT_START_STATE;
2195          !!!next-input-character;          !!!next-input-character;
2196          redo A;          redo A;
2197        } elsif ($self->{next_input_character} == 0x003E) { # >        } else {
2198            !!!cp (128);
2199            !!!parse-error (type => 'bogus comment',
2200                            line => $self->{line_prev},
2201                            column => $self->{column_prev} - 2);
2202            $self->{state} = BOGUS_COMMENT_STATE;
2203            ## Reconsume.
2204            $self->{ct} = {type => COMMENT_TOKEN,
2205                                      data => '-',
2206                                      line => $self->{line_prev},
2207                                      column => $self->{column_prev} - 2,
2208                                     };
2209            redo A;
2210          }
2211        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2212          ## ASCII case-insensitive.
2213          if ($self->{nc} == [
2214                undef,
2215                0x004F, # O
2216                0x0043, # C
2217                0x0054, # T
2218                0x0059, # Y
2219                0x0050, # P
2220              ]->[length $self->{s_kwd}] or
2221              $self->{nc} == [
2222                undef,
2223                0x006F, # o
2224                0x0063, # c
2225                0x0074, # t
2226                0x0079, # y
2227                0x0070, # p
2228              ]->[length $self->{s_kwd}]) {
2229            !!!cp (131);
2230            ## Stay in the state.
2231            $self->{s_kwd} .= chr $self->{nc};
2232            !!!next-input-character;
2233            redo A;
2234          } elsif ((length $self->{s_kwd}) == 6 and
2235                   ($self->{nc} == 0x0045 or # E
2236                    $self->{nc} == 0x0065)) { # e
2237            !!!cp (129);
2238            $self->{state} = DOCTYPE_STATE;
2239            $self->{ct} = {type => DOCTYPE_TOKEN,
2240                                      quirks => 1,
2241                                      line => $self->{line_prev},
2242                                      column => $self->{column_prev} - 7,
2243                                     };
2244            !!!next-input-character;
2245            redo A;
2246          } else {
2247            !!!cp (132);        
2248            !!!parse-error (type => 'bogus comment',
2249                            line => $self->{line_prev},
2250                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2251            $self->{state} = BOGUS_COMMENT_STATE;
2252            ## Reconsume.
2253            $self->{ct} = {type => COMMENT_TOKEN,
2254                                      data => $self->{s_kwd},
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2257                                     };
2258            redo A;
2259          }
2260        } elsif ($self->{state} == MD_CDATA_STATE) {
2261          if ($self->{nc} == {
2262                '[' => 0x0043, # C
2263                '[C' => 0x0044, # D
2264                '[CD' => 0x0041, # A
2265                '[CDA' => 0x0054, # T
2266                '[CDAT' => 0x0041, # A
2267              }->{$self->{s_kwd}}) {
2268            !!!cp (135.1);
2269            ## Stay in the state.
2270            $self->{s_kwd} .= chr $self->{nc};
2271            !!!next-input-character;
2272            redo A;
2273          } elsif ($self->{s_kwd} eq '[CDATA' and
2274                   $self->{nc} == 0x005B) { # [
2275            !!!cp (135.2);
2276            $self->{ct} = {type => CHARACTER_TOKEN,
2277                                      data => '',
2278                                      line => $self->{line_prev},
2279                                      column => $self->{column_prev} - 7};
2280            $self->{state} = CDATA_SECTION_STATE;
2281            !!!next-input-character;
2282            redo A;
2283          } else {
2284            !!!cp (135.3);
2285            !!!parse-error (type => 'bogus comment',
2286                            line => $self->{line_prev},
2287                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2288            $self->{state} = BOGUS_COMMENT_STATE;
2289            ## Reconsume.
2290            $self->{ct} = {type => COMMENT_TOKEN,
2291                                      data => $self->{s_kwd},
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2294                                     };
2295            redo A;
2296          }
2297        } elsif ($self->{state} == COMMENT_START_STATE) {
2298          if ($self->{nc} == 0x002D) { # -
2299            !!!cp (137);
2300            $self->{state} = COMMENT_START_DASH_STATE;
2301            !!!next-input-character;
2302            redo A;
2303          } elsif ($self->{nc} == 0x003E) { # >
2304            !!!cp (138);
2305          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2306          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2307          !!!next-input-character;          !!!next-input-character;
2308    
2309          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2310    
2311          redo A;          redo A;
2312        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2313            !!!cp (139);
2314          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2315          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2316          ## reconsume          ## reconsume
2317    
2318          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2319    
2320          redo A;          redo A;
2321        } else {        } else {
2322          $self->{current_token}->{data} # comment          !!!cp (140);
2323              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2324          $self->{state} = 'comment';              .= chr ($self->{nc});
2325            $self->{state} = COMMENT_STATE;
2326          !!!next-input-character;          !!!next-input-character;
2327          redo A;          redo A;
2328        }        }
2329      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2330        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2331          $self->{state} = 'comment end';          !!!cp (141);
2332            $self->{state} = COMMENT_END_STATE;
2333          !!!next-input-character;          !!!next-input-character;
2334          redo A;          redo A;
2335        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2336            !!!cp (142);
2337          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2338          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2339          !!!next-input-character;          !!!next-input-character;
2340    
2341          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2342    
2343          redo A;          redo A;
2344        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2345            !!!cp (143);
2346          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2347          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2348          ## reconsume          ## reconsume
2349    
2350          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2351    
2352          redo A;          redo A;
2353        } else {        } else {
2354          $self->{current_token}->{data} # comment          !!!cp (144);
2355              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2356          $self->{state} = 'comment';              .= '-' . chr ($self->{nc});
2357            $self->{state} = COMMENT_STATE;
2358          !!!next-input-character;          !!!next-input-character;
2359          redo A;          redo A;
2360        }        }
2361      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2362        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2363          $self->{state} = 'comment end dash';          !!!cp (145);
2364            $self->{state} = COMMENT_END_DASH_STATE;
2365          !!!next-input-character;          !!!next-input-character;
2366          redo A;          redo A;
2367        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2368            !!!cp (146);
2369          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2370          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2371          ## reconsume          ## reconsume
2372    
2373          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2374    
2375          redo A;          redo A;
2376        } else {        } else {
2377          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2378            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2379            $self->{read_until}->($self->{ct}->{data},
2380                                  q[-],
2381                                  length $self->{ct}->{data});
2382    
2383          ## Stay in the state          ## Stay in the state
2384          !!!next-input-character;          !!!next-input-character;
2385          redo A;          redo A;
2386        }        }
2387      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2388        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2389          $self->{state} = 'comment end';          !!!cp (148);
2390            $self->{state} = COMMENT_END_STATE;
2391          !!!next-input-character;          !!!next-input-character;
2392          redo A;          redo A;
2393        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2394            !!!cp (149);
2395          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2396          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2397          ## reconsume          ## reconsume
2398    
2399          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2400    
2401          redo A;          redo A;
2402        } else {        } else {
2403          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2404          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2405            $self->{state} = COMMENT_STATE;
2406          !!!next-input-character;          !!!next-input-character;
2407          redo A;          redo A;
2408        }        }
2409      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2410        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2411          $self->{state} = 'data';          !!!cp (151);
2412            $self->{state} = DATA_STATE;
2413          !!!next-input-character;          !!!next-input-character;
2414    
2415          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2416    
2417          redo A;          redo A;
2418        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2419          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2420          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2421                            line => $self->{line_prev},
2422                            column => $self->{column_prev});
2423            $self->{ct}->{data} .= '-'; # comment
2424          ## Stay in the state          ## Stay in the state
2425          !!!next-input-character;          !!!next-input-character;
2426          redo A;          redo A;
2427        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2428            !!!cp (153);
2429          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2430          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2431          ## reconsume          ## reconsume
2432    
2433          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2434    
2435          redo A;          redo A;
2436        } else {        } else {
2437          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2438          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2439          $self->{state} = 'comment';                          line => $self->{line_prev},
2440                            column => $self->{column_prev});
2441            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2442            $self->{state} = COMMENT_STATE;
2443          !!!next-input-character;          !!!next-input-character;
2444          redo A;          redo A;
2445        }        }
2446      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2447        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2448            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2449            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'before DOCTYPE name';  
2450          !!!next-input-character;          !!!next-input-character;
2451          redo A;          redo A;
2452        } else {        } else {
2453            !!!cp (156);
2454          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2455          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2456          ## reconsume          ## reconsume
2457          redo A;          redo A;
2458        }        }
2459      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2460        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2461            $self->{next_input_character} == 0x000A or # LF          !!!cp (157);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2462          ## Stay in the state          ## Stay in the state
2463          !!!next-input-character;          !!!next-input-character;
2464          redo A;          redo A;
2465        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2466            !!!cp (158);
2467          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2468          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2469          !!!next-input-character;          !!!next-input-character;
2470    
2471          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2472    
2473          redo A;          redo A;
2474        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2475            !!!cp (159);
2476          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2477          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2478          ## reconsume          ## reconsume
2479    
2480          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2481    
2482          redo A;          redo A;
2483        } else {        } else {
2484          $self->{current_token}          !!!cp (160);
2485              = {type => 'DOCTYPE',          $self->{ct}->{name} = chr $self->{nc};
2486                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
2487                 correct => 1};          $self->{state} = DOCTYPE_NAME_STATE;
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = 'DOCTYPE name';  
2488          !!!next-input-character;          !!!next-input-character;
2489          redo A;          redo A;
2490        }        }
2491      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2492  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2493        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2494            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
2495            $self->{next_input_character} == 0x000B or # VT          $self->{state} = AFTER_DOCTYPE_NAME_STATE;
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
         $self->{state} = 'after DOCTYPE name';  
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2499          $self->{state} = 'data';          !!!cp (162);
2500            $self->{state} = DATA_STATE;
2501          !!!next-input-character;          !!!next-input-character;
2502    
2503          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2504    
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2507            !!!cp (163);
2508          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2509          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2510          ## reconsume          ## reconsume
2511    
2512          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2513          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2514    
2515          redo A;          redo A;
2516        } else {        } else {
2517          $self->{current_token}->{name}          !!!cp (164);
2518            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2519              .= chr ($self->{nc}); # DOCTYPE
2520          ## Stay in the state          ## Stay in the state
2521          !!!next-input-character;          !!!next-input-character;
2522          redo A;          redo A;
2523        }        }
2524      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2525        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2526            $self->{next_input_character} == 0x000A or # LF          !!!cp (165);
           $self->{next_input_character} == 0x000B or # VT  
           $self->{next_input_character} == 0x000C or # FF  
           $self->{next_input_character} == 0x0020) { # SP  
2527          ## Stay in the state          ## Stay in the state
2528          !!!next-input-character;          !!!next-input-character;
2529          redo A;          redo A;
2530        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2531          $self->{state} = 'data';          !!!cp (166);
2532            $self->{state} = DATA_STATE;
2533          !!!next-input-character;          !!!next-input-character;
2534    
2535          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2536    
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2539            !!!cp (167);
2540          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2541          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2542          ## reconsume          ## reconsume
2543    
2544          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2545          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2546    
2547          redo A;          redo A;
2548        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2549                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2550            $self->{state} = PUBLIC_STATE;
2551            $self->{s_kwd} = chr $self->{nc};
2552          !!!next-input-character;          !!!next-input-character;
2553          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2554              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2555            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2556            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2557                $self->{next_input_character} == 0x0062) { # b          $self->{s_kwd} = chr $self->{nc};
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x004C or # L  
                 $self->{next_input_character} == 0x006C) { # l  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0049 or # I  
                   $self->{next_input_character} == 0x0069) { # i  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x0043 or # C  
                     $self->{next_input_character} == 0x0063) { # c  
                   $self->{state} = 'before DOCTYPE public identifier';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
       } elsif ($self->{next_input_character} == 0x0053 or # S  
                $self->{next_input_character} == 0x0073) { # s  
2558          !!!next-input-character;          !!!next-input-character;
2559          if ($self->{next_input_character} == 0x0059 or # Y          redo A;
             $self->{next_input_character} == 0x0079) { # y  
           !!!next-input-character;  
           if ($self->{next_input_character} == 0x0053 or # S  
               $self->{next_input_character} == 0x0073) { # s  
             !!!next-input-character;  
             if ($self->{next_input_character} == 0x0054 or # T  
                 $self->{next_input_character} == 0x0074) { # t  
               !!!next-input-character;  
               if ($self->{next_input_character} == 0x0045 or # E  
                   $self->{next_input_character} == 0x0065) { # e  
                 !!!next-input-character;  
                 if ($self->{next_input_character} == 0x004D or # M  
                     $self->{next_input_character} == 0x006D) { # m  
                   $self->{state} = 'before DOCTYPE system identifier';  
                   !!!next-input-character;  
                   redo A;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2560        } else {        } else {
2561            !!!cp (180);
2562            !!!parse-error (type => 'string after DOCTYPE name');
2563            $self->{ct}->{quirks} = 1;
2564    
2565            $self->{state} = BOGUS_DOCTYPE_STATE;
2566          !!!next-input-character;          !!!next-input-character;
2567          #          redo A;
2568        }        }
2569        } elsif ($self->{state} == PUBLIC_STATE) {
2570        !!!parse-error (type => 'string after DOCTYPE name');        ## ASCII case-insensitive
2571        $self->{state} = 'bogus DOCTYPE';        if ($self->{nc} == [
2572        # next-input-character is already done              undef,
2573        redo A;              0x0055, # U
2574      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {              0x0042, # B
2575        if ({              0x004C, # L
2576              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0049, # I
2577              #0x000D => 1, # HT, LF, VT, FF, SP, CR            ]->[length $self->{s_kwd}] or
2578            }->{$self->{next_input_character}}) {            $self->{nc} == [
2579                undef,
2580                0x0075, # u
2581                0x0062, # b
2582                0x006C, # l
2583                0x0069, # i
2584              ]->[length $self->{s_kwd}]) {
2585            !!!cp (175);
2586            ## Stay in the state.
2587            $self->{s_kwd} .= chr $self->{nc};
2588            !!!next-input-character;
2589            redo A;
2590          } elsif ((length $self->{s_kwd}) == 5 and
2591                   ($self->{nc} == 0x0043 or # C
2592                    $self->{nc} == 0x0063)) { # c
2593            !!!cp (168);
2594            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2595            !!!next-input-character;
2596            redo A;
2597          } else {
2598            !!!cp (169);
2599            !!!parse-error (type => 'string after DOCTYPE name',
2600                            line => $self->{line_prev},
2601                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2602            $self->{ct}->{quirks} = 1;
2603    
2604            $self->{state} = BOGUS_DOCTYPE_STATE;
2605            ## Reconsume.
2606            redo A;
2607          }
2608        } elsif ($self->{state} == SYSTEM_STATE) {
2609          ## ASCII case-insensitive
2610          if ($self->{nc} == [
2611                undef,
2612                0x0059, # Y
2613                0x0053, # S
2614                0x0054, # T
2615                0x0045, # E
2616              ]->[length $self->{s_kwd}] or
2617              $self->{nc} == [
2618                undef,
2619                0x0079, # y
2620                0x0073, # s
2621                0x0074, # t
2622                0x0065, # e
2623              ]->[length $self->{s_kwd}]) {
2624            !!!cp (170);
2625            ## Stay in the state.
2626            $self->{s_kwd} .= chr $self->{nc};
2627            !!!next-input-character;
2628            redo A;
2629          } elsif ((length $self->{s_kwd}) == 5 and
2630                   ($self->{nc} == 0x004D or # M
2631                    $self->{nc} == 0x006D)) { # m
2632            !!!cp (171);
2633            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2634            !!!next-input-character;
2635            redo A;
2636          } else {
2637            !!!cp (172);
2638            !!!parse-error (type => 'string after DOCTYPE name',
2639                            line => $self->{line_prev},
2640                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2641            $self->{ct}->{quirks} = 1;
2642    
2643            $self->{state} = BOGUS_DOCTYPE_STATE;
2644            ## Reconsume.
2645            redo A;
2646          }
2647        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2648          if ($is_space->{$self->{nc}}) {
2649            !!!cp (181);
2650          ## Stay in the state          ## Stay in the state
2651          !!!next-input-character;          !!!next-input-character;
2652          redo A;          redo A;
2653        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2654          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2655          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2656            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2657          !!!next-input-character;          !!!next-input-character;
2658          redo A;          redo A;
2659        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2660          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2661          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2662            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2663          !!!next-input-character;          !!!next-input-character;
2664          redo A;          redo A;
2665        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2666            !!!cp (184);
2667          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2668    
2669          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2670          !!!next-input-character;          !!!next-input-character;
2671    
2672          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2673          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2674    
2675          redo A;          redo A;
2676        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2677            !!!cp (185);
2678          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2679    
2680          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2681          ## reconsume          ## reconsume
2682    
2683          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2684          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2685    
2686          redo A;          redo A;
2687        } else {        } else {
2688            !!!cp (186);
2689          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2690          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2691    
2692            $self->{state} = BOGUS_DOCTYPE_STATE;
2693          !!!next-input-character;          !!!next-input-character;
2694          redo A;          redo A;
2695        }        }
2696      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2697        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2698          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (187);
2699            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2700          !!!next-input-character;          !!!next-input-character;
2701          redo A;          redo A;
2702        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2703            !!!cp (188);
2704          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2705    
2706          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2707            !!!next-input-character;
2708    
2709            $self->{ct}->{quirks} = 1;
2710            !!!emit ($self->{ct}); # DOCTYPE
2711    
2712            redo A;
2713          } elsif ($self->{nc} == -1) {
2714            !!!cp (189);
2715            !!!parse-error (type => 'unclosed PUBLIC literal');
2716    
2717            $self->{state} = DATA_STATE;
2718          ## reconsume          ## reconsume
2719    
2720          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2721          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2722    
2723          redo A;          redo A;
2724        } else {        } else {
2725          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2726              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2727                .= chr $self->{nc};
2728            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2729                                  length $self->{ct}->{pubid});
2730    
2731          ## Stay in the state          ## Stay in the state
2732          !!!next-input-character;          !!!next-input-character;
2733          redo A;          redo A;
2734        }        }
2735      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2736        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2737          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2738            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2739          !!!next-input-character;          !!!next-input-character;
2740          redo A;          redo A;
2741        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2742            !!!cp (192);
2743            !!!parse-error (type => 'unclosed PUBLIC literal');
2744    
2745            $self->{state} = DATA_STATE;
2746            !!!next-input-character;
2747    
2748            $self->{ct}->{quirks} = 1;
2749            !!!emit ($self->{ct}); # DOCTYPE
2750    
2751            redo A;
2752          } elsif ($self->{nc} == -1) {
2753            !!!cp (193);
2754          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2755    
2756          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2757          ## reconsume          ## reconsume
2758    
2759          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2760          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2761    
2762          redo A;          redo A;
2763        } else {        } else {
2764          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2765              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2766                .= chr $self->{nc};
2767            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2768                                  length $self->{ct}->{pubid});
2769    
2770          ## Stay in the state          ## Stay in the state
2771          !!!next-input-character;          !!!next-input-character;
2772          redo A;          redo A;
2773        }        }
2774      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2775        if ({        if ($is_space->{$self->{nc}}) {
2776              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2777          ## Stay in the state          ## Stay in the state
2778          !!!next-input-character;          !!!next-input-character;
2779          redo A;          redo A;
2780        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2781          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2782          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2783            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2784          !!!next-input-character;          !!!next-input-character;
2785          redo A;          redo A;
2786        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2787          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2788          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2789            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2790          !!!next-input-character;          !!!next-input-character;
2791          redo A;          redo A;
2792        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2793          $self->{state} = 'data';          !!!cp (198);
2794            $self->{state} = DATA_STATE;
2795          !!!next-input-character;          !!!next-input-character;
2796    
2797          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2798    
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2801            !!!cp (199);
2802          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2803    
2804          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2805          ## reconsume          ## reconsume
2806    
2807          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2808          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2809    
2810          redo A;          redo A;
2811        } else {        } else {
2812            !!!cp (200);
2813          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2814          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2815    
2816            $self->{state} = BOGUS_DOCTYPE_STATE;
2817          !!!next-input-character;          !!!next-input-character;
2818          redo A;          redo A;
2819        }        }
2820      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2821        if ({        if ($is_space->{$self->{nc}}) {
2822              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2823          ## Stay in the state          ## Stay in the state
2824          !!!next-input-character;          !!!next-input-character;
2825          redo A;          redo A;
2826        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2827          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2828          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2829            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2830          !!!next-input-character;          !!!next-input-character;
2831          redo A;          redo A;
2832        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2833          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2834          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2835            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2836          !!!next-input-character;          !!!next-input-character;
2837          redo A;          redo A;
2838        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2839            !!!cp (204);
2840          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2841          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2842          !!!next-input-character;          !!!next-input-character;
2843    
2844          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2845          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2846    
2847          redo A;          redo A;
2848        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2849            !!!cp (205);
2850          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2851    
2852          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2853          ## reconsume          ## reconsume
2854    
2855          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2856          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2857    
2858          redo A;          redo A;
2859        } else {        } else {
2860          !!!parse-error (type => 'string after PUBLIC literal');          !!!cp (206);
2861          $self->{state} = 'bogus DOCTYPE';          !!!parse-error (type => 'string after SYSTEM');
2862            $self->{ct}->{quirks} = 1;
2863    
2864            $self->{state} = BOGUS_DOCTYPE_STATE;
2865          !!!next-input-character;          !!!next-input-character;
2866          redo A;          redo A;
2867        }        }
2868      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2869        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2870          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2871            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2872            !!!next-input-character;
2873            redo A;
2874          } elsif ($self->{nc} == 0x003E) { # >
2875            !!!cp (208);
2876            !!!parse-error (type => 'unclosed SYSTEM literal');
2877    
2878            $self->{state} = DATA_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880    
2881            $self->{ct}->{quirks} = 1;
2882            !!!emit ($self->{ct}); # DOCTYPE
2883    
2884          redo A;          redo A;
2885        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2886            !!!cp (209);
2887          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2888    
2889          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2890          ## reconsume          ## reconsume
2891    
2892          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2893          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2894    
2895          redo A;          redo A;
2896        } else {        } else {
2897          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2898              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2899                .= chr $self->{nc};
2900            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2901                                  length $self->{ct}->{sysid});
2902    
2903          ## Stay in the state          ## Stay in the state
2904          !!!next-input-character;          !!!next-input-character;
2905          redo A;          redo A;
2906        }        }
2907      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2908        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2909          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2910            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2911          !!!next-input-character;          !!!next-input-character;
2912          redo A;          redo A;
2913        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2914            !!!cp (212);
2915          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2916    
2917          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2918            !!!next-input-character;
2919    
2920            $self->{ct}->{quirks} = 1;
2921            !!!emit ($self->{ct}); # DOCTYPE
2922    
2923            redo A;
2924          } elsif ($self->{nc} == -1) {
2925            !!!cp (213);
2926            !!!parse-error (type => 'unclosed SYSTEM literal');
2927    
2928            $self->{state} = DATA_STATE;
2929          ## reconsume          ## reconsume
2930    
2931          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2932          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2933    
2934          redo A;          redo A;
2935        } else {        } else {
2936          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2937              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2938                .= chr $self->{nc};
2939            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2940                                  length $self->{ct}->{sysid});
2941    
2942          ## Stay in the state          ## Stay in the state
2943          !!!next-input-character;          !!!next-input-character;
2944          redo A;          redo A;
2945        }        }
2946      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2947        if ({        if ($is_space->{$self->{nc}}) {
2948              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2949          ## Stay in the state          ## Stay in the state
2950          !!!next-input-character;          !!!next-input-character;
2951          redo A;          redo A;
2952        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2953          $self->{state} = 'data';          !!!cp (216);
2954            $self->{state} = DATA_STATE;
2955          !!!next-input-character;          !!!next-input-character;
2956    
2957          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2958    
2959          redo A;          redo A;
2960        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2961            !!!cp (217);
2962          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2963            $self->{state} = DATA_STATE;
         $self->{state} = 'data';  
2964          ## reconsume          ## reconsume
2965    
2966          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2967          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2968    
2969          redo A;          redo A;
2970        } else {        } else {
2971            !!!cp (218);
2972          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2973          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2974    
2975            $self->{state} = BOGUS_DOCTYPE_STATE;
2976          !!!next-input-character;          !!!next-input-character;
2977          redo A;          redo A;
2978        }        }
2979      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2980        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2981          $self->{state} = 'data';          !!!cp (219);
2982            $self->{state} = DATA_STATE;
2983          !!!next-input-character;          !!!next-input-character;
2984    
2985          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2986    
2987          redo A;          redo A;
2988        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2989            !!!cp (220);
2990          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2991          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2992          ## reconsume          ## reconsume
2993    
2994          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2995    
2996          redo A;          redo A;
2997        } else {        } else {
2998            !!!cp (221);
2999            my $s = '';
3000            $self->{read_until}->($s, q[>], 0);
3001    
3002          ## Stay in the state          ## Stay in the state
3003          !!!next-input-character;          !!!next-input-character;
3004          redo A;          redo A;
3005        }        }
3006      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3007        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3008      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3009    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3010          
3011    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3012  } # _get_next_token          !!!cp (221.1);
3013            $self->{state} = CDATA_SECTION_MSE1_STATE;
3014            !!!next-input-character;
3015            redo A;
3016          } elsif ($self->{nc} == -1) {
3017            $self->{state} = DATA_STATE;
3018            !!!next-input-character;
3019            if (length $self->{ct}->{data}) { # character
3020              !!!cp (221.2);
3021              !!!emit ($self->{ct}); # character
3022            } else {
3023              !!!cp (221.3);
3024              ## No token to emit. $self->{ct} is discarded.
3025            }        
3026            redo A;
3027          } else {
3028            !!!cp (221.4);
3029            $self->{ct}->{data} .= chr $self->{nc};
3030            $self->{read_until}->($self->{ct}->{data},
3031                                  q<]>,
3032                                  length $self->{ct}->{data});
3033    
3034  sub _tokenize_attempt_to_consume_an_entity ($$) {          ## Stay in the state.
3035    my ($self, $in_attr) = @_;          !!!next-input-character;
3036            redo A;
3037          }
3038    
3039    if ({        ## ISSUE: "text tokens" in spec.
3040         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3041         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3042        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3043      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3044      ## No error          !!!next-input-character;
3045      return undef;          redo A;
3046    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3047      !!!next-input-character;          !!!cp (221.6);
3048      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3049          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3050        my $code;          ## Reconsume.
3051        X: {          redo A;
3052          my $x_char = $self->{next_input_character};        }
3053          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3054          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3055              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3056            $code ||= 0;          !!!next-input-character;
3057            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3058            $code += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3059            redo X;            !!!emit ($self->{ct}); # character
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           $self->{next_input_character} = 0x0023; # #  
           !!!back-next-input-character ($x_char);  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3060          } else {          } else {
3061            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3062              ## No token to emit. $self->{ct} is discarded.
3063          }          }
3064            redo A;
3065          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3066            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3067            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3068          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
           !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);  
           $code = 0xFFFD;  
         } elsif ($code == 0x000D) {  
           !!!parse-error (type => 'CR character reference');  
           $code = 0x000A;  
         } elsif (0x80 <= $code and $code <= 0x9F) {  
           !!!parse-error (type => sprintf 'c1 entity:U+%04X', $code);  
           $code = $c1_entity_char->{$code};  
         }  
   
         return {type => 'character', data => chr $code};  
       } # X  
     } elsif (0x0030 <= $self->{next_input_character} and  
              $self->{next_input_character} <= 0x0039) { # 0..9  
       my $code = $self->{next_input_character} - 0x0030;  
       !!!next-input-character;  
         
       while (0x0030 <= $self->{next_input_character} and  
                 $self->{next_input_character} <= 0x0039) { # 0..9  
         $code *= 10;  
         $code += $self->{next_input_character} - 0x0030;  
           
3069          !!!next-input-character;          !!!next-input-character;
3070            redo A;
3071          } else {
3072            !!!cp (221.11);
3073            $self->{ct}->{data} .= ']]'; # character
3074            $self->{state} = CDATA_SECTION_STATE;
3075            ## Reconsume.
3076            redo A;
3077          }
3078        } elsif ($self->{state} == ENTITY_STATE) {
3079          if ($is_space->{$self->{nc}} or
3080              {
3081                0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3082                $self->{entity_add} => 1,
3083              }->{$self->{nc}}) {
3084            !!!cp (1001);
3085            ## Don't consume
3086            ## No error
3087            ## Return nothing.
3088            #
3089          } elsif ($self->{nc} == 0x0023) { # #
3090            !!!cp (999);
3091            $self->{state} = ENTITY_HASH_STATE;
3092            $self->{s_kwd} = '#';
3093            !!!next-input-character;
3094            redo A;
3095          } elsif ((0x0041 <= $self->{nc} and
3096                    $self->{nc} <= 0x005A) or # A..Z
3097                   (0x0061 <= $self->{nc} and
3098                    $self->{nc} <= 0x007A)) { # a..z
3099            !!!cp (998);
3100            require Whatpm::_NamedEntityList;
3101            $self->{state} = ENTITY_NAME_STATE;
3102            $self->{s_kwd} = chr $self->{nc};
3103            $self->{entity__value} = $self->{s_kwd};
3104            $self->{entity__match} = 0;
3105            !!!next-input-character;
3106            redo A;
3107          } else {
3108            !!!cp (1027);
3109            !!!parse-error (type => 'bare ero');
3110            ## Return nothing.
3111            #
3112        }        }
3113    
3114        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3115          ## reference" algorithm.  In other word, there is an "&" character
3116          ## that does not introduce a character reference, which would be
3117          ## appended to the parent element or the attribute value in later
3118          ## process of the tokenizer.
3119    
3120          if ($self->{prev_state} == DATA_STATE) {
3121            !!!cp (997);
3122            $self->{state} = $self->{prev_state};
3123            ## Reconsume.
3124            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3125                      line => $self->{line_prev},
3126                      column => $self->{column_prev},
3127                     });
3128            redo A;
3129          } else {
3130            !!!cp (996);
3131            $self->{ca}->{value} .= '&';
3132            $self->{state} = $self->{prev_state};
3133            ## Reconsume.
3134            redo A;
3135          }
3136        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3137          if ($self->{nc} == 0x0078 or # x
3138              $self->{nc} == 0x0058) { # X
3139            !!!cp (995);
3140            $self->{state} = HEXREF_X_STATE;
3141            $self->{s_kwd} .= chr $self->{nc};
3142            !!!next-input-character;
3143            redo A;
3144          } elsif (0x0030 <= $self->{nc} and
3145                   $self->{nc} <= 0x0039) { # 0..9
3146            !!!cp (994);
3147            $self->{state} = NCR_NUM_STATE;
3148            $self->{s_kwd} = $self->{nc} - 0x0030;
3149            !!!next-input-character;
3150            redo A;
3151          } else {
3152            !!!parse-error (type => 'bare nero',
3153                            line => $self->{line_prev},
3154                            column => $self->{column_prev} - 1);
3155    
3156            ## NOTE: According to the spec algorithm, nothing is returned,
3157            ## and then "&#" is appended to the parent element or the attribute
3158            ## value in the later processing.
3159    
3160            if ($self->{prev_state} == DATA_STATE) {
3161              !!!cp (1019);
3162              $self->{state} = $self->{prev_state};
3163              ## Reconsume.
3164              !!!emit ({type => CHARACTER_TOKEN,
3165                        data => '&#',
3166                        line => $self->{line_prev},
3167                        column => $self->{column_prev} - 1,
3168                       });
3169              redo A;
3170            } else {
3171              !!!cp (993);
3172              $self->{ca}->{value} .= '&#';
3173              $self->{state} = $self->{prev_state};
3174              ## Reconsume.
3175              redo A;
3176            }
3177          }
3178        } elsif ($self->{state} == NCR_NUM_STATE) {
3179          if (0x0030 <= $self->{nc} and
3180              $self->{nc} <= 0x0039) { # 0..9
3181            !!!cp (1012);
3182            $self->{s_kwd} *= 10;
3183            $self->{s_kwd} += $self->{nc} - 0x0030;
3184            
3185            ## Stay in the state.
3186          !!!next-input-character;          !!!next-input-character;
3187            redo A;
3188          } elsif ($self->{nc} == 0x003B) { # ;
3189            !!!cp (1013);
3190            !!!next-input-character;
3191            #
3192        } else {        } else {
3193            !!!cp (1014);
3194          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3195            ## Reconsume.
3196            #
3197        }        }
3198    
3199        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3200          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3201          my $c = $self->{column_prev};
3202          if ($charref_map->{$code}) {
3203            !!!cp (1015);
3204            !!!parse-error (type => 'invalid character reference',
3205                            text => (sprintf 'U+%04X', $code),
3206                            line => $l, column => $c);
3207            $code = $charref_map->{$code};
3208          } elsif ($code > 0x10FFFF) {
3209            !!!cp (1016);
3210            !!!parse-error (type => 'invalid character reference',
3211                            text => (sprintf 'U-%08X', $code),
3212                            line => $l, column => $c);
3213          $code = 0xFFFD;          $code = 0xFFFD;
3214          }
3215    
3216          if ($self->{prev_state} == DATA_STATE) {
3217            !!!cp (992);
3218            $self->{state} = $self->{prev_state};
3219            ## Reconsume.
3220            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3221                      line => $l, column => $c,
3222                     });
3223            redo A;
3224          } else {
3225            !!!cp (991);
3226            $self->{ca}->{value} .= chr $code;
3227            $self->{ca}->{has_reference} = 1;
3228            $self->{state} = $self->{prev_state};
3229            ## Reconsume.
3230            redo A;
3231          }
3232        } elsif ($self->{state} == HEXREF_X_STATE) {
3233          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3234              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3235              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3236            # 0..9, A..F, a..f
3237            !!!cp (990);
3238            $self->{state} = HEXREF_HEX_STATE;
3239            $self->{s_kwd} = 0;
3240            ## Reconsume.
3241            redo A;
3242          } else {
3243            !!!parse-error (type => 'bare hcro',
3244                            line => $self->{line_prev},
3245                            column => $self->{column_prev} - 2);
3246    
3247            ## NOTE: According to the spec algorithm, nothing is returned,
3248            ## and then "&#" followed by "X" or "x" is appended to the parent
3249            ## element or the attribute value in the later processing.
3250    
3251            if ($self->{prev_state} == DATA_STATE) {
3252              !!!cp (1005);
3253              $self->{state} = $self->{prev_state};
3254              ## Reconsume.
3255              !!!emit ({type => CHARACTER_TOKEN,
3256                        data => '&' . $self->{s_kwd},
3257                        line => $self->{line_prev},
3258                        column => $self->{column_prev} - length $self->{s_kwd},
3259                       });
3260              redo A;
3261            } else {
3262              !!!cp (989);
3263              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3264              $self->{state} = $self->{prev_state};
3265              ## Reconsume.
3266              redo A;
3267            }
3268          }
3269        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3270          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3271            # 0..9
3272            !!!cp (1002);
3273            $self->{s_kwd} *= 0x10;
3274            $self->{s_kwd} += $self->{nc} - 0x0030;
3275            ## Stay in the state.
3276            !!!next-input-character;
3277            redo A;
3278          } elsif (0x0061 <= $self->{nc} and
3279                   $self->{nc} <= 0x0066) { # a..f
3280            !!!cp (1003);
3281            $self->{s_kwd} *= 0x10;
3282            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3283            ## Stay in the state.
3284            !!!next-input-character;
3285            redo A;
3286          } elsif (0x0041 <= $self->{nc} and
3287                   $self->{nc} <= 0x0046) { # A..F
3288            !!!cp (1004);
3289            $self->{s_kwd} *= 0x10;
3290            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3291            ## Stay in the state.
3292            !!!next-input-character;
3293            redo A;
3294          } elsif ($self->{nc} == 0x003B) { # ;
3295            !!!cp (1006);
3296            !!!next-input-character;
3297            #
3298          } else {
3299            !!!cp (1007);
3300            !!!parse-error (type => 'no refc',
3301                            line => $self->{line},
3302                            column => $self->{column});
3303            ## Reconsume.
3304            #
3305          }
3306    
3307          my $code = $self->{s_kwd};
3308          my $l = $self->{line_prev};
3309          my $c = $self->{column_prev};
3310          if ($charref_map->{$code}) {
3311            !!!cp (1008);
3312            !!!parse-error (type => 'invalid character reference',
3313                            text => (sprintf 'U+%04X', $code),
3314                            line => $l, column => $c);
3315            $code = $charref_map->{$code};
3316        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3317          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3318            !!!parse-error (type => 'invalid character reference',
3319                            text => (sprintf 'U-%08X', $code),
3320                            line => $l, column => $c);
3321          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!parse-error (type => sprintf 'c1 entity:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3322        }        }
3323          
3324        return {type => 'character', data => chr $code};        if ($self->{prev_state} == DATA_STATE) {
3325      } else {          !!!cp (988);
3326        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3327        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3328        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3329        return undef;                    line => $l, column => $c,
3330      }                   });
3331    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3332              $self->{next_input_character} <= 0x005A) or        } else {
3333             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3334              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3335      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3336      !!!next-input-character;          $self->{state} = $self->{prev_state};
3337            ## Reconsume.
3338      my $value = $entity_name;          redo A;
3339      my $match;        }
3340      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3341      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3342              ## NOTE: Some number greater than the maximum length of entity name
3343      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3344             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3345             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3346               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3347              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3348               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3349              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3350               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3351              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3352        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3353        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3354          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3355            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3356            $match = 1;              $self->{entity__match} = 1;
3357                !!!next-input-character;
3358                #
3359              } else {
3360                !!!cp (1021);
3361                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3362                $self->{entity__match} = -1;
3363                ## Stay in the state.
3364                !!!next-input-character;
3365                redo A;
3366              }
3367            } else {
3368              !!!cp (1022);
3369              $self->{entity__value} .= chr $self->{nc};
3370              $self->{entity__match} *= 2;
3371              ## Stay in the state.
3372            !!!next-input-character;            !!!next-input-character;
3373            last;            redo A;
3374          } elsif (not $in_attr) {          }
3375            $value = $EntityChar->{$entity_name};        }
3376            $match = -1;  
3377          my $data;
3378          my $has_ref;
3379          if ($self->{entity__match} > 0) {
3380            !!!cp (1023);
3381            $data = $self->{entity__value};
3382            $has_ref = 1;
3383            #
3384          } elsif ($self->{entity__match} < 0) {
3385            !!!parse-error (type => 'no refc');
3386            if ($self->{prev_state} != DATA_STATE and # in attribute
3387                $self->{entity__match} < -1) {
3388              !!!cp (1024);
3389              $data = '&' . $self->{s_kwd};
3390              #
3391          } else {          } else {
3392            $value .= chr $self->{next_input_character};            !!!cp (1025);
3393              $data = $self->{entity__value};
3394              $has_ref = 1;
3395              #
3396          }          }
3397        } else {        } else {
3398          $value .= chr $self->{next_input_character};          !!!cp (1026);
3399            !!!parse-error (type => 'bare ero',
3400                            line => $self->{line_prev},
3401                            column => $self->{column_prev} - length $self->{s_kwd});
3402            $data = '&' . $self->{s_kwd};
3403            #
3404          }
3405      
3406          ## NOTE: In these cases, when a character reference is found,
3407          ## it is consumed and a character token is returned, or, otherwise,
3408          ## nothing is consumed and returned, according to the spec algorithm.
3409          ## In this implementation, anything that has been examined by the
3410          ## tokenizer is appended to the parent element or the attribute value
3411          ## as string, either literal string when no character reference or
3412          ## entity-replaced string otherwise, in this stage, since any characters
3413          ## that would not be consumed are appended in the data state or in an
3414          ## appropriate attribute value state anyway.
3415    
3416          if ($self->{prev_state} == DATA_STATE) {
3417            !!!cp (986);
3418            $self->{state} = $self->{prev_state};
3419            ## Reconsume.
3420            !!!emit ({type => CHARACTER_TOKEN,
3421                      data => $data,
3422                      line => $self->{line_prev},
3423                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3424                     });
3425            redo A;
3426          } else {
3427            !!!cp (985);
3428            $self->{ca}->{value} .= $data;
3429            $self->{ca}->{has_reference} = 1 if $has_ref;
3430            $self->{state} = $self->{prev_state};
3431            ## Reconsume.
3432            redo A;
3433        }        }
       !!!next-input-character;  
     }  
       
     if ($match > 0) {  
       return {type => 'character', data => $value};  
     } elsif ($match < 0) {  
       !!!parse-error (type => 'refc');  
       return {type => 'character', data => $value};  
3434      } else {      } else {
3435        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       return {type => 'character', data => '&'.$value};  
3436      }      }
3437    } else {    } # A  
3438      ## no characters are consumed  
3439      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3440      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3441    
3442  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3443    my $self = shift;    my $self = shift;
# Line 1737  sub _initialize_tree_constructor ($) { Line 3446  sub _initialize_tree_constructor ($) {
3446    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3447    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3448    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3449      $self->{document}->set_user_data (manakai_source_line => 1);
3450      $self->{document}->set_user_data (manakai_source_column => 1);
3451  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3452    
3453  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1763  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474        
3475    !!!next-token;    !!!next-token;
3476    
   $self->{insertion_mode} = 'before head';  
3477    undef $self->{form_element};    undef $self->{form_element};
3478    undef $self->{head_element};    undef $self->{head_element};
3479    $self->{open_elements} = [];    $self->{open_elements} = [];
3480    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3481    
3482      ## NOTE: The "initial" insertion mode.
3483    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3484    
3485      ## NOTE: The "before html" insertion mode.
3486    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3487      $self->{insertion_mode} = BEFORE_HEAD_IM;
3488    
3489      ## NOTE: The "before head" insertion mode and so on.
3490    $self->_tree_construction_main;    $self->_tree_construction_main;
3491  } # _construct_tree  } # _construct_tree
3492    
3493  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3494    my $self = shift;    my $self = shift;
3495    
3496      ## NOTE: "initial" insertion mode
3497    
3498    INITIAL: {    INITIAL: {
3499      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
3500        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3501        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
3502        ## language.        ## language.
3503        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3504        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3505        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3506        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3507            defined $token->{public_identifier} or            defined $token->{sysid}) {
3508            defined $token->{system_identifier}) {          !!!cp ('t1');
3509          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3510        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3511          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3512          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3513          } elsif (defined $token->{pubid}) {
3514            if ($token->{pubid} eq 'XSLT-compat') {
3515              !!!cp ('t1.2');
3516              !!!parse-error (type => 'XSLT-compat', token => $token,
3517                              level => $self->{level}->{should});
3518            } else {
3519              !!!parse-error (type => 'not HTML5', token => $token);
3520            }
3521          } else {
3522            !!!cp ('t3');
3523            #
3524        }        }
3525                
3526        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3527          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3528        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3529            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3530        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3531            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3532        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3533        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3534        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3535                
3536        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3537            !!!cp ('t4');
3538          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3539        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3540          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3541          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3542          if ({          my $prefix = [
3543            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3544            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3545            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3546            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3547            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3548            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3549            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3550            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3551            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3552            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3553            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3554            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3555            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3556            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3557            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3558            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3559            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3560            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3561            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3562            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3563            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3564            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3565            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3566            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3567            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3568            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3569            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3570            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3571            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3572            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3573            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3574            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3575            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3576            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3577            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3578            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3579            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3580            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3581            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3582            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3583            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3584            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3585            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3587            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3588            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3589            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3590            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3591            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3592            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3593            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3594            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3595            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3596            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3597            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3598            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3599            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3600            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3601            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3602            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3603            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3604            "-//W3C//DTD W3 HTML//EN" => 1,            }
3605            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3606            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3607            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3608            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3609            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3610            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3611            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3612          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3613                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3614            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3615                !!!cp ('t6');
3616              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3617            } else {            } else {
3618                !!!cp ('t7');
3619              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3620            }            }
3621          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3622                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3623              !!!cp ('t8');
3624            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3625            } else {
3626              !!!cp ('t9');
3627          }          }
3628          } else {
3629            !!!cp ('t10');
3630        }        }
3631        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3632          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3633          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3634          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {          if ($sysid eq "http://www.ibm.com/data/dtd/v11/ibmxhtml1-transitional.dtd") {
3635              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3636              ## marked as quirks.
3637            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3638              !!!cp ('t11');
3639            } else {
3640              !!!cp ('t12');
3641          }          }
3642          } else {
3643            !!!cp ('t13');
3644        }        }
3645                
3646        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3647        !!!next-token;        !!!next-token;
3648        return;        return;
3649      } elsif ({      } elsif ({
3650                'start tag' => 1,                START_TAG_TOKEN, 1,
3651                'end tag' => 1,                END_TAG_TOKEN, 1,
3652                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
3653               }->{$token->{type}}) {               }->{$token->{type}}) {
3654        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3655          !!!parse-error (type => 'no DOCTYPE', token => $token);
3656        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3657        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3658        ## reprocess        ## reprocess
3659          !!!ack-later;
3660        return;        return;
3661      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3662        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3663          ## Ignore the token          ## Ignore the token
3664    
3665          unless (length $token->{data}) {          unless (length $token->{data}) {
3666            ## Stay in the phase            !!!cp ('t15');
3667              ## Stay in the insertion mode.
3668            !!!next-token;            !!!next-token;
3669            redo INITIAL;            redo INITIAL;
3670            } else {
3671              !!!cp ('t16');
3672          }          }
3673          } else {
3674            !!!cp ('t17');
3675        }        }
3676    
3677        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3678        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3679        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3680        ## reprocess        ## reprocess
3681        return;        return;
3682      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
3683          !!!cp ('t18');
3684        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3685        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3686                
3687        ## Stay in the phase.        ## Stay in the insertion mode.
3688        !!!next-token;        !!!next-token;
3689        redo INITIAL;        redo INITIAL;
3690      } else {      } else {
3691        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
3692      }      }
3693    } # INITIAL    } # INITIAL
3694    
3695      die "$0: _tree_construction_initial: This should be never reached";
3696  } # _tree_construction_initial  } # _tree_construction_initial
3697    
3698  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3699    my $self = shift;    my $self = shift;
3700    
3701      ## NOTE: "before html" insertion mode.
3702        
3703    B: {    B: {
3704        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3705          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3706            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3707          ## Ignore the token          ## Ignore the token
3708          ## Stay in the phase          ## Stay in the insertion mode.
3709          !!!next-token;          !!!next-token;
3710          redo B;          redo B;
3711        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3712            !!!cp ('t20');
3713          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3714          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3715          ## Stay in the phase          ## Stay in the insertion mode.
3716          !!!next-token;          !!!next-token;
3717          redo B;          redo B;
3718        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3719          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3720            ## Ignore the token.            ## Ignore the token.
3721    
3722            unless (length $token->{data}) {            unless (length $token->{data}) {
3723              ## Stay in the phase              !!!cp ('t21');
3724                ## Stay in the insertion mode.
3725              !!!next-token;              !!!next-token;
3726              redo B;              redo B;
3727              } else {
3728                !!!cp ('t22');
3729            }            }
3730            } else {
3731              !!!cp ('t23');
3732          }          }
3733    
3734            $self->{application_cache_selection}->(undef);
3735    
3736          #          #
3737          } elsif ($token->{type} == START_TAG_TOKEN) {
3738            if ($token->{tag_name} eq 'html') {
3739              my $root_element;
3740              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3741              $self->{document}->append_child ($root_element);
3742              push @{$self->{open_elements}},
3743                  [$root_element, $el_category->{html}];
3744    
3745              if ($token->{attributes}->{manifest}) {
3746                !!!cp ('t24');
3747                $self->{application_cache_selection}
3748                    ->($token->{attributes}->{manifest}->{value});
3749                ## ISSUE: Spec is unclear on relative references.
3750                ## According to Hixie (#whatwg 2008-03-19), it should be
3751                ## resolved against the base URI of the document in HTML
3752                ## or xml:base of the element in XHTML.
3753              } else {
3754                !!!cp ('t25');
3755                $self->{application_cache_selection}->(undef);
3756              }
3757    
3758              !!!nack ('t25c');
3759    
3760              !!!next-token;
3761              return; ## Go to the "before head" insertion mode.
3762            } else {
3763              !!!cp ('t25.1');
3764              #
3765            }
3766        } elsif ({        } elsif ({
3767                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3768                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3769                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3770          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3771          #          #
3772        } else {        } else {
3773          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3774        }        }
3775        my $root_element; !!!create-element ($root_element, 'html');  
3776        $self->{document}->append_child ($root_element);      my $root_element;
3777        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3778        #$phase = 'main';      $self->{document}->append_child ($root_element);
3779        ## reprocess      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3780        #redo B;  
3781        return;      $self->{application_cache_selection}->(undef);
3782    
3783        ## NOTE: Reprocess the token.
3784        !!!ack-later;
3785        return; ## Go to the "before head" insertion mode.
3786    } # B    } # B
3787    
3788      die "$0: _tree_construction_root_element: This should never be reached";
3789  } # _tree_construction_root_element  } # _tree_construction_root_element
3790    
3791  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2001  sub _reset_insertion_mode ($) { Line 3800  sub _reset_insertion_mode ($) {
3800            
3801      ## Step 3      ## Step 3
3802      S3: {      S3: {
3803        $last = 1 if $self->{open_elements}->[0]->[0] eq $node->[0];        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3804        if (defined $self->{inner_html_node}) {          $last = 1;
3805          if ($self->{inner_html_node}->[1] eq 'td' or          if (defined $self->{inner_html_node}) {
3806              $self->{inner_html_node}->[1] eq 'th') {            !!!cp ('t28');
3807              $node = $self->{inner_html_node};
3808            } else {
3809              die "_reset_insertion_mode: t27";
3810            }
3811          }
3812          
3813          ## Step 4..14
3814          my $new_mode;
3815          if ($node->[1] & FOREIGN_EL) {
3816            !!!cp ('t28.1');
3817            ## NOTE: Strictly spaking, the line below only applies to MathML and
3818            ## SVG elements.  Currently the HTML syntax supports only MathML and
3819            ## SVG elements as foreigners.
3820            $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3821          } elsif ($node->[1] & TABLE_CELL_EL) {
3822            if ($last) {
3823              !!!cp ('t28.2');
3824            #            #
3825          } else {          } else {
3826            $node = $self->{inner_html_node};            !!!cp ('t28.3');
3827              $new_mode = IN_CELL_IM;
3828          }          }
3829          } else {
3830            !!!cp ('t28.4');
3831            $new_mode = {
3832                          select => IN_SELECT_IM,
3833                          ## NOTE: |option| and |optgroup| do not set
3834                          ## insertion mode to "in select" by themselves.
3835                          tr => IN_ROW_IM,
3836                          tbody => IN_TABLE_BODY_IM,
3837                          thead => IN_TABLE_BODY_IM,
3838                          tfoot => IN_TABLE_BODY_IM,
3839                          caption => IN_CAPTION_IM,
3840                          colgroup => IN_COLUMN_GROUP_IM,
3841                          table => IN_TABLE_IM,
3842                          head => IN_BODY_IM, # not in head!
3843                          body => IN_BODY_IM,
3844                          frameset => IN_FRAMESET_IM,
3845                         }->{$node->[0]->manakai_local_name};
3846        }        }
       
       ## Step 4..13  
       my $new_mode = {  
                       select => 'in select',  
                       td => 'in cell',  
                       th => 'in cell',  
                       tr => 'in row',  
                       tbody => 'in table body',  
                       thead => 'in table head',  
                       tfoot => 'in table foot',  
                       caption => 'in caption',  
                       colgroup => 'in column group',  
                       table => 'in table',  
                       head => 'in body', # not in head!  
                       body => 'in body',  
                       frameset => 'in frameset',  
                      }->{$node->[1]};  
3847        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3848                
3849        ## Step 14        ## Step 15
3850        if ($node->[1] eq 'html') {        if ($node->[1] & HTML_EL) {
3851          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3852            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3853              $self->{insertion_mode} = BEFORE_HEAD_IM;
3854          } else {          } else {
3855            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3856              !!!cp ('t30');
3857              $self->{insertion_mode} = AFTER_HEAD_IM;
3858          }          }
3859          return;          return;
3860          } else {
3861            !!!cp ('t31');
3862        }        }
3863                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3864        ## Step 16        ## Step 16
3865          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3866          
3867          ## Step 17
3868        $i--;        $i--;
3869        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3870                
3871        ## Step 17        ## Step 18
3872        redo S3;        redo S3;
3873      } # S3      } # S3
3874    
3875      die "$0: _reset_insertion_mode: This line should never be reached";
3876  } # _reset_insertion_mode  } # _reset_insertion_mode
3877    
3878  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3879    my $self = shift;    my $self = shift;
3880    
   my $phase = 'main';  
   
3881    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3882    
3883    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2072  sub _tree_construction_main ($) { Line 3894  sub _tree_construction_main ($) {
3894      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3895      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3896        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3897            !!!cp ('t32');
3898          return;          return;
3899        }        }
3900      }      }
# Line 2086  sub _tree_construction_main ($) { Line 3909  sub _tree_construction_main ($) {
3909    
3910        ## Step 6        ## Step 6
3911        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3912            !!!cp ('t33_1');
3913          #          #
3914        } else {        } else {
3915          my $in_open_elements;          my $in_open_elements;
3916          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3917            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3918                !!!cp ('t33');
3919              $in_open_elements = 1;              $in_open_elements = 1;
3920              last OE;              last OE;
3921            }            }
3922          }          }
3923          if ($in_open_elements) {          if ($in_open_elements) {
3924              !!!cp ('t34');
3925            #            #
3926          } else {          } else {
3927              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3928              !!!cp ('t35');
3929            redo S4;            redo S4;
3930          }          }
3931        }        }
# Line 2120  sub _tree_construction_main ($) { Line 3948  sub _tree_construction_main ($) {
3948    
3949        ## Step 11        ## Step 11
3950        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3951            !!!cp ('t36');
3952          ## Step 7'          ## Step 7'
3953          $i++;          $i++;
3954          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3955                    
3956          redo S7;          redo S7;
3957        }        }
3958    
3959          !!!cp ('t37');
3960      } # S7      } # S7
3961    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3962    
3963    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3964      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3965        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3966            !!!cp ('t38');
3967          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3968          return;          return;
3969        }        }
3970      }      }
3971    
3972        !!!cp ('t39');
3973    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3974    
3975    my $parse_rcdata = sub ($$) {    my $insert;
3976      my ($content_model_flag, $insert) = @_;  
3977      my $parse_rcdata = sub ($) {
3978        my ($content_model_flag) = @_;
3979    
3980      ## Step 1      ## Step 1
3981      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3982      my $el;      my $el;
3983      !!!create-element ($el, $start_tag_name, $token->{attributes});      !!!create-element ($el, $HTML_NS, $start_tag_name, $token->{attributes}, $token);
3984    
3985      ## Step 2      ## Step 2
3986      $insert->($el); # /context node/->append_child ($el)      $insert->($el);
3987    
3988      ## Step 3      ## Step 3
3989      $self->{content_model_flag} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3990      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3991    
3992      ## Step 4      ## Step 4
3993      my $text = '';      my $text = '';
3994        !!!nack ('t40.1');
3995      !!!next-token;      !!!next-token;
3996      while ($token->{type} eq 'character') { # or until stop tokenizing      while ($token->{type} == CHARACTER_TOKEN) { # or until stop tokenizing
3997          !!!cp ('t40');
3998        $text .= $token->{data};        $text .= $token->{data};
3999        !!!next-token;        !!!next-token;
4000      }      }
4001    
4002      ## Step 5      ## Step 5
4003      if (length $text) {      if (length $text) {
4004          !!!cp ('t41');
4005        my $text = $self->{document}->create_text_node ($text);        my $text = $self->{document}->create_text_node ($text);
4006        $el->append_child ($text);        $el->append_child ($text);
4007      }      }
4008    
4009      ## Step 6      ## Step 6
4010      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
4011    
4012      ## Step 7      ## Step 7
4013      if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {      if ($token->{type} == END_TAG_TOKEN and
4014            $token->{tag_name} eq $start_tag_name) {
4015          !!!cp ('t42');
4016        ## Ignore the token        ## Ignore the token
4017      } else {      } else {
4018        !!!parse-error (type => 'in '.$content_model_flag.':#'.$token->{type});        ## NOTE: An end-of-file token.
4019          if ($content_model_flag == CDATA_CONTENT_MODEL) {
4020            !!!cp ('t43');
4021            !!!parse-error (type => 'in CDATA:#eof', token => $token);
4022          } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {
4023            !!!cp ('t44');
4024            !!!parse-error (type => 'in RCDATA:#eof', token => $token);
4025          } else {
4026            die "$0: $content_model_flag in parse_rcdata";
4027          }
4028      }      }
4029      !!!next-token;      !!!next-token;
4030    }; # $parse_rcdata    }; # $parse_rcdata
4031    
4032    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
     my $insert = $_[0];  
4033      my $script_el;      my $script_el;
4034      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4035      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4036    
4037      $self->{content_model_flag} = 'CDATA';      $self->{content_model} = CDATA_CONTENT_MODEL;
4038      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
4039            
4040      my $text = '';      my $text = '';
4041        !!!nack ('t45.1');
4042      !!!next-token;      !!!next-token;
4043      while ($token->{type} eq 'character') {      while ($token->{type} == CHARACTER_TOKEN) {
4044          !!!cp ('t45');
4045        $text .= $token->{data};        $text .= $token->{data};
4046        !!!next-token;        !!!next-token;
4047      } # stop if non-character token or tokenizer stops tokenising      } # stop if non-character token or tokenizer stops tokenising
4048      if (length $text) {      if (length $text) {
4049          !!!cp ('t46');
4050        $script_el->manakai_append_text ($text);        $script_el->manakai_append_text ($text);
4051      }      }
4052                                
4053      $self->{content_model_flag} = 'PCDATA';      $self->{content_model} = PCDATA_CONTENT_MODEL;
4054    
4055      if ($token->{type} eq 'end tag' and      if ($token->{type} == END_TAG_TOKEN and
4056          $token->{tag_name} eq 'script') {          $token->{tag_name} eq 'script') {
4057          !!!cp ('t47');
4058        ## Ignore the token        ## Ignore the token
4059      } else {      } else {
4060        !!!parse-error (type => 'in CDATA:#'.$token->{type});        !!!cp ('t48');
4061          !!!parse-error (type => 'in CDATA:#eof', token => $token);
4062        ## ISSUE: And ignore?        ## ISSUE: And ignore?
4063        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4064      }      }
4065            
4066      if (defined $self->{inner_html_node}) {      if (defined $self->{inner_html_node}) {
4067          !!!cp ('t49');
4068        ## TODO: mark as "already executed"        ## TODO: mark as "already executed"
4069      } else {      } else {
4070          !!!cp ('t50');
4071        ## TODO: $old_insertion_point = current insertion point        ## TODO: $old_insertion_point = current insertion point
4072        ## TODO: insertion point = just before the next input character        ## TODO: insertion point = just before the next input character
4073    
# Line 2225  sub _tree_construction_main ($) { Line 4081  sub _tree_construction_main ($) {
4081      !!!next-token;      !!!next-token;
4082    }; # $script_start_tag    }; # $script_start_tag
4083    
4084      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4085      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4086      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4087    
4088    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4089      my $tag_name = shift;      my $end_tag_token = shift;
4090        my $tag_name = $end_tag_token->{tag_name};
4091    
4092        ## NOTE: The adoption agency algorithm (AAA).
4093    
4094      FET: {      FET: {
4095        ## Step 1        ## Step 1
4096        my $formatting_element;        my $formatting_element;
4097        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4098        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4099          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4100              !!!cp ('t52');
4101              last AFE;
4102            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4103                         eq $tag_name) {
4104              !!!cp ('t51');
4105            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4106            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4107            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4108          }          }
4109        } # AFE        } # AFE
4110        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4111          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4112            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4113          ## Ignore the token          ## Ignore the token
4114          !!!next-token;          !!!next-token;
4115          return;          return;
# Line 2254  sub _tree_construction_main ($) { Line 4121  sub _tree_construction_main ($) {
4121          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4122          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4123            if ($in_scope) {            if ($in_scope) {
4124                !!!cp ('t54');
4125              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4126              last INSCOPE;              last INSCOPE;
4127            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4128              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4129                !!!parse-error (type => 'unmatched end tag',
4130                                text => $token->{tag_name},
4131                                token => $end_tag_token);
4132              ## Ignore the token              ## Ignore the token
4133              !!!next-token;              !!!next-token;
4134              return;              return;
4135            }            }
4136          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4137                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4138            $in_scope = 0;            $in_scope = 0;
4139          }          }
4140        } # INSCOPE        } # INSCOPE
4141        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4142          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4143            !!!parse-error (type => 'unmatched end tag',
4144                            text => $token->{tag_name},
4145                            token => $end_tag_token);
4146          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4147          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4148          return;          return;
4149        }        }
4150        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4151          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4152            !!!parse-error (type => 'not closed',
4153                            text => $self->{open_elements}->[-1]->[0]
4154                                ->manakai_local_name,
4155                            token => $end_tag_token);
4156        }        }
4157                
4158        ## Step 2        ## Step 2
# Line 2284  sub _tree_construction_main ($) { Line 4160  sub _tree_construction_main ($) {
4160        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4161        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4162          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4163          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4164              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4165              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4166               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4167              !!!cp ('t59');
4168            $furthest_block = $node;            $furthest_block = $node;
4169            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4170          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4171              !!!cp ('t60');
4172            last OE;            last OE;
4173          }          }
4174        } # OE        } # OE
4175                
4176        ## Step 3        ## Step 3
4177        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4178            !!!cp ('t61');
4179          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4180          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4181          !!!next-token;          !!!next-token;
# Line 2309  sub _tree_construction_main ($) { Line 4188  sub _tree_construction_main ($) {
4188        ## Step 5        ## Step 5
4189        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4190        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4191            !!!cp ('t62');
4192          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4193        }        }
4194                
# Line 2331  sub _tree_construction_main ($) { Line 4211  sub _tree_construction_main ($) {
4211          S7S2: {          S7S2: {
4212            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4213              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4214                  !!!cp ('t63');
4215                $node_i_in_active = $_;                $node_i_in_active = $_;
4216                last S7S2;                last S7S2;
4217              }              }
# Line 2344  sub _tree_construction_main ($) { Line 4225  sub _tree_construction_main ($) {
4225                    
4226          ## Step 4          ## Step 4
4227          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4228              !!!cp ('t64');
4229            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4230          }          }
4231                    
4232          ## Step 5          ## Step 5
4233          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4234              !!!cp ('t65');
4235            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4236            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4237            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2366  sub _tree_construction_main ($) { Line 4249  sub _tree_construction_main ($) {
4249        } # S7          } # S7  
4250                
4251        ## Step 8        ## Step 8
4252        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4253            my $foster_parent_element;
4254            my $next_sibling;
4255            OE: for (reverse 0..$#{$self->{open_elements}}) {
4256              if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
4257                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4258                                 if (defined $parent and $parent->node_type == 1) {
4259                                   !!!cp ('t65.1');
4260                                   $foster_parent_element = $parent;
4261                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4262                                 } else {
4263                                   !!!cp ('t65.2');
4264                                   $foster_parent_element
4265                                     = $self->{open_elements}->[$_ - 1]->[0];
4266                                 }
4267                                 last OE;
4268                               }
4269                             } # OE
4270                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4271                               unless defined $foster_parent_element;
4272            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4273            $open_tables->[-1]->[1] = 1; # tainted
4274          } else {
4275            !!!cp ('t65.3');
4276            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4277          }
4278                
4279        ## Step 9        ## Step 9
4280        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2383  sub _tree_construction_main ($) { Line 4291  sub _tree_construction_main ($) {
4291        my $i;        my $i;
4292        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4293          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4294              !!!cp ('t66');
4295            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4296            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4297          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4298              !!!cp ('t67');
4299            $i = $_;            $i = $_;
4300          }          }
4301        } # AFE        } # AFE
# Line 2395  sub _tree_construction_main ($) { Line 4305  sub _tree_construction_main ($) {
4305        undef $i;        undef $i;
4306        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4307          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4308              !!!cp ('t68');
4309            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4310            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4311          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4312              !!!cp ('t69');
4313            $i = $_;            $i = $_;
4314          }          }
4315        } # OE        } # OE
# Line 2408  sub _tree_construction_main ($) { Line 4320  sub _tree_construction_main ($) {
4320      } # FET      } # FET
4321    }; # $formatting_end_tag    }; # $formatting_end_tag
4322    
4323    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4324      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4325    }; # $insert_to_current    }; # $insert_to_current
4326    
4327    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4328                         my $child = shift;      my $child = shift;
4329                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4330                              table => 1, tbody => 1, tfoot => 1,        # MUST
4331                              thead => 1, tr => 1,        my $foster_parent_element;
4332                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4333                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4334                           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') {  
4335                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4336                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4337                                   !!!cp ('t70');
4338                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4339                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4340                               } else {                               } else {
4341                                   !!!cp ('t71');
4342                                 $foster_parent_element                                 $foster_parent_element
4343                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4344                               }                               }
# Line 2438  sub _tree_construction_main ($) { Line 4349  sub _tree_construction_main ($) {
4349                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4350                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4351                             ($child, $next_sibling);                             ($child, $next_sibling);
4352                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4353                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4354                         }        !!!cp ('t72');
4355          $self->{open_elements}->[-1]->[0]->append_child ($child);
4356        }
4357    }; # $insert_to_foster    }; # $insert_to_foster
4358    
4359    my $in_body = sub {    B: while (1) {
4360      my $insert = shift;      if ($token->{type} == DOCTYPE_TOKEN) {
4361      if ($token->{type} eq 'start tag') {        !!!cp ('t73');
4362        if ($token->{tag_name} eq 'script') {        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4363          ## NOTE: This is an "as if in head" code clone        ## Ignore the token
4364          $script_start_tag->($insert);        ## Stay in the phase
4365          return;        !!!next-token;
4366        } elsif ($token->{tag_name} eq 'style') {        next B;
4367          ## NOTE: This is an "as if in head" code clone      } elsif ($token->{type} == START_TAG_TOKEN and
4368          $parse_rcdata->('CDATA', $insert);               $token->{tag_name} eq 'html') {
4369          return;        if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4370        } elsif ({          !!!cp ('t79');
4371                  base => 1, link => 1, meta => 1,          !!!parse-error (type => 'after html', text => 'html', token => $token);
4372                 }->{$token->{tag_name}}) {          $self->{insertion_mode} = AFTER_BODY_IM;
4373          ## NOTE: This is an "as if in head" code clone, only "-t" differs        } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4374          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!cp ('t80');
4375          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.          !!!parse-error (type => 'after html', text => 'html', token => $token);
4376          !!!next-token;          $self->{insertion_mode} = AFTER_FRAMESET_IM;
4377          ## TODO: Extracting |charset| from |meta|.        } else {
4378          return;          !!!cp ('t81');
4379        } elsif ($token->{tag_name} eq 'title') {        }
4380          !!!parse-error (type => 'in body:title');  
4381          ## NOTE: This is an "as if in head" code clone        !!!cp ('t82');
4382          $parse_rcdata->('RCDATA', $insert);        !!!parse-error (type => 'not first start tag', token => $token);
4383          return;        my $top_el = $self->{open_elements}->[0]->[0];
4384        } elsif ($token->{tag_name} eq 'body') {        for my $attr_name (keys %{$token->{attributes}}) {
4385          !!!parse-error (type => 'in body:body');          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4386                            !!!cp ('t84');
4387          if (@{$self->{open_elements}} == 1 or            $top_el->set_attribute_ns
4388              $self->{open_elements}->[1]->[1] ne 'body') {              (undef, [undef, $attr_name],
4389            ## Ignore the token               $token->{attributes}->{$attr_name}->{value});
         } 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});  
             }  
           }  
4390          }          }
4391          }
4392          !!!nack ('t84.1');
4393          !!!next-token;
4394          next B;
4395        } elsif ($token->{type} == COMMENT_TOKEN) {
4396          my $comment = $self->{document}->create_comment ($token->{data});
4397          if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4398            !!!cp ('t85');
4399            $self->{document}->append_child ($comment);
4400          } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4401            !!!cp ('t86');
4402            $self->{open_elements}->[0]->[0]->append_child ($comment);
4403          } else {
4404            !!!cp ('t87');
4405            $self->{open_elements}->[-1]->[0]->append_child ($comment);
4406          }
4407          !!!next-token;
4408          next B;
4409        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4410          if ($token->{type} == CHARACTER_TOKEN) {
4411            !!!cp ('t87.1');
4412            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4413          !!!next-token;          !!!next-token;
4414          return;          next B;
4415        } elsif ({        } elsif ($token->{type} == START_TAG_TOKEN) {
4416                  address => 1, blockquote => 1, center => 1, dir => 1,          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4417                  div => 1, dl => 1, fieldset => 1, listing => 1,               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4418                  menu => 1, ol => 1, p => 1, ul => 1,              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4419                  pre => 1,              ($token->{tag_name} eq 'svg' and
4420                 }->{$token->{tag_name}}) {               $self->{open_elements}->[-1]->[1] & MML_AXML_EL)) {
4421          ## has a p element in scope            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4422          INSCOPE: for (reverse @{$self->{open_elements}}) {            !!!cp ('t87.2');
4423            if ($_->[1] eq 'p') {            #
4424              !!!back-token;          } elsif ({
4425              $token = {type => 'end tag', tag_name => 'p'};                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4426              return;                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4427            } elsif ({                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4428                      table => 1, caption => 1, td => 1, th => 1,                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4429                      button => 1, marquee => 1, object => 1, html => 1,                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4430                     }->{$_->[1]}) {                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4431              last INSCOPE;                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4432            }                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4433          } # INSCOPE                   }->{$token->{tag_name}}) {
4434                        !!!cp ('t87.2');
4435          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            !!!parse-error (type => 'not closed',
4436          if ($token->{tag_name} eq 'pre') {                            text => $self->{open_elements}->[-1]->[0]
4437            !!!next-token;                                ->manakai_local_name,
4438            if ($token->{type} eq 'character') {                            token => $token);
4439              $token->{data} =~ s/^\x0A//;  
4440              unless (length $token->{data}) {            pop @{$self->{open_elements}}
4441                !!!next-token;                while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4442              }  
4443            }            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4444          } else {            ## Reprocess.
4445            !!!next-token;            next B;
         }  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         if (defined $self->{form_element}) {  
           !!!parse-error (type => 'in form:form');  
           ## Ignore the token  
           !!!next-token;  
           return;  
4446          } else {          } else {
4447            ## has a p element in scope            my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4448            INSCOPE: for (reverse @{$self->{open_elements}}) {            my $tag_name = $token->{tag_name};
4449              if ($_->[1] eq 'p') {            if ($nsuri eq $SVG_NS) {
4450                !!!back-token;              $tag_name = {
4451                $token = {type => 'end tag', tag_name => 'p'};                 altglyph => 'altGlyph',
4452                return;                 altglyphdef => 'altGlyphDef',
4453              } elsif ({                 altglyphitem => 'altGlyphItem',
4454                        table => 1, caption => 1, td => 1, th => 1,                 animatecolor => 'animateColor',
4455                        button => 1, marquee => 1, object => 1, html => 1,                 animatemotion => 'animateMotion',
4456                       }->{$_->[1]}) {                 animatetransform => 'animateTransform',
4457                last INSCOPE;                 clippath => 'clipPath',
4458              }                 feblend => 'feBlend',
4459            } # INSCOPE                 fecolormatrix => 'feColorMatrix',
4460                               fecomponenttransfer => 'feComponentTransfer',
4461            !!!insert-element-t ($token->{tag_name}, $token->{attributes});                 fecomposite => 'feComposite',
4462            $self->{form_element} = $self->{open_elements}->[-1]->[0];                 feconvolvematrix => 'feConvolveMatrix',
4463            !!!next-token;                 fediffuselighting => 'feDiffuseLighting',
4464            return;                 fedisplacementmap => 'feDisplacementMap',
4465          }                 fedistantlight => 'feDistantLight',
4466        } elsif ($token->{tag_name} eq 'li') {                 feflood => 'feFlood',
4467          ## has a p element in scope                 fefunca => 'feFuncA',
4468          INSCOPE: for (reverse @{$self->{open_elements}}) {                 fefuncb => 'feFuncB',
4469            if ($_->[1] eq 'p') {                 fefuncg => 'feFuncG',
4470              !!!back-token;                 fefuncr => 'feFuncR',
4471              $token = {type => 'end tag', tag_name => 'p'};                 fegaussianblur => 'feGaussianBlur',
4472              return;                 feimage => 'feImage',
4473            } elsif ({                 femerge => 'feMerge',
4474                      table => 1, caption => 1, td => 1, th => 1,                 femergenode => 'feMergeNode',
4475                      button => 1, marquee => 1, object => 1, html => 1,                 femorphology => 'feMorphology',
4476                     }->{$_->[1]}) {                 feoffset => 'feOffset',
4477              last INSCOPE;                 fepointlight => 'fePointLight',
4478                   fespecularlighting => 'feSpecularLighting',
4479                   fespotlight => 'feSpotLight',
4480                   fetile => 'feTile',
4481                   feturbulence => 'feTurbulence',
4482                   foreignobject => 'foreignObject',
4483                   glyphref => 'glyphRef',
4484                   lineargradient => 'linearGradient',
4485                   radialgradient => 'radialGradient',
4486                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4487                   textpath => 'textPath',  
4488                }->{$tag_name} || $tag_name;
4489            }            }
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'li') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
               ## TODO: test  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
               ## TODO: test  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model_flag} = 'PLAINTEXT';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'a') {  
         AFE: for my $i (reverse 0..$#$active_formatting_elements) {  
           my $node = $active_formatting_elements->[$i];  
           if ($node->[1] eq 'a') {  
             !!!parse-error (type => 'in a:a');  
               
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'a'};  
             $formatting_end_tag->($token->{tag_name});  
               
             AFE2: for (reverse 0..$#$active_formatting_elements) {  
               if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$active_formatting_elements, $_, 1;  
                 last AFE2;  
               }  
             } # AFE2  
             OE: for (reverse 0..$#{$self->{open_elements}}) {  
               if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {  
                 splice @{$self->{open_elements}}, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4490    
4491          !!!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];  
4492    
4493          !!!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);  
4494    
4495          ## has a |nobr| element in scope            !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'nobr') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'nobr'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
4496    
4497          !!!next-token;            if ($self->{self_closing}) {
4498          return;              pop @{$self->{open_elements}};
4499        } elsif ($token->{tag_name} eq 'marquee' or              !!!ack ('t87.3');
                $token->{tag_name} eq 'object') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->('CDATA', $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{insertion_mode} = 'in table';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, img => 1, param => 1, spacer => 1, wbr => 1,  
                 image => 1,  
                }->{$token->{tag_name}}) {  
         if ($token->{tag_name} eq 'image') {  
           !!!parse-error (type => 'image');  
           $token->{tag_name} = 'img';  
         }  
           
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
           !!!next-token;  
           return;  
         } else {  
           my $at = $token->{attributes};  
           my $form_attrs;  
           $form_attrs->{action} = $at->{action} if $at->{action};  
           my $prompt_attr = $at->{prompt};  
           $at->{name} = {name => 'name', value => 'isindex'};  
           delete $at->{action};  
           delete $at->{prompt};  
           my @tokens = (  
                         {type => 'start tag', tag_name => 'form',  
                          attributes => $form_attrs},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'start tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'label'},  
                        );  
           if ($prompt_attr) {  
             push @tokens, {type => 'character', data => $prompt_attr->{value}};  
4500            } else {            } else {
4501              push @tokens, {type => 'character',              !!!cp ('t87.4');
                            data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD  
             ## TODO: make this configurable  
           }  
           push @tokens,  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'};  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'textarea') {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         ## TODO: $self->{form_element} if defined  
         $self->{content_model_flag} = 'RCDATA';  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         if ($token->{type} eq 'character') {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!next-token;  
4502            }            }
4503          }  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
4504            !!!next-token;            !!!next-token;
4505              next B;
4506          }          }
4507          if (length $text) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4508            $el->manakai_append_text ($text);          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4509          }          !!!cp ('t87.5');
4510                    #
4511          $self->{content_model_flag} = 'PCDATA';        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4512                    !!!cp ('t87.6');
4513          if ($token->{type} eq 'end tag' and          !!!parse-error (type => 'not closed',
4514              $token->{tag_name} eq $tag_name) {                          text => $self->{open_elements}->[-1]->[0]
4515            ## Ignore the token                              ->manakai_local_name,
4516          } else {                          token => $token);
4517            !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4518          }          pop @{$self->{open_elements}}
4519          !!!next-token;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4520          return;  
4521        } elsif ({          ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4522                  iframe => 1,  
4523                  noembed => 1,          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4524                  noframes => 1,          ## Reprocess.
4525                  noscript => 0, ## TODO: 1 if scripting is enabled          next B;
                }->{$token->{tag_name}}) {  
         $parse_rcdata->('CDATA', $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = 'in select';  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'in body:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
4526        } else {        } else {
4527          $reconstruct_active_formatting_elements->($insert_to_current);          die "$0: $token->{type}: Unknown token type";        
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
4528        }        }
4529      } elsif ($token->{type} eq 'end tag') {      }
       if ($token->{tag_name} eq 'body') {  
         if (@{$self->{open_elements}} > 1 and  
             $self->{open_elements}->[1]->[1] eq 'body') {  
           for (@{$self->{open_elements}}) {  
             unless ({  
                        dd => 1, dt => 1, li => 1, p => 1, td => 1,  
                        th => 1, tr => 1, body => 1, html => 1,  
                     }->{$_->[1]}) {  
               !!!parse-error (type => 'not closed:'.$_->[1]);  
             }  
           }  
4530    
4531            $self->{insertion_mode} = 'after body';      if ($self->{insertion_mode} & HEAD_IMS) {
4532            !!!next-token;        if ($token->{type} == CHARACTER_TOKEN) {
4533            return;          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4534          } else {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4535            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.2');
4536            ## Ignore the token              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4537            !!!next-token;              #
4538            return;            } else {
4539          }              !!!cp ('t88.1');
4540        } elsif ($token->{tag_name} eq 'html') {              ## Ignore the token.
4541          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]);  
4542            }            }
4543            $self->{insertion_mode} = 'after body';            unless (length $token->{data}) {
4544            ## reprocess              !!!cp ('t88');
4545            return;              !!!next-token;
4546          } else {              next B;
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4547            }            }
4548          } # INSCOPE  ## TODO: set $token->{column} appropriately
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4549          }          }
4550            
4551          splice @{$self->{open_elements}}, $i if defined $i;          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4552          $clear_up_to_marker->()            !!!cp ('t89');
4553            if {            ## As if <head>
4554              button => 1, marquee => 1, object => 1,            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4555            }->{$token->{tag_name}};            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4556          !!!next-token;            push @{$self->{open_elements}},
4557          return;                [$self->{head_element}, $el_category->{head}];
4558        } elsif ($token->{tag_name} eq 'form') {  
4559          ## has an element in scope            ## Reprocess in the "in head" insertion mode...
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
4560            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
4561    
4562          undef $self->{form_element};            ## Reprocess in the "after head" insertion mode...
4563          !!!next-token;          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4564          return;            !!!cp ('t90');
4565        } elsif ({            ## As if </noscript>
4566                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,            pop @{$self->{open_elements}};
4567                 }->{$token->{tag_name}}) {            !!!parse-error (type => 'in noscript:#text', token => $token);
4568          ## has an element in scope            
4569          my $i;            ## Reprocess in the "in head" insertion mode...
4570          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            ## As if </head>
4571            my $node = $self->{open_elements}->[$_];            pop @{$self->{open_elements}};
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
 ## TODO: <http://html5.org/tools/web-apps-tracker?from=883&to=884>  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1, br => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
4572    
4573          ## Step 2            ## Reprocess in the "after head" insertion mode...
4574          S2: {          } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4575            if ($node->[1] eq $token->{tag_name}) {            !!!cp ('t91');
4576              ## Step 1            pop @{$self->{open_elements}};
4577              ## generate implied end tags  
4578              if ({            ## Reprocess in the "after head" insertion mode...
4579                   dd => 1, dt => 1, li => 1, p => 1,          } else {
4580                   td => 1, th => 1, tr => 1,            !!!cp ('t92');
4581                  }->{$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;  
4582    
4583            ## "after head" insertion mode
4584            ## As if <body>
4585            !!!insert-element ('body',, $token);
4586            $self->{insertion_mode} = IN_BODY_IM;
4587            ## reprocess
4588            next B;
4589          } elsif ($token->{type} == START_TAG_TOKEN) {
4590            if ($token->{tag_name} eq 'head') {
4591              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4592                !!!cp ('t93');
4593                !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4594                $self->{open_elements}->[-1]->[0]->append_child
4595                    ($self->{head_element});
4596                push @{$self->{open_elements}},
4597                    [$self->{head_element}, $el_category->{head}];
4598                $self->{insertion_mode} = IN_HEAD_IM;
4599                !!!nack ('t93.1');
4600              !!!next-token;              !!!next-token;
4601              last S2;              next B;
4602              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4603                !!!cp ('t93.2');
4604                !!!parse-error (type => 'after head', text => 'head',
4605                                token => $token);
4606                ## Ignore the token
4607                !!!nack ('t93.3');
4608                !!!next-token;
4609                next B;
4610            } else {            } else {
4611              ## Step 3              !!!cp ('t95');
4612              if (not $formatting_category->{$node->[1]} and              !!!parse-error (type => 'in head:head',
4613                  #not $phrasing_category->{$node->[1]} and                              token => $token); # or in head noscript
4614                  ($special_category->{$node->[1]} or              ## Ignore the token
4615                   $scoping_category->{$node->[1]})) {              !!!nack ('t95.1');
4616                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!next-token;
4617                ## Ignore the token              next B;
               !!!next-token;  
               last S2;  
             }  
4618            }            }
4619                      } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4620            ## Step 4            !!!cp ('t96');
4621            $node_i--;            ## As if <head>
4622            $node = $self->{open_elements}->[$node_i];            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4623                        $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4624            ## Step 5;            push @{$self->{open_elements}},
4625            redo S2;                [$self->{head_element}, $el_category->{head}];
         } # S2  
         return;  
       }  
     }  
   }; # $in_body  
4626    
4627    B: {            $self->{insertion_mode} = IN_HEAD_IM;
4628      if ($phase eq 'main') {            ## Reprocess in the "in head" insertion mode...
4629        if ($token->{type} eq 'DOCTYPE') {          } else {
4630          !!!parse-error (type => 'in html:#DOCTYPE');            !!!cp ('t97');
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
         ## TODO: unless it is the first start tag token, parse-error  
         my $top_el = $self->{open_elements}->[0]->[0];  
         for my $attr_name (keys %{$token->{attributes}}) {  
           unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
             $top_el->set_attribute_ns  
               (undef, [undef, $attr_name],  
                $token->{attributes}->{$attr_name}->{value});  
           }  
         }  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4631          }          }
4632    
4633          ## Stop parsing              if ($token->{tag_name} eq 'base') {
4634          last B;                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4635                    !!!cp ('t98');
4636                    ## As if </noscript>
4637                    pop @{$self->{open_elements}};
4638                    !!!parse-error (type => 'in noscript', text => 'base',
4639                                    token => $token);
4640                  
4641                    $self->{insertion_mode} = IN_HEAD_IM;
4642                    ## Reprocess in the "in head" insertion mode...
4643                  } else {
4644                    !!!cp ('t99');
4645                  }
4646    
4647          ## ISSUE: There is an issue in the spec.                ## NOTE: There is a "as if in head" code clone.
4648        } else {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4649          if ($self->{insertion_mode} eq 'before head') {                  !!!cp ('t100');
4650            if ($token->{type} eq 'character') {                  !!!parse-error (type => 'after head',
4651              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                                  text => $token->{tag_name}, token => $token);
4652                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);                  push @{$self->{open_elements}},
4653                unless (length $token->{data}) {                      [$self->{head_element}, $el_category->{head}];
4654                  !!!next-token;                } else {
4655                  redo B;                  !!!cp ('t101');
4656                }                }
4657              }                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4658              ## As if <head>                pop @{$self->{open_elements}};
4659              !!!create-element ($self->{head_element}, 'head');                pop @{$self->{open_elements}} # <head>
4660              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4661              push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                !!!nack ('t101.1');
             $self->{insertion_mode} = 'in head';  
             ## reprocess  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};  
             !!!create-element ($self->{head_element}, 'head', $attr);  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             if ($token->{tag_name} eq 'head') {  
               !!!next-token;  
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ({head => 1, body => 1, html => 1}->{$token->{tag_name}}) {  
               ## As if <head>  
               !!!create-element ($self->{head_element}, 'head');  
               $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
               push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token ## ISSUE: An issue in the spec.  
4662                !!!next-token;                !!!next-token;
4663                redo B;                next B;
4664              }          } elsif ($token->{tag_name} eq 'link') {
4665              ## NOTE: There is a "as if in head" code clone.
4666              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4667                !!!cp ('t102');
4668                !!!parse-error (type => 'after head',
4669                                text => $token->{tag_name}, token => $token);
4670                push @{$self->{open_elements}},
4671                    [$self->{head_element}, $el_category->{head}];
4672            } else {            } else {
4673              die "$0: $token->{type}: Unknown type";              !!!cp ('t103');
4674            }            }
4675          } elsif ($self->{insertion_mode} eq 'in head' or            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4676                   $self->{insertion_mode} eq 'in head noscript' or            pop @{$self->{open_elements}};
4677                   $self->{insertion_mode} eq 'after head') {            pop @{$self->{open_elements}} # <head>
4678            if ($token->{type} eq 'character') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4679              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            !!!ack ('t103.1');
4680                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            !!!next-token;
4681                unless (length $token->{data}) {            next B;
4682                  !!!next-token;          } elsif ($token->{tag_name} eq 'command' or
4683                  redo B;                   $token->{tag_name} eq 'eventsource') {
4684                }            if ($self->{insertion_mode} == IN_HEAD_IM) {
4685              }              ## NOTE: If the insertion mode at the time of the emission
4686                            ## of the token was "before head", $self->{insertion_mode}
4687              #              ## is already changed to |IN_HEAD_IM|.
4688            } elsif ($token->{type} eq 'comment') {  
4689              my $comment = $self->{document}->create_comment ($token->{data});              ## NOTE: There is a "as if in head" code clone.
4690              $self->{open_elements}->[-1]->[0]->append_child ($comment);              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4691                pop @{$self->{open_elements}};
4692                pop @{$self->{open_elements}} # <head>
4693                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4694                !!!ack ('t103.2');
4695              !!!next-token;              !!!next-token;
4696              redo B;              next B;
4697            } elsif ($token->{type} eq 'start tag') {            } else {
4698              if ({base => ($self->{insertion_mode} eq 'in head' or              ## NOTE: "in head noscript" or "after head" insertion mode
4699                            $self->{insertion_mode} eq 'after head'),              ## - in these cases, these tags are treated as same as
4700                   link => 1, meta => 1}->{$token->{tag_name}}) {              ## normal in-body tags.
4701                !!!cp ('t103.3');
4702                #
4703              }
4704                } elsif ($token->{tag_name} eq 'meta') {
4705                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4706                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4707                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t104');
4708                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4709                }                                  text => $token->{tag_name}, token => $token);
4710                !!!insert-element ($token->{tag_name}, $token->{attributes});                  push @{$self->{open_elements}},
4711                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.                      [$self->{head_element}, $el_category->{head}];
4712                ## TODO: Extracting |charset| from |meta|.                } else {
4713                pop @{$self->{open_elements}}                  !!!cp ('t105');
4714                    if $self->{insertion_mode} eq 'after head';                }
4715                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4716                  my $meta_el = pop @{$self->{open_elements}};
4717    
4718                  unless ($self->{confident}) {
4719                    if ($token->{attributes}->{charset}) {
4720                      !!!cp ('t106');
4721                      ## NOTE: Whether the encoding is supported or not is handled
4722                      ## in the {change_encoding} callback.
4723                      $self->{change_encoding}
4724                          ->($self, $token->{attributes}->{charset}->{value},
4725                             $token);
4726                      
4727                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4728                          ->set_user_data (manakai_has_reference =>
4729                                               $token->{attributes}->{charset}
4730                                                   ->{has_reference});
4731                    } elsif ($token->{attributes}->{content}) {
4732                      if ($token->{attributes}->{content}->{value}
4733                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4734                              [\x09\x0A\x0C\x0D\x20]*=
4735                              [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4736                              ([^"'\x09\x0A\x0C\x0D\x20]
4737                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4738                        !!!cp ('t107');
4739                        ## NOTE: Whether the encoding is supported or not is handled
4740                        ## in the {change_encoding} callback.
4741                        $self->{change_encoding}
4742                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4743                               $token);
4744                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4745                            ->set_user_data (manakai_has_reference =>
4746                                                 $token->{attributes}->{content}
4747                                                       ->{has_reference});
4748                      } else {
4749                        !!!cp ('t108');
4750                      }
4751                    }
4752                  } else {
4753                    if ($token->{attributes}->{charset}) {
4754                      !!!cp ('t109');
4755                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4756                          ->set_user_data (manakai_has_reference =>
4757                                               $token->{attributes}->{charset}
4758                                                   ->{has_reference});
4759                    }
4760                    if ($token->{attributes}->{content}) {
4761                      !!!cp ('t110');
4762                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4763                          ->set_user_data (manakai_has_reference =>
4764                                               $token->{attributes}->{content}
4765                                                   ->{has_reference});
4766                    }
4767                  }
4768    
4769                  pop @{$self->{open_elements}} # <head>
4770                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4771                  !!!ack ('t110.1');
4772                !!!next-token;                !!!next-token;
4773                redo B;                next B;
4774              } elsif ($token->{tag_name} eq 'title' and              } elsif ($token->{tag_name} eq 'title') {
4775                       $self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4776                ## NOTE: There is a "as if in head" code clone.                  !!!cp ('t111');
4777                if ($self->{insertion_mode} eq 'after head') {                  ## As if </noscript>
4778                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  pop @{$self->{open_elements}};
4779                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'in noscript', text => 'title',
4780                                    token => $token);
4781                  
4782                    $self->{insertion_mode} = IN_HEAD_IM;
4783                    ## Reprocess in the "in head" insertion mode...
4784                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4785                    !!!cp ('t112');
4786                    !!!parse-error (type => 'after head',
4787                                    text => $token->{tag_name}, token => $token);
4788                    push @{$self->{open_elements}},
4789                        [$self->{head_element}, $el_category->{head}];
4790                  } else {
4791                    !!!cp ('t113');
4792                }                }
4793                $parse_rcdata->('RCDATA', $insert_to_current);  
4794                pop @{$self->{open_elements}}                ## NOTE: There is a "as if in head" code clone.
4795                    if $self->{insertion_mode} eq 'after head';                my $parent = defined $self->{head_element} ? $self->{head_element}
4796                redo B;                    : $self->{open_elements}->[-1]->[0];
4797              } elsif ($token->{tag_name} eq 'style') {                $parse_rcdata->(RCDATA_CONTENT_MODEL);
4798                  pop @{$self->{open_elements}} # <head>
4799                      if $self->{insertion_mode} == AFTER_HEAD_IM;
4800                  next B;
4801                } elsif ($token->{tag_name} eq 'style' or
4802                         $token->{tag_name} eq 'noframes') {
4803                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4804                ## insertion mode 'in head')                ## insertion mode IN_HEAD_IM)
4805                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4806                if ($self->{insertion_mode} eq 'after head') {                if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4807                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t114');
4808                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'after head',
4809                                    text => $token->{tag_name}, token => $token);
4810                    push @{$self->{open_elements}},
4811                        [$self->{head_element}, $el_category->{head}];
4812                  } else {
4813                    !!!cp ('t115');
4814                }                }
4815                $parse_rcdata->('CDATA', $insert_to_current);                $parse_rcdata->(CDATA_CONTENT_MODEL);
4816                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4817                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4818                redo B;                next B;
4819              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
4820                if ($self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_IM) {
4821                    !!!cp ('t116');
4822                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4823                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4824                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4825                    !!!nack ('t116.1');
4826                  !!!next-token;                  !!!next-token;
4827                  redo B;                  next B;
4828                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4829                  !!!parse-error (type => 'noscript in noscript');                  !!!cp ('t117');
4830                    !!!parse-error (type => 'in noscript', text => 'noscript',
4831                                    token => $token);
4832                  ## Ignore the token                  ## Ignore the token
4833                  redo B;                  !!!nack ('t117.1');
4834                    !!!next-token;
4835                    next B;
4836                } else {                } else {
4837                    !!!cp ('t118');
4838                  #                  #
4839                }                }
4840              } elsif ($token->{tag_name} eq 'head' and              } elsif ($token->{tag_name} eq 'script') {
4841                       $self->{insertion_mode} ne 'after head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4842                !!!parse-error (type => 'in head:head'); # or in head noscript                  !!!cp ('t119');
4843                ## Ignore the token                  ## As if </noscript>
4844                !!!next-token;                  pop @{$self->{open_elements}};
4845                redo B;                  !!!parse-error (type => 'in noscript', text => 'script',
4846              } elsif ($self->{insertion_mode} ne 'in head noscript' and                                  token => $token);
4847                       $token->{tag_name} eq 'script') {                
4848                if ($self->{insertion_mode} eq 'after head') {                  $self->{insertion_mode} = IN_HEAD_IM;
4849                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  ## Reprocess in the "in head" insertion mode...
4850                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4851                    !!!cp ('t120');
4852                    !!!parse-error (type => 'after head',
4853                                    text => $token->{tag_name}, token => $token);
4854                    push @{$self->{open_elements}},
4855                        [$self->{head_element}, $el_category->{head}];
4856                  } else {
4857                    !!!cp ('t121');
4858                }                }
4859    
4860                ## NOTE: There is a "as if in head" code clone.                ## NOTE: There is a "as if in head" code clone.
4861                $script_start_tag->($insert_to_current);                $script_start_tag->();
4862                pop @{$self->{open_elements}}                pop @{$self->{open_elements}} # <head>
4863                    if $self->{insertion_mode} eq 'after head';                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4864                redo B;                next B;
4865              } 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  
4866                       $token->{tag_name} eq 'frameset') {                       $token->{tag_name} eq 'frameset') {
4867                !!!insert-element ('frameset', $token->{attributes});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4868                $self->{insertion_mode} = 'in frameset';                  !!!cp ('t122');
4869                    ## As if </noscript>
4870                    pop @{$self->{open_elements}};
4871                    !!!parse-error (type => 'in noscript',
4872                                    text => $token->{tag_name}, token => $token);
4873                    
4874                    ## Reprocess in the "in head" insertion mode...
4875                    ## As if </head>
4876                    pop @{$self->{open_elements}};
4877                    
4878                    ## Reprocess in the "after head" insertion mode...
4879                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4880                    !!!cp ('t124');
4881                    pop @{$self->{open_elements}};
4882                    
4883                    ## Reprocess in the "after head" insertion mode...
4884                  } else {
4885                    !!!cp ('t125');
4886                  }
4887    
4888                  ## "after head" insertion mode
4889                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4890                  if ($token->{tag_name} eq 'body') {
4891                    !!!cp ('t126');
4892                    $self->{insertion_mode} = IN_BODY_IM;
4893                  } elsif ($token->{tag_name} eq 'frameset') {
4894                    !!!cp ('t127');
4895                    $self->{insertion_mode} = IN_FRAMESET_IM;
4896                  } else {
4897                    die "$0: tag name: $self->{tag_name}";
4898                  }
4899                  !!!nack ('t127.1');
4900                !!!next-token;                !!!next-token;
4901                redo B;                next B;
4902              } else {              } else {
4903                  !!!cp ('t128');
4904                #                #
4905              }              }
4906            } elsif ($token->{type} eq 'end tag') {  
4907              if ($self->{insertion_mode} eq 'in head' and              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4908                  $token->{tag_name} eq 'head') {                !!!cp ('t129');
4909                  ## As if </noscript>
4910                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4911                $self->{insertion_mode} = 'after head';                !!!parse-error (type => 'in noscript:/',
4912                !!!next-token;                                text => $token->{tag_name}, token => $token);
4913                redo B;                
4914              } elsif ($self->{insertion_mode} eq 'in head noscript' and                ## Reprocess in the "in head" insertion mode...
4915                  $token->{tag_name} eq 'noscript') {                ## As if </head>
4916                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               $self->{insertion_mode} = 'in head';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'in head' and  
                      ($token->{tag_name} eq 'body' or  
                       $token->{tag_name} eq 'html')) {  
               #  
             } elsif ($self->{insertion_mode} ne 'after head') {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
   
           ## As if </head> or </noscript> or <body>  
           if ($self->{insertion_mode} eq 'in head') {  
             pop @{$self->{open_elements}};  
             $self->{insertion_mode} = 'after head';  
           } elsif ($self->{insertion_mode} eq 'in head noscript') {  
             pop @{$self->{open_elements}};  
             !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));  
             $self->{insertion_mode} = 'in head';  
           } else { # 'after head'  
             !!!insert-element ('body');  
             $self->{insertion_mode} = 'in body';  
           }  
           ## reprocess  
           redo B;  
4917    
4918            ## ISSUE: An issue in the spec.                ## Reprocess in the "after head" insertion mode...
4919          } elsif ($self->{insertion_mode} eq 'in body') {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4920            if ($token->{type} eq 'character') {                !!!cp ('t130');
4921              ## NOTE: There is a code clone of "character in body".                ## As if </head>
4922              $reconstruct_active_formatting_elements->($insert_to_current);                pop @{$self->{open_elements}};
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
4923    
4924              !!!next-token;                ## Reprocess in the "after head" insertion mode...
4925              redo B;              } else {
4926            } elsif ($token->{type} eq 'comment') {                !!!cp ('t131');
             ## NOTE: There is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } else {  
             $in_body->($insert_to_current);  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There are "character in table" code clones.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
4927              }              }
4928    
4929              !!!parse-error (type => 'in table:#character');              ## "after head" insertion mode
4930                ## As if <body>
4931                !!!insert-element ('body',, $token);
4932                $self->{insertion_mode} = IN_BODY_IM;
4933                ## reprocess
4934                !!!ack-later;
4935                next B;
4936              } elsif ($token->{type} == END_TAG_TOKEN) {
4937                if ($token->{tag_name} eq 'head') {
4938                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4939                    !!!cp ('t132');
4940                    ## As if <head>
4941                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4942                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4943                    push @{$self->{open_elements}},
4944                        [$self->{head_element}, $el_category->{head}];
4945    
4946              ## As if in body, but insert into foster parent element                  ## Reprocess in the "in head" insertion mode...
4947              ## ISSUE: Spec says that "whenever a node would be inserted                  pop @{$self->{open_elements}};
4948              ## into the current node" while characters might not be                  $self->{insertion_mode} = AFTER_HEAD_IM;
4949              ## result in a new Text node.                  !!!next-token;
4950              $reconstruct_active_formatting_elements->($insert_to_foster);                  next B;
4951                              } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4952              if ({                  !!!cp ('t133');
4953                   table => 1, tbody => 1, tfoot => 1,                  ## As if </noscript>
4954                   thead => 1, tr => 1,                  pop @{$self->{open_elements}};
4955                  }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'in noscript:/',
4956                # MUST                                  text => 'head', token => $token);
4957                my $foster_parent_element;                  
4958                my $next_sibling;                  ## Reprocess in the "in head" insertion mode...
4959                my $prev_sibling;                  pop @{$self->{open_elements}};
4960                OE: for (reverse 0..$#{$self->{open_elements}}) {                  $self->{insertion_mode} = AFTER_HEAD_IM;
4961                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  !!!next-token;
4962                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  next B;
4963                    if (defined $parent and $parent->node_type == 1) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4964                      $foster_parent_element = $parent;                  !!!cp ('t134');
4965                      $next_sibling = $self->{open_elements}->[$_]->[0];                  pop @{$self->{open_elements}};
4966                      $prev_sibling = $next_sibling->previous_sibling;                  $self->{insertion_mode} = AFTER_HEAD_IM;
4967                    } else {                  !!!next-token;
4968                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                  next B;
4969                      $prev_sibling = $foster_parent_element->last_child;                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4970                    }                  !!!cp ('t134.1');
4971                    last OE;                  !!!parse-error (type => 'unmatched end tag', text => 'head',
4972                  }                                  token => $token);
4973                } # OE                  ## Ignore the token
4974                $foster_parent_element = $self->{open_elements}->[0]->[0] and                  !!!next-token;
4975                $prev_sibling = $foster_parent_element->last_child                  next B;
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
4976                } else {                } else {
4977                  $foster_parent_element->insert_before                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
4978                }                }
4979              } else {              } elsif ($token->{tag_name} eq 'noscript') {
4980                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4981              }                  !!!cp ('t136');
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ({  
                  caption => 1,  
                  colgroup => 1,  
                  tbody => 1, tfoot => 1, thead => 1,  
                 }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4982                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
4983                    $self->{insertion_mode} = IN_HEAD_IM;
4984                    !!!next-token;
4985                    next B;
4986                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
4987                           $self->{insertion_mode} == AFTER_HEAD_IM) {
4988                    !!!cp ('t137');
4989                    !!!parse-error (type => 'unmatched end tag',
4990                                    text => 'noscript', token => $token);
4991                    ## Ignore the token ## ISSUE: An issue in the spec.
4992                    !!!next-token;
4993                    next B;
4994                  } else {
4995                    !!!cp ('t138');
4996                    #
4997                }                }
   
               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;  
4998              } elsif ({              } elsif ({
4999                        col => 1,                        body => 1, html => 1,
                       td => 1, th => 1, tr => 1,  
5000                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5001                ## Clear back to table context                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5002                while ($self->{open_elements}->[-1]->[1] ne 'table' and                    $self->{insertion_mode} == IN_HEAD_IM or
5003                       $self->{open_elements}->[-1]->[1] ne 'html') {                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5004                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t140');
5005                  pop @{$self->{open_elements}};                  !!!parse-error (type => 'unmatched end tag',
5006                                    text => $token->{tag_name}, token => $token);
5007                    ## Ignore the token
5008                    !!!next-token;
5009                    next B;
5010                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5011                    !!!cp ('t140.1');
5012                    !!!parse-error (type => 'unmatched end tag',
5013                                    text => $token->{tag_name}, token => $token);
5014                    ## Ignore the token
5015                    !!!next-token;
5016                    next B;
5017                  } else {
5018                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5019                }                }
5020                } elsif ($token->{tag_name} eq 'p') {
5021                  !!!cp ('t142');
5022                  !!!parse-error (type => 'unmatched end tag',
5023                                  text => $token->{tag_name}, token => $token);
5024                  ## Ignore the token
5025                  !!!next-token;
5026                  next B;
5027                } elsif ($token->{tag_name} eq 'br') {
5028                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5029                    !!!cp ('t142.2');
5030                    ## (before head) as if <head>, (in head) as if </head>
5031                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5032                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5033                    $self->{insertion_mode} = AFTER_HEAD_IM;
5034      
5035                    ## Reprocess in the "after head" insertion mode...
5036                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5037                    !!!cp ('t143.2');
5038                    ## As if </head>
5039                    pop @{$self->{open_elements}};
5040                    $self->{insertion_mode} = AFTER_HEAD_IM;
5041      
5042                    ## Reprocess in the "after head" insertion mode...
5043                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5044                    !!!cp ('t143.3');
5045                    ## ISSUE: Two parse errors for <head><noscript></br>
5046                    !!!parse-error (type => 'unmatched end tag',
5047                                    text => 'br', token => $token);
5048                    ## As if </noscript>
5049                    pop @{$self->{open_elements}};
5050                    $self->{insertion_mode} = IN_HEAD_IM;
5051    
5052                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                  ## Reprocess in the "in head" insertion mode...
5053                $self->{insertion_mode} = $token->{tag_name} eq 'col'                  ## As if </head>
5054                  ? 'in column group' : 'in table body';                  pop @{$self->{open_elements}};
5055                ## reprocess                  $self->{insertion_mode} = AFTER_HEAD_IM;
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: There are code clones for this "table in table"  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5056    
5057                ## As if </table>                  ## Reprocess in the "after head" insertion mode...
5058                ## have a table element in table scope                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5059                my $i;                  !!!cp ('t143.4');
5060                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  #
5061                  my $node = $self->{open_elements}->[$_];                } else {
5062                  if ($node->[1] eq 'table') {                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5063                }                }
5064    
5065                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5066                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'unmatched end tag',
5067                }                                text => 'br', token => $token);
5068                  ## Ignore the token
5069                  !!!next-token;
5070                  next B;
5071                } else {
5072                  !!!cp ('t145');
5073                  !!!parse-error (type => 'unmatched end tag',
5074                                  text => $token->{tag_name}, token => $token);
5075                  ## Ignore the token
5076                  !!!next-token;
5077                  next B;
5078                }
5079    
5080                splice @{$self->{open_elements}}, $i;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5081                  !!!cp ('t146');
5082                  ## As if </noscript>
5083                  pop @{$self->{open_elements}};
5084                  !!!parse-error (type => 'in noscript:/',
5085                                  text => $token->{tag_name}, token => $token);
5086                  
5087                  ## Reprocess in the "in head" insertion mode...
5088                  ## As if </head>
5089                  pop @{$self->{open_elements}};
5090    
5091                $self->_reset_insertion_mode;                ## Reprocess in the "after head" insertion mode...
5092                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5093                  !!!cp ('t147');
5094                  ## As if </head>
5095                  pop @{$self->{open_elements}};
5096    
5097                ## reprocess                ## Reprocess in the "after head" insertion mode...
5098                redo B;              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5099    ## ISSUE: This case cannot be reached?
5100                  !!!cp ('t148');
5101                  !!!parse-error (type => 'unmatched end tag',
5102                                  text => $token->{tag_name}, token => $token);
5103                  ## Ignore the token ## ISSUE: An issue in the spec.
5104                  !!!next-token;
5105                  next B;
5106              } else {              } else {
5107                #                !!!cp ('t149');
5108              }              }
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'table') {  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
5109    
5110                if ($self->{open_elements}->[-1]->[1] ne 'table') {              ## "after head" insertion mode
5111                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## As if <body>
5112                }              !!!insert-element ('body',, $token);
5113                $self->{insertion_mode} = IN_BODY_IM;
5114                ## reprocess
5115                next B;
5116          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5117            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5118              !!!cp ('t149.1');
5119    
5120              ## NOTE: As if <head>
5121              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5122              $self->{open_elements}->[-1]->[0]->append_child
5123                  ($self->{head_element});
5124              #push @{$self->{open_elements}},
5125              #    [$self->{head_element}, $el_category->{head}];
5126              #$self->{insertion_mode} = IN_HEAD_IM;
5127              ## NOTE: Reprocess.
5128    
5129              ## NOTE: As if </head>
5130              #pop @{$self->{open_elements}};
5131              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5132              ## NOTE: Reprocess.
5133              
5134              #
5135            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5136              !!!cp ('t149.2');
5137    
5138                splice @{$self->{open_elements}}, $i;            ## NOTE: As if </head>
5139              pop @{$self->{open_elements}};
5140              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5141              ## NOTE: Reprocess.
5142    
5143                $self->_reset_insertion_mode;            #
5144            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5145              !!!cp ('t149.3');
5146    
5147                !!!next-token;            !!!parse-error (type => 'in noscript:#eof', token => $token);
5148                redo B;  
5149              } elsif ({            ## As if </noscript>
5150                        body => 1, caption => 1, col => 1, colgroup => 1,            pop @{$self->{open_elements}};
5151                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,            #$self->{insertion_mode} = IN_HEAD_IM;
5152                        thead => 1, tr => 1,            ## NOTE: Reprocess.
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
5153    
5154            !!!parse-error (type => 'in table:'.$token->{tag_name});            ## NOTE: As if </head>
5155            $in_body->($insert_to_foster);            pop @{$self->{open_elements}};
5156            redo B;            #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5157          } elsif ($self->{insertion_mode} eq 'in caption') {            ## NOTE: Reprocess.
5158            if ($token->{type} eq 'character') {  
5159              ## NOTE: This is a code clone of "character in body".            #
5160            } else {
5161              !!!cp ('t149.4');
5162              #
5163            }
5164    
5165            ## NOTE: As if <body>
5166            !!!insert-element ('body',, $token);
5167            $self->{insertion_mode} = IN_BODY_IM;
5168            ## NOTE: Reprocess.
5169            next B;
5170          } else {
5171            die "$0: $token->{type}: Unknown token type";
5172          }
5173        } elsif ($self->{insertion_mode} & BODY_IMS) {
5174              if ($token->{type} == CHARACTER_TOKEN) {
5175                !!!cp ('t150');
5176                ## NOTE: There is a code clone of "character in body".
5177              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5178                            
5179              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5180    
5181              !!!next-token;              !!!next-token;
5182              redo B;              next B;
5183            } elsif ($token->{type} eq 'comment') {            } elsif ($token->{type} == START_TAG_TOKEN) {
             ## NOTE: This is a code clone of "comment in body".  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
5184              if ({              if ({
5185                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5186                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5187                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5188                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
5189                    ## have an element in table scope
5190                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
5191                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
5192                my $i;                    if ($node->[1] & TABLE_CELL_EL) {
5193                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
5194                  my $node = $self->{open_elements}->[$_];  
5195                  if ($node->[1] eq 'caption') {                      ## Close the cell
5196                    $i = $_;                      !!!back-token; # <x>
5197                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
5198                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
5199                            table => 1, html => 1,                                line => $token->{line},
5200                           }->{$node->[1]}) {                                column => $token->{column}};
5201                    last INSCOPE;                      next B;
5202                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5203                        !!!cp ('t152');
5204                        ## ISSUE: This case can never be reached, maybe.
5205                        last;
5206                      }
5207                  }                  }
5208                } # INSCOPE  
5209                unless (defined $i) {                  !!!cp ('t153');
5210                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
5211                        text => $token->{tag_name}, token => $token);
5212                  ## Ignore the token                  ## Ignore the token
5213                    !!!nack ('t153.1');
5214                  !!!next-token;                  !!!next-token;
5215                  redo B;                  next B;
5216                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5217                                  !!!parse-error (type => 'not closed', text => 'caption',
5218                ## generate implied end tags                                  token => $token);
5219                if ({                  
5220                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5221                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5222                    }->{$self->{open_elements}->[-1]->[1]}) {                  my $i;
5223                  !!!back-token; # <?>                  INSCOPE: {
5224                  $token = {type => 'end tag', tag_name => 'caption'};                    for (reverse 0..$#{$self->{open_elements}}) {
5225                  !!!back-token;                      my $node = $self->{open_elements}->[$_];
5226                  $token = {type => 'end tag',                      if ($node->[1] & CAPTION_EL) {
5227                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        !!!cp ('t155');
5228                  redo B;                        $i = $_;
5229                }                        last INSCOPE;
5230                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5231                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        !!!cp ('t156');
5232                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                        last;
5233                }                      }
5234                      }
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
5235    
5236                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
5237                      !!!parse-error (type => 'start tag not allowed',
5238                                      text => $token->{tag_name}, token => $token);
5239                      ## Ignore the token
5240                      !!!nack ('t157.1');
5241                      !!!next-token;
5242                      next B;
5243                    } # INSCOPE
5244                    
5245                    ## generate implied end tags
5246                    while ($self->{open_elements}->[-1]->[1]
5247                               & END_TAG_OPTIONAL_EL) {
5248                      !!!cp ('t158');
5249                      pop @{$self->{open_elements}};
5250                    }
5251    
5252                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5253                redo B;                    !!!cp ('t159');
5254                      !!!parse-error (type => 'not closed',
5255                                      text => $self->{open_elements}->[-1]->[0]
5256                                          ->manakai_local_name,
5257                                      token => $token);
5258                    } else {
5259                      !!!cp ('t160');
5260                    }
5261                    
5262                    splice @{$self->{open_elements}}, $i;
5263                    
5264                    $clear_up_to_marker->();
5265                    
5266                    $self->{insertion_mode} = IN_TABLE_IM;
5267                    
5268                    ## reprocess
5269                    !!!ack-later;
5270                    next B;
5271                  } else {
5272                    !!!cp ('t161');
5273                    #
5274                  }
5275              } else {              } else {
5276                  !!!cp ('t162');
5277                #                #
5278              }              }
5279            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5280              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5281                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
5282                my $i;                  ## have an element in table scope
5283                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
5284                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5285                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5286                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5287                    last INSCOPE;                      !!!cp ('t163');
5288                  } elsif ({                      $i = $_;
5289                            table => 1, html => 1,                      last INSCOPE;
5290                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5291                    last INSCOPE;                      !!!cp ('t164');
5292                        last INSCOPE;
5293                      }
5294                    } # INSCOPE
5295                      unless (defined $i) {
5296                        !!!cp ('t165');
5297                        !!!parse-error (type => 'unmatched end tag',
5298                                        text => $token->{tag_name},
5299                                        token => $token);
5300                        ## Ignore the token
5301                        !!!next-token;
5302                        next B;
5303                      }
5304                    
5305                    ## generate implied end tags
5306                    while ($self->{open_elements}->[-1]->[1]
5307                               & END_TAG_OPTIONAL_EL) {
5308                      !!!cp ('t166');
5309                      pop @{$self->{open_elements}};
5310                  }                  }
5311                } # INSCOPE  
5312                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5313                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5314                      !!!cp ('t167');
5315                      !!!parse-error (type => 'not closed',
5316                                      text => $self->{open_elements}->[-1]->[0]
5317                                          ->manakai_local_name,
5318                                      token => $token);
5319                    } else {
5320                      !!!cp ('t168');
5321                    }
5322                    
5323                    splice @{$self->{open_elements}}, $i;
5324                    
5325                    $clear_up_to_marker->();
5326                    
5327                    $self->{insertion_mode} = IN_ROW_IM;
5328                    
5329                    !!!next-token;
5330                    next B;
5331                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5332                    !!!cp ('t169');
5333                    !!!parse-error (type => 'unmatched end tag',
5334                                    text => $token->{tag_name}, token => $token);
5335                  ## Ignore the token                  ## Ignore the token
5336                  !!!next-token;                  !!!next-token;
5337                  redo B;                  next B;
5338                }                } else {
5339                                  !!!cp ('t170');
5340                ## generate implied end tags                  #
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5341                }                }
5342                } elsif ($token->{tag_name} eq 'caption') {
5343                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5344                    ## have a table element in table scope
5345                    my $i;
5346                    INSCOPE: {
5347                      for (reverse 0..$#{$self->{open_elements}}) {
5348                        my $node = $self->{open_elements}->[$_];
5349                        if ($node->[1] & CAPTION_EL) {
5350                          !!!cp ('t171');
5351                          $i = $_;
5352                          last INSCOPE;
5353                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5354                          !!!cp ('t172');
5355                          last;
5356                        }
5357                      }
5358    
5359                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5360                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5361                                      text => $token->{tag_name}, token => $token);
5362                      ## Ignore the token
5363                      !!!next-token;
5364                      next B;
5365                    } # INSCOPE
5366                    
5367                    ## generate implied end tags
5368                    while ($self->{open_elements}->[-1]->[1]
5369                               & END_TAG_OPTIONAL_EL) {
5370                      !!!cp ('t174');
5371                      pop @{$self->{open_elements}};
5372                    }
5373                    
5374                    unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5375                      !!!cp ('t175');
5376                      !!!parse-error (type => 'not closed',
5377                                      text => $self->{open_elements}->[-1]->[0]
5378                                          ->manakai_local_name,
5379                                      token => $token);
5380                    } else {
5381                      !!!cp ('t176');
5382                    }
5383                    
5384                    splice @{$self->{open_elements}}, $i;
5385                    
5386                    $clear_up_to_marker->();
5387                    
5388                    $self->{insertion_mode} = IN_TABLE_IM;
5389                    
5390                    !!!next-token;
5391                    next B;
5392                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5393                    !!!cp ('t177');
5394                    !!!parse-error (type => 'unmatched end tag',
5395                                    text => $token->{tag_name}, token => $token);
5396                    ## Ignore the token
5397                    !!!next-token;
5398                    next B;
5399                  } else {
5400                    !!!cp ('t178');
5401                    #
5402                }                }
5403                } elsif ({
5404                          table => 1, tbody => 1, tfoot => 1,
5405                          thead => 1, tr => 1,
5406                         }->{$token->{tag_name}} and
5407                         $self->{insertion_mode} == IN_CELL_IM) {
5408                  ## have an element in table scope
5409                  my $i;
5410                  my $tn;
5411                  INSCOPE: {
5412                    for (reverse 0..$#{$self->{open_elements}}) {
5413                      my $node = $self->{open_elements}->[$_];
5414                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5415                        !!!cp ('t179');
5416                        $i = $_;
5417    
5418                        ## Close the cell
5419                        !!!back-token; # </x>
5420                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5421                                  line => $token->{line},
5422                                  column => $token->{column}};
5423                        next B;
5424                      } elsif ($node->[1] & TABLE_CELL_EL) {
5425                        !!!cp ('t180');
5426                        $tn = $node->[0]->manakai_local_name;
5427                        ## NOTE: There is exactly one |td| or |th| element
5428                        ## in scope in the stack of open elements by definition.
5429                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5430                        ## ISSUE: Can this be reached?
5431                        !!!cp ('t181');
5432                        last;
5433                      }
5434                    }
5435    
5436                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5437                    !!!parse-error (type => 'unmatched end tag',
5438                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5439                    ## Ignore the token
5440                $self->{insertion_mode} = 'in table';                  !!!next-token;
5441                    next B;
5442                !!!next-token;                } # INSCOPE
5443                redo B;              } elsif ($token->{tag_name} eq 'table' and
5444              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5445                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5446                                  token => $token);
5447    
5448                ## As if </caption>                ## As if </caption>
5449                ## have a table element in table scope                ## have a table element in table scope
5450                my $i;                my $i;
5451                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5452                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5453                  if ($node->[1] eq 'caption') {                  if ($node->[1] & CAPTION_EL) {
5454                      !!!cp ('t184');
5455                    $i = $_;                    $i = $_;
5456                    last INSCOPE;                    last INSCOPE;
5457                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5458                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5459                    last INSCOPE;                    last INSCOPE;
5460                  }                  }
5461                } # INSCOPE                } # INSCOPE
5462                unless (defined $i) {                unless (defined $i) {
5463                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5464                    !!!parse-error (type => 'unmatched end tag',
5465                                    text => 'caption', token => $token);
5466                  ## Ignore the token                  ## Ignore the token
5467                  !!!next-token;                  !!!next-token;
5468                  redo B;                  next B;
5469                }                }
5470                                
5471                ## generate implied end tags                ## generate implied end tags
5472                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5473                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5474                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5475                }                }
5476    
5477                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] & CAPTION_EL) {
5478                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5479                    !!!parse-error (type => 'not closed',
5480                                    text => $self->{open_elements}->[-1]->[0]
5481                                        ->manakai_local_name,
5482                                    token => $token);
5483                  } else {
5484                    !!!cp ('t189');
5485                }                }
5486    
5487                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5488    
5489                $clear_up_to_marker->();                $clear_up_to_marker->();
5490    
5491                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5492    
5493                ## reprocess                ## reprocess
5494                redo B;                next B;
5495              } elsif ({              } elsif ({
5496                        body => 1, col => 1, colgroup => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
                       html => 1, tbody => 1, td => 1, tfoot => 1,  
                       th => 1, thead => 1, tr => 1,  
5497                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5498                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5499                ## Ignore the token                  !!!cp ('t190');
5500                redo B;                  !!!parse-error (type => 'unmatched end tag',
5501              } else {                                  text => $token->{tag_name}, token => $token);
               #  
             }  
           } else {  
             #  
           }  
                 
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in column group') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'col') {  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}};  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'colgroup') {  
               if ($self->{open_elements}->[-1]->[1] eq 'html') {  
                 !!!parse-error (type => 'unmatched end tag:colgroup');  
5502                  ## Ignore the token                  ## Ignore the token
5503                  !!!next-token;                  !!!next-token;
5504                  redo B;                  next B;
5505                } else {                } else {
5506                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
5507                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5508                }                }
5509              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5510                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
5511                          thead => 1, tr => 1,
5512                         }->{$token->{tag_name}} and
5513                         $self->{insertion_mode} == IN_CAPTION_IM) {
5514                  !!!cp ('t192');
5515                  !!!parse-error (type => 'unmatched end tag',
5516                                  text => $token->{tag_name}, token => $token);
5517                ## Ignore the token                ## Ignore the token
5518                !!!next-token;                !!!next-token;
5519                redo B;                next B;
5520              } else {              } else {
5521                #                !!!cp ('t193');
5522                  #
5523              }              }
5524            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5525              #          for my $entry (@{$self->{open_elements}}) {
5526              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5527                !!!cp ('t75');
5528                !!!parse-error (type => 'in body:#eof', token => $token);
5529                last;
5530            }            }
5531            }
5532    
5533            ## As if </colgroup>          ## Stop parsing.
5534            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
5535              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
5536              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5537          }
5538    
5539          $insert = $insert_to_current;
5540          #
5541        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5542          if ($token->{type} == CHARACTER_TOKEN) {
5543            if (not $open_tables->[-1]->[1] and # tainted
5544                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5545              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5546                  
5547              unless (length $token->{data}) {
5548                !!!cp ('t194');
5549              !!!next-token;              !!!next-token;
5550              redo B;              next B;
5551            } else {            } else {
5552              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
5553            }            }
5554          } 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;  
               }  
             }  
5555    
5556              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5557    
5558              ## As if in body, but insert into foster parent element              ## As if in body, but insert into foster parent element
5559              ## ISSUE: Spec says that "whenever a node would be inserted              ## ISSUE: Spec says that "whenever a node would be inserted
5560              ## into the current node" while characters might not be              ## into the current node" while characters might not be
5561              ## result in a new Text node.              ## result in a new Text node.
5562              $reconstruct_active_formatting_elements->($insert_to_foster);              $reconstruct_active_formatting_elements->($insert_to_foster);
5563                
5564              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]}) {  
5565                # MUST                # MUST
5566                my $foster_parent_element;                my $foster_parent_element;
5567                my $next_sibling;                my $next_sibling;
5568                my $prev_sibling;                my $prev_sibling;
5569                OE: for (reverse 0..$#{$self->{open_elements}}) {                OE: for (reverse 0..$#{$self->{open_elements}}) {
5570                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  if ($self->{open_elements}->[$_]->[1] & TABLE_EL) {
5571                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5572                    if (defined $parent and $parent->node_type == 1) {                    if (defined $parent and $parent->node_type == 1) {
5573                        !!!cp ('t196');
5574                      $foster_parent_element = $parent;                      $foster_parent_element = $parent;
5575                      $next_sibling = $self->{open_elements}->[$_]->[0];                      $next_sibling = $self->{open_elements}->[$_]->[0];
5576                      $prev_sibling = $next_sibling->previous_sibling;                      $prev_sibling = $next_sibling->previous_sibling;
5577                    } else {                    } else {
5578                        !!!cp ('t197');
5579                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5580                      $prev_sibling = $foster_parent_element->last_child;                      $prev_sibling = $foster_parent_element->last_child;
5581                    }                    }
# Line 4009  sub _tree_construction_main ($) { Line 5587  sub _tree_construction_main ($) {
5587                  unless defined $foster_parent_element;                  unless defined $foster_parent_element;
5588                if (defined $prev_sibling and                if (defined $prev_sibling and
5589                    $prev_sibling->node_type == 3) {                    $prev_sibling->node_type == 3) {
5590                    !!!cp ('t198');
5591                  $prev_sibling->manakai_append_text ($token->{data});                  $prev_sibling->manakai_append_text ($token->{data});
5592                } else {                } else {
5593                    !!!cp ('t199');
5594                  $foster_parent_element->insert_before                  $foster_parent_element->insert_before
5595                    ($self->{document}->create_text_node ($token->{data}),                    ($self->{document}->create_text_node ($token->{data}),
5596                     $next_sibling);                     $next_sibling);
5597                }                }
5598              } else {            $open_tables->[-1]->[1] = 1; # tainted
5599                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
5600              !!!cp ('t200');
5601              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5602            }
5603                
5604            !!!next-token;
5605            next B;
5606          } elsif ($token->{type} == START_TAG_TOKEN) {
5607            if ({
5608                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5609                 th => 1, td => 1,
5610                }->{$token->{tag_name}}) {
5611              if ($self->{insertion_mode} == IN_TABLE_IM) {
5612                ## Clear back to table context
5613                while (not ($self->{open_elements}->[-1]->[1]
5614                                & TABLE_SCOPING_EL)) {
5615                  !!!cp ('t201');
5616                  pop @{$self->{open_elements}};
5617              }              }
5618                            
5619              !!!next-token;              !!!insert-element ('tbody',, $token);
5620              redo B;              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5621            } elsif ($token->{type} eq 'comment') {              ## reprocess in the "in table body" insertion mode...
5622              ## Copied from 'in table'            }
5623              my $comment = $self->{document}->create_comment ($token->{data});            
5624              $self->{open_elements}->[-1]->[0]->append_child ($comment);            if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5625              !!!next-token;              unless ($token->{tag_name} eq 'tr') {
5626              redo B;                !!!cp ('t202');
5627            } elsif ($token->{type} eq 'start tag') {                !!!parse-error (type => 'missing start tag:tr', token => $token);
5628              if ({              }
5629                   tr => 1,                  
5630                   th => 1, td => 1,              ## Clear back to table body context
5631                  }->{$token->{tag_name}}) {              while (not ($self->{open_elements}->[-1]->[1]
5632                unless ($token->{tag_name} eq 'tr') {                              & TABLE_ROWS_SCOPING_EL)) {
5633                  !!!parse-error (type => 'missing start tag:tr');                !!!cp ('t203');
5634                  ## ISSUE: Can this case be reached?
5635                  pop @{$self->{open_elements}};
5636                }
5637                    
5638                    $self->{insertion_mode} = IN_ROW_IM;
5639                    if ($token->{tag_name} eq 'tr') {
5640                      !!!cp ('t204');
5641                      !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5642                      !!!nack ('t204');
5643                      !!!next-token;
5644                      next B;
5645                    } else {
5646                      !!!cp ('t205');
5647                      !!!insert-element ('tr',, $token);
5648                      ## reprocess in the "in row" insertion mode
5649                    }
5650                  } else {
5651                    !!!cp ('t206');
5652                }                }
5653    
5654                ## Clear back to table body context                ## Clear back to table row context
5655                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5656                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5657                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5658                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5659                }                }
5660                                
5661                $self->{insertion_mode} = 'in row';                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5662                if ($token->{tag_name} eq 'tr') {                $self->{insertion_mode} = IN_CELL_IM;
5663                  !!!insert-element ($token->{tag_name}, $token->{attributes});  
5664                  !!!next-token;                push @$active_formatting_elements, ['#marker', ''];
5665                } else {                
5666                  !!!insert-element ('tr');                !!!nack ('t207.1');
5667                  ## reprocess                !!!next-token;
5668                }                next B;
               redo B;  
5669              } elsif ({              } elsif ({
5670                        caption => 1, col => 1, colgroup => 1,                        caption => 1, col => 1, colgroup => 1,
5671                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
5672                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5673                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5674                ## have an element in table scope                if ($self->{insertion_mode} == IN_ROW_IM) {
5675                my $i;                  ## As if </tr>
5676                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
5677                  my $node = $self->{open_elements}->[$_];                  my $i;
5678                  if ({                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5679                       tbody => 1, thead => 1, tfoot => 1,                    my $node = $self->{open_elements}->[$_];
5680                      }->{$node->[1]}) {                    if ($node->[1] & TABLE_ROW_EL) {
5681                    $i = $_;                      !!!cp ('t208');
5682                    last INSCOPE;                      $i = $_;
5683                  } elsif ({                      last INSCOPE;
5684                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5685                           }->{$node->[1]}) {                      !!!cp ('t209');
5686                    last INSCOPE;                      last INSCOPE;
5687                      }
5688                    } # INSCOPE
5689                    unless (defined $i) {
5690                      !!!cp ('t210');
5691    ## TODO: This type is wrong.
5692                      !!!parse-error (type => 'unmacthed end tag',
5693                                      text => $token->{tag_name}, token => $token);
5694                      ## Ignore the token
5695                      !!!nack ('t210.1');
5696                      !!!next-token;
5697                      next B;
5698                    }
5699                    
5700                    ## Clear back to table row context
5701                    while (not ($self->{open_elements}->[-1]->[1]
5702                                    & TABLE_ROW_SCOPING_EL)) {
5703                      !!!cp ('t211');
5704                      ## ISSUE: Can this case be reached?
5705                      pop @{$self->{open_elements}};
5706                    }
5707                    
5708                    pop @{$self->{open_elements}}; # tr
5709                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5710                    if ($token->{tag_name} eq 'tr') {
5711                      !!!cp ('t212');
5712                      ## reprocess
5713                      !!!ack-later;
5714                      next B;
5715                    } else {
5716                      !!!cp ('t213');
5717                      ## reprocess in the "in table body" insertion mode...
5718                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5719                }                }
5720    
5721                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5722                while (not {                  ## have an element in table scope
5723                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5724                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5725                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5726                      if ($node->[1] & TABLE_ROW_GROUP_EL) {
5727                        !!!cp ('t214');
5728                        $i = $_;
5729                        last INSCOPE;
5730                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5731                        !!!cp ('t215');
5732                        last INSCOPE;
5733                      }
5734                    } # INSCOPE
5735                    unless (defined $i) {
5736                      !!!cp ('t216');
5737    ## TODO: This erorr type is wrong.
5738                      !!!parse-error (type => 'unmatched end tag',
5739                                      text => $token->{tag_name}, token => $token);
5740                      ## Ignore the token
5741                      !!!nack ('t216.1');
5742                      !!!next-token;
5743                      next B;
5744                    }
5745    
5746                    ## Clear back to table body context
5747                    while (not ($self->{open_elements}->[-1]->[1]
5748                                    & TABLE_ROWS_SCOPING_EL)) {
5749                      !!!cp ('t217');
5750                      ## ISSUE: Can this state be reached?
5751                      pop @{$self->{open_elements}};
5752                    }
5753                    
5754                    ## As if <{current node}>
5755                    ## have an element in table scope
5756                    ## true by definition
5757                    
5758                    ## Clear back to table body context
5759                    ## nop by definition
5760                    
5761                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5762                    $self->{insertion_mode} = IN_TABLE_IM;
5763                    ## reprocess in "in table" insertion mode...
5764                  } else {
5765                    !!!cp ('t218');
5766                }                }
5767    
5768                ## As if <{current node}>                if ($token->{tag_name} eq 'col') {
5769                ## have an element in table scope                  ## Clear back to table context
5770                ## true by definition                  while (not ($self->{open_elements}->[-1]->[1]
5771                                    & TABLE_SCOPING_EL)) {
5772                ## Clear back to table body context                    !!!cp ('t219');
5773                ## nop by definition                    ## ISSUE: Can this state be reached?
5774                      pop @{$self->{open_elements}};
5775                pop @{$self->{open_elements}};                  }
5776                $self->{insertion_mode} = 'in table';                  
5777                ## reprocess                  !!!insert-element ('colgroup',, $token);
5778                redo B;                  $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5779                    ## reprocess
5780                    !!!ack-later;
5781                    next B;
5782                  } elsif ({
5783                            caption => 1,
5784                            colgroup => 1,
5785                            tbody => 1, tfoot => 1, thead => 1,
5786                           }->{$token->{tag_name}}) {
5787                    ## Clear back to table context
5788                    while (not ($self->{open_elements}->[-1]->[1]
5789                                    & TABLE_SCOPING_EL)) {
5790                      !!!cp ('t220');
5791                      ## ISSUE: Can this state be reached?
5792                      pop @{$self->{open_elements}};
5793                    }
5794                    
5795                    push @$active_formatting_elements, ['#marker', '']
5796                        if $token->{tag_name} eq 'caption';
5797                    
5798                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5799                    $self->{insertion_mode} = {
5800                                               caption => IN_CAPTION_IM,
5801                                               colgroup => IN_COLUMN_GROUP_IM,
5802                                               tbody => IN_TABLE_BODY_IM,
5803                                               tfoot => IN_TABLE_BODY_IM,
5804                                               thead => IN_TABLE_BODY_IM,
5805                                              }->{$token->{tag_name}};
5806                    !!!next-token;
5807                    !!!nack ('t220.1');
5808                    next B;
5809                  } else {
5810                    die "$0: in table: <>: $token->{tag_name}";
5811                  }
5812              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5813                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5814                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5815                                      ->manakai_local_name,
5816                                  token => $token);
5817    
5818                ## As if </table>                ## As if </table>
5819                ## have a table element in table scope                ## have a table element in table scope
5820                my $i;                my $i;
5821                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5822                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5823                  if ($node->[1] eq 'table') {                  if ($node->[1] & TABLE_EL) {
5824                      !!!cp ('t221');
5825                    $i = $_;                    $i = $_;
5826                    last INSCOPE;                    last INSCOPE;
5827                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5828                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5829                    last INSCOPE;                    last INSCOPE;
5830                  }                  }
5831                } # INSCOPE                } # INSCOPE
5832                unless (defined $i) {                unless (defined $i) {
5833                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5834    ## TODO: The following is wrong, maybe.
5835                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5836                                    token => $token);
5837                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5838                    !!!nack ('t223.1');
5839                  !!!next-token;                  !!!next-token;
5840                  redo B;                  next B;
5841                }                }
5842                                
5843    ## TODO: Followings are removed from the latest spec.
5844                ## generate implied end tags                ## generate implied end tags
5845                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5846                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5847                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5848                }                }
5849    
5850                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] & TABLE_EL) {
5851                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5852                    ## NOTE: |<table><tr><table>|
5853                    !!!parse-error (type => 'not closed',
5854                                    text => $self->{open_elements}->[-1]->[0]
5855                                        ->manakai_local_name,
5856                                    token => $token);
5857                  } else {
5858                    !!!cp ('t226');
5859                }                }
5860    
5861                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5862                  pop @{$open_tables};
5863    
5864                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5865    
5866                ## reprocess            ## reprocess
5867                redo B;            !!!ack-later;
5868              } else {            next B;
5869                #          } elsif ($token->{tag_name} eq 'style') {
5870              }            if (not $open_tables->[-1]->[1]) { # tainted
5871            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5872              if ({              ## NOTE: This is a "as if in head" code clone.
5873                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5874                  }->{$token->{tag_name}}) {              next B;
5875                ## have an element in table scope            } else {
5876                my $i;              !!!cp ('t227.7');
5877                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              #
5878                  my $node = $self->{open_elements}->[$_];            }
5879                  if ($node->[1] eq $token->{tag_name}) {          } elsif ($token->{tag_name} eq 'script') {
5880                    $i = $_;            if (not $open_tables->[-1]->[1]) { # tainted
5881                    last INSCOPE;              !!!cp ('t227.6');
5882                  } elsif ({              ## NOTE: This is a "as if in head" code clone.
5883                            table => 1, html => 1,              $script_start_tag->();
5884                           }->{$node->[1]}) {              next B;
5885                    last INSCOPE;            } else {
5886                  }              !!!cp ('t227.5');
5887                } # INSCOPE              #
5888                unless (defined $i) {            }
5889                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          } elsif ($token->{tag_name} eq 'input') {
5890                  ## Ignore the token            if (not $open_tables->[-1]->[1]) { # tainted
5891                  !!!next-token;              if ($token->{attributes}->{type}) { ## TODO: case
5892                  redo B;                my $type = lc $token->{attributes}->{type}->{value};
5893                }                if ($type eq 'hidden') {
5894                    !!!cp ('t227.3');
5895                    !!!parse-error (type => 'in table',
5896                                    text => $token->{tag_name}, token => $token);
5897    
5898                ## 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}};  
               }  
5899    
5900                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;  
               }  
5901    
               ## 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]);  
5902                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
   
               ## Clear back to table body context  
               ## nop by definition  
5903    
5904                pop @{$self->{open_elements}};                  !!!next-token;
5905                $self->{insertion_mode} = 'in table';                  !!!ack ('t227.2.1');
5906                ## reprocess                  next B;
5907                redo B;                } else {
5908              } elsif ({                  !!!cp ('t227.2');
5909                        body => 1, caption => 1, col => 1, colgroup => 1,                  #
5910                        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;  
5911              } else {              } else {
5912                  !!!cp ('t227.1');
5913                #                #
5914              }              }
5915            } else {            } else {
5916                !!!cp ('t227.4');
5917              #              #
5918            }            }
5919                      } else {
5920            ## As if in table            !!!cp ('t227');
5921            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5922            $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;  
               }  
             }  
5923    
5924              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table', text => $token->{tag_name},
5925                            token => $token);
5926    
5927              ## As if in body, but insert into foster parent element          $insert = $insert_to_foster;
5928              ## ISSUE: Spec says that "whenever a node would be inserted          #
5929              ## into the current node" while characters might not be        } elsif ($token->{type} == END_TAG_TOKEN) {
5930              ## result in a new Text node.              if ($token->{tag_name} eq 'tr' and
5931              $reconstruct_active_formatting_elements->($insert_to_foster);                  $self->{insertion_mode} == IN_ROW_IM) {
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
   
               push @$active_formatting_elements, ['#marker', ''];  
                 
               !!!next-token;  
               redo B;  
             } elsif ({  
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
5932                ## have an element in table scope                ## have an element in table scope
5933                my $i;                my $i;
5934                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5935                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5936                  if ($node->[1] eq 'tr') {                  if ($node->[1] & TABLE_ROW_EL) {
5937                      !!!cp ('t228');
5938                    $i = $_;                    $i = $_;
5939                    last INSCOPE;                    last INSCOPE;
5940                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5941                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
5942                    last INSCOPE;                    last INSCOPE;
5943                  }                  }
5944                } # INSCOPE                } # INSCOPE
5945                unless (defined $i) {                unless (defined $i) {
5946                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
5947                    !!!parse-error (type => 'unmatched end tag',
5948                                    text => $token->{tag_name}, token => $token);
5949                  ## Ignore the token                  ## Ignore the token
5950                    !!!nack ('t230.1');
5951                  !!!next-token;                  !!!next-token;
5952                  redo B;                  next B;
5953                  } else {
5954                    !!!cp ('t232');
5955                }                }
5956    
5957                ## Clear back to table row context                ## Clear back to table row context
5958                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5959                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5960                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
5961                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
5962                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5963                }                }
5964    
5965                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
5966                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5967                ## reprocess                !!!next-token;
5968                redo B;                !!!nack ('t231.1');
5969                  next B;
5970              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5971                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
5972                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
5973                    ## have an element in table scope
5974                ## As if </table>                  my $i;
5975                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5976                my $i;                    my $node = $self->{open_elements}->[$_];
5977                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] & TABLE_ROW_EL) {
5978                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
5979                  if ($node->[1] eq 'table') {                      $i = $_;
5980                    $i = $_;                      last INSCOPE;
5981                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5982                  } elsif ({                      !!!cp ('t234');
5983                            table => 1, html => 1,                      last INSCOPE;
5984                           }->{$node->[1]}) {                    }
5985                    last INSCOPE;                  } # INSCOPE
5986                    unless (defined $i) {
5987                      !!!cp ('t235');
5988    ## TODO: The following is wrong.
5989                      !!!parse-error (type => 'unmatched end tag',
5990                                      text => $token->{type}, token => $token);
5991                      ## Ignore the token
5992                      !!!nack ('t236.1');
5993                      !!!next-token;
5994                      next B;
5995                  }                  }
5996                } # INSCOPE                  
5997                unless (defined $i) {                  ## Clear back to table row context
5998                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
5999                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
6000                  !!!next-token;                    !!!cp ('t236');
6001                  redo B;  ## ISSUE: Can this state be reached?
6002                }                    pop @{$self->{open_elements}};
6003                                  }
6004                ## generate implied end tags                  
6005                if ({                  pop @{$self->{open_elements}}; # tr
6006                     dd => 1, dt => 1, li => 1, p => 1,                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6007                     td => 1, th => 1, tr => 1,                  ## reprocess in the "in table body" insertion mode...
6008                    }->{$self->{open_elements}->[-1]->[1]}) {                }
6009                  !!!back-token; # <table>  
6010                  $token = {type => 'end tag', tag_name => 'table'};                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6011                  !!!back-token;                  ## have an element in table scope
6012                  $token = {type => 'end tag',                  my $i;
6013                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6014                  redo B;                    my $node = $self->{open_elements}->[$_];
6015                }                    if ($node->[1] & TABLE_ROW_GROUP_EL) {
6016                        !!!cp ('t237');
6017                if ($self->{open_elements}->[-1]->[1] ne 'table') {                      $i = $_;
6018                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      last INSCOPE;
6019                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6020                        !!!cp ('t238');
6021                        last INSCOPE;
6022                      }
6023                    } # INSCOPE
6024                    unless (defined $i) {
6025                      !!!cp ('t239');
6026                      !!!parse-error (type => 'unmatched end tag',
6027                                      text => $token->{tag_name}, token => $token);
6028                      ## Ignore the token
6029                      !!!nack ('t239.1');
6030                      !!!next-token;
6031                      next B;
6032                    }
6033                    
6034                    ## Clear back to table body context
6035                    while (not ($self->{open_elements}->[-1]->[1]
6036                                    & TABLE_ROWS_SCOPING_EL)) {
6037                      !!!cp ('t240');
6038                      pop @{$self->{open_elements}};
6039                    }
6040                    
6041                    ## As if <{current node}>
6042                    ## have an element in table scope
6043                    ## true by definition
6044                    
6045                    ## Clear back to table body context
6046                    ## nop by definition
6047                    
6048                    pop @{$self->{open_elements}};
6049                    $self->{insertion_mode} = IN_TABLE_IM;
6050                    ## reprocess in the "in table" insertion mode...
6051                }                }
6052    
6053                splice @{$self->{open_elements}}, $i;                ## NOTE: </table> in the "in table" insertion mode.
6054                  ## When you edit the code fragment below, please ensure that
6055                $self->_reset_insertion_mode;                ## the code for <table> in the "in table" insertion mode
6056                  ## is synced with it.
6057    
6058                ## 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  
6059                my $i;                my $i;
6060                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6061                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6062                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] & TABLE_EL) {
6063                      !!!cp ('t241');
6064                    $i = $_;                    $i = $_;
6065                    last INSCOPE;                    last INSCOPE;
6066                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6067                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6068                    last INSCOPE;                    last INSCOPE;
6069                  }                  }
6070                } # INSCOPE                } # INSCOPE
6071                unless (defined $i) {                unless (defined $i) {
6072                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6073                    !!!parse-error (type => 'unmatched end tag',
6074                                    text => $token->{tag_name}, token => $token);
6075                  ## Ignore the token                  ## Ignore the token
6076                    !!!nack ('t243.1');
6077                  !!!next-token;                  !!!next-token;
6078                  redo B;                  next B;
6079                }                }
6080                    
6081                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6082                while (not {                pop @{$open_tables};
6083                  tr => 1, html => 1,                
6084                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6085                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6086                  pop @{$self->{open_elements}};                !!!next-token;
6087                  next B;
6088                } elsif ({
6089                          tbody => 1, tfoot => 1, thead => 1,
6090                         }->{$token->{tag_name}} and
6091                         $self->{insertion_mode} & ROW_IMS) {
6092                  if ($self->{insertion_mode} == IN_ROW_IM) {
6093                    ## have an element in table scope
6094                    my $i;
6095                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6096                      my $node = $self->{open_elements}->[$_];
6097                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6098                        !!!cp ('t247');
6099                        $i = $_;
6100                        last INSCOPE;
6101                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6102                        !!!cp ('t248');
6103                        last INSCOPE;
6104                      }
6105                    } # INSCOPE
6106                      unless (defined $i) {
6107                        !!!cp ('t249');
6108                        !!!parse-error (type => 'unmatched end tag',
6109                                        text => $token->{tag_name}, token => $token);
6110                        ## Ignore the token
6111                        !!!nack ('t249.1');
6112                        !!!next-token;
6113                        next B;
6114                      }
6115                    
6116                    ## As if </tr>
6117                    ## have an element in table scope
6118                    my $i;
6119                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6120                      my $node = $self->{open_elements}->[$_];
6121                      if ($node->[1] & TABLE_ROW_EL) {
6122                        !!!cp ('t250');
6123                        $i = $_;
6124                        last INSCOPE;
6125                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6126                        !!!cp ('t251');
6127                        last INSCOPE;
6128                      }
6129                    } # INSCOPE
6130                      unless (defined $i) {
6131                        !!!cp ('t252');
6132                        !!!parse-error (type => 'unmatched end tag',
6133                                        text => 'tr', token => $token);
6134                        ## Ignore the token
6135                        !!!nack ('t252.1');
6136                        !!!next-token;
6137                        next B;
6138                      }
6139                    
6140                    ## Clear back to table row context
6141                    while (not ($self->{open_elements}->[-1]->[1]
6142                                    & TABLE_ROW_SCOPING_EL)) {
6143                      !!!cp ('t253');
6144    ## ISSUE: Can this case be reached?
6145                      pop @{$self->{open_elements}};
6146                    }
6147                    
6148                    pop @{$self->{open_elements}}; # tr
6149                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
6150                    ## reprocess in the "in table body" insertion mode...
6151                }                }
6152    
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </tr>  
6153                ## have an element in table scope                ## have an element in table scope
6154                my $i;                my $i;
6155                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6156                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6157                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6158                      !!!cp ('t254');
6159                    $i = $_;                    $i = $_;
6160                    last INSCOPE;                    last INSCOPE;
6161                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6162                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6163                    last INSCOPE;                    last INSCOPE;
6164                  }                  }
6165                } # INSCOPE                } # INSCOPE
6166                unless (defined $i) {                unless (defined $i) {
6167                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t256');
6168                    !!!parse-error (type => 'unmatched end tag',
6169                                    text => $token->{tag_name}, token => $token);
6170                  ## Ignore the token                  ## Ignore the token
6171                    !!!nack ('t256.1');
6172                  !!!next-token;                  !!!next-token;
6173                  redo B;                  next B;
6174                }                }
6175    
6176                ## Clear back to table row context                ## Clear back to table body context
6177                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6178                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6179                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6180                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6181                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6182                }                }
6183    
6184                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6185                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6186                ## reprocess                !!!nack ('t257.1');
6187                redo B;                !!!next-token;
6188                  next B;
6189              } elsif ({              } elsif ({
6190                        tbody => 1, tfoot => 1, thead => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6191                          html => 1, td => 1, th => 1,
6192                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6193                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6194                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6195                ## have an element in table scope            !!!cp ('t258');
6196                my $i;            !!!parse-error (type => 'unmatched end tag',
6197                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
6198                  my $node = $self->{open_elements}->[$_];            ## Ignore the token
6199                  if ($node->[1] eq $token->{tag_name}) {            !!!nack ('t258.1');
6200                    $i = $_;             !!!next-token;
6201                    last INSCOPE;            next B;
6202                  } elsif ({          } else {
6203                            table => 1, html => 1,            !!!cp ('t259');
6204                           }->{$node->[1]}) {            !!!parse-error (type => 'in table:/',
6205                    last INSCOPE;                            text => $token->{tag_name}, token => $token);
6206                  }  
6207                } # INSCOPE            $insert = $insert_to_foster;
6208                unless (defined $i) {            #
6209                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          }
6210                  ## Ignore the token        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6211            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6212                    @{$self->{open_elements}} == 1) { # redundant, maybe
6213              !!!parse-error (type => 'in body:#eof', token => $token);
6214              !!!cp ('t259.1');
6215              #
6216            } else {
6217              !!!cp ('t259.2');
6218              #
6219            }
6220    
6221            ## Stop parsing
6222            last B;
6223          } else {
6224            die "$0: $token->{type}: Unknown token type";
6225          }
6226        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6227              if ($token->{type} == CHARACTER_TOKEN) {
6228                if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6229                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6230                  unless (length $token->{data}) {
6231                    !!!cp ('t260');
6232                  !!!next-token;                  !!!next-token;
6233                  redo B;                  next B;
6234                }                }
6235                }
6236                ## As if </tr>              
6237                ## have an element in table scope              !!!cp ('t261');
6238                my $i;              #
6239                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6240                  my $node = $self->{open_elements}->[$_];              if ($token->{tag_name} eq 'col') {
6241                  if ($node->[1] eq 'tr') {                !!!cp ('t262');
6242                    $i = $_;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6243                    last INSCOPE;                pop @{$self->{open_elements}};
6244                  } elsif ({                !!!ack ('t262.1');
6245                            table => 1, html => 1,                !!!next-token;
6246                           }->{$node->[1]}) {                next B;
6247                    last INSCOPE;              } else {
6248                  }                !!!cp ('t263');
6249                } # INSCOPE                #
6250                unless (defined $i) {              }
6251                  !!!parse-error (type => 'unmatched end tag:tr');            } elsif ($token->{type} == END_TAG_TOKEN) {
6252                if ($token->{tag_name} eq 'colgroup') {
6253                  if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6254                    !!!cp ('t264');
6255                    !!!parse-error (type => 'unmatched end tag',
6256                                    text => 'colgroup', token => $token);
6257                  ## Ignore the token                  ## Ignore the token
6258                  !!!next-token;                  !!!next-token;
6259                  redo B;                  next B;
6260                }                } else {
6261                    !!!cp ('t265');
6262                ## Clear back to table row context                  pop @{$self->{open_elements}}; # colgroup
6263                while (not {                  $self->{insertion_mode} = IN_TABLE_IM;
6264                  tr => 1, html => 1,                  !!!next-token;
6265                }->{$self->{open_elements}->[-1]->[1]}) {                  next B;            
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
6266                }                }
6267                } elsif ($token->{tag_name} eq 'col') {
6268                pop @{$self->{open_elements}}; # tr                !!!cp ('t266');
6269                $self->{insertion_mode} = 'in table body';                !!!parse-error (type => 'unmatched end tag',
6270                ## 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});  
6271                ## Ignore the token                ## Ignore the token
6272                !!!next-token;                !!!next-token;
6273                redo B;                next B;
6274              } else {              } else {
6275                #                !!!cp ('t267');
6276                  #
6277              }              }
6278          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6279            if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6280                @{$self->{open_elements}} == 1) { # redundant, maybe
6281              !!!cp ('t270.2');
6282              ## Stop parsing.
6283              last B;
6284            } else {
6285              ## NOTE: As if </colgroup>.
6286              !!!cp ('t270.1');
6287              pop @{$self->{open_elements}}; # colgroup
6288              $self->{insertion_mode} = IN_TABLE_IM;
6289              ## Reprocess.
6290              next B;
6291            }
6292          } else {
6293            die "$0: $token->{type}: Unknown token type";
6294          }
6295    
6296              ## As if </colgroup>
6297              if ($self->{open_elements}->[-1]->[1] & HTML_EL) {
6298                !!!cp ('t269');
6299    ## TODO: Wrong error type?
6300                !!!parse-error (type => 'unmatched end tag',
6301                                text => 'colgroup', token => $token);
6302                ## Ignore the token
6303                !!!nack ('t269.1');
6304                !!!next-token;
6305                next B;
6306            } else {            } else {
6307              #              !!!cp ('t270');
6308                pop @{$self->{open_elements}}; # colgroup
6309                $self->{insertion_mode} = IN_TABLE_IM;
6310                !!!ack-later;
6311                ## reprocess
6312                next B;
6313              }
6314        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6315          if ($token->{type} == CHARACTER_TOKEN) {
6316            !!!cp ('t271');
6317            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6318            !!!next-token;
6319            next B;
6320          } elsif ($token->{type} == START_TAG_TOKEN) {
6321            if ($token->{tag_name} eq 'option') {
6322              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6323                !!!cp ('t272');
6324                ## As if </option>
6325                pop @{$self->{open_elements}};
6326              } else {
6327                !!!cp ('t273');
6328            }            }
6329    
6330            ## As if in table            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6331            !!!parse-error (type => 'in table:'.$token->{tag_name});            !!!nack ('t273.1');
6332            $in_body->($insert_to_foster);            !!!next-token;
6333            redo B;            next B;
6334          } elsif ($self->{insertion_mode} eq 'in cell') {          } elsif ($token->{tag_name} eq 'optgroup') {
6335            if ($token->{type} eq 'character') {            if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6336              ## NOTE: This is a code clone of "character in body".              !!!cp ('t274');
6337              $reconstruct_active_formatting_elements->($insert_to_current);              ## As if </option>
6338                            pop @{$self->{open_elements}};
6339              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            } else {
6340                !!!cp ('t275');
6341              }
6342    
6343              if ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6344                !!!cp ('t276');
6345                ## As if </optgroup>
6346                pop @{$self->{open_elements}};
6347              } else {
6348                !!!cp ('t277');
6349              }
6350    
6351              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6352              !!!nack ('t277.1');
6353              !!!next-token;
6354              next B;
6355            } elsif ({
6356                       select => 1, input => 1, textarea => 1,
6357                     }->{$token->{tag_name}} or
6358                     ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6359                      {
6360                       caption => 1, table => 1,
6361                       tbody => 1, tfoot => 1, thead => 1,
6362                       tr => 1, td => 1, th => 1,
6363                      }->{$token->{tag_name}})) {
6364              ## TODO: The type below is not good - <select> is replaced by </select>
6365              !!!parse-error (type => 'not closed', text => 'select',
6366                              token => $token);
6367              ## NOTE: As if the token were </select> (<select> case) or
6368              ## as if there were </select> (otherwise).
6369              ## have an element in table scope
6370              my $i;
6371              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6372                my $node = $self->{open_elements}->[$_];
6373                if ($node->[1] & SELECT_EL) {
6374                  !!!cp ('t278');
6375                  $i = $_;
6376                  last INSCOPE;
6377                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6378                  !!!cp ('t279');
6379                  last INSCOPE;
6380                }
6381              } # INSCOPE
6382              unless (defined $i) {
6383                !!!cp ('t280');
6384                !!!parse-error (type => 'unmatched end tag',
6385                                text => 'select', token => $token);
6386                ## Ignore the token
6387                !!!nack ('t280.1');
6388              !!!next-token;              !!!next-token;
6389              redo B;              next B;
6390            } elsif ($token->{type} eq 'comment') {            }
6391              ## NOTE: This is a code clone of "comment in body".                
6392              my $comment = $self->{document}->create_comment ($token->{data});            !!!cp ('t281');
6393              $self->{open_elements}->[-1]->[0]->append_child ($comment);            splice @{$self->{open_elements}}, $i;
6394    
6395              $self->_reset_insertion_mode;
6396    
6397              if ($token->{tag_name} eq 'select') {
6398                !!!nack ('t281.2');
6399              !!!next-token;              !!!next-token;
6400              redo B;              next B;
6401            } elsif ($token->{type} eq 'start tag') {            } else {
6402              if ({              !!!cp ('t281.1');
6403                   caption => 1, col => 1, colgroup => 1,              !!!ack-later;
6404                   tbody => 1, td => 1, tfoot => 1, th => 1,              ## Reprocess the token.
6405                   thead => 1, tr => 1,              next B;
6406                  }->{$token->{tag_name}}) {            }
6407                ## have an element in table scope          } else {
6408                my $tn;            !!!cp ('t282');
6409                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            !!!parse-error (type => 'in select',
6410                  my $node = $self->{open_elements}->[$_];                            text => $token->{tag_name}, token => $token);
6411                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {            ## Ignore the token
6412                    $tn = $node->[1];            !!!nack ('t282.1');
6413                    last INSCOPE;            !!!next-token;
6414                  } elsif ({            next B;
6415                            table => 1, html => 1,          }
6416                           }->{$node->[1]}) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6417                    last INSCOPE;          if ($token->{tag_name} eq 'optgroup') {
6418                  }            if ($self->{open_elements}->[-1]->[1] & OPTION_EL and
6419                } # INSCOPE                $self->{open_elements}->[-2]->[1] & OPTGROUP_EL) {
6420                unless (defined $tn) {              !!!cp ('t283');
6421                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              ## As if </option>
6422                  ## Ignore the token              splice @{$self->{open_elements}}, -2;
6423                  !!!next-token;            } elsif ($self->{open_elements}->[-1]->[1] & OPTGROUP_EL) {
6424                  redo B;              !!!cp ('t284');
6425                }              pop @{$self->{open_elements}};
6426              } else {
6427                !!!cp ('t285');
6428                !!!parse-error (type => 'unmatched end tag',
6429                                text => $token->{tag_name}, token => $token);
6430                ## Ignore the token
6431              }
6432              !!!nack ('t285.1');
6433              !!!next-token;
6434              next B;
6435            } elsif ($token->{tag_name} eq 'option') {
6436              if ($self->{open_elements}->[-1]->[1] & OPTION_EL) {
6437                !!!cp ('t286');
6438                pop @{$self->{open_elements}};
6439              } else {
6440                !!!cp ('t287');
6441                !!!parse-error (type => 'unmatched end tag',
6442                                text => $token->{tag_name}, token => $token);
6443                ## Ignore the token
6444              }
6445              !!!nack ('t287.1');
6446              !!!next-token;
6447              next B;
6448            } elsif ($token->{tag_name} eq 'select') {
6449              ## have an element in table scope
6450              my $i;
6451              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6452                my $node = $self->{open_elements}->[$_];
6453                if ($node->[1] & SELECT_EL) {
6454                  !!!cp ('t288');
6455                  $i = $_;
6456                  last INSCOPE;
6457                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6458                  !!!cp ('t289');
6459                  last INSCOPE;
6460                }
6461              } # INSCOPE
6462              unless (defined $i) {
6463                !!!cp ('t290');
6464                !!!parse-error (type => 'unmatched end tag',
6465                                text => $token->{tag_name}, token => $token);
6466                ## Ignore the token
6467                !!!nack ('t290.1');
6468                !!!next-token;
6469                next B;
6470              }
6471                  
6472              !!!cp ('t291');
6473              splice @{$self->{open_elements}}, $i;
6474    
6475                ## Close the cell            $self->_reset_insertion_mode;
6476                !!!back-token; # <?>  
6477                $token = {type => 'end tag', tag_name => $tn};            !!!nack ('t291.1');
6478                redo B;            !!!next-token;
6479              } else {            next B;
6480                #          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6481                     {
6482                      caption => 1, table => 1, tbody => 1,
6483                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6484                     }->{$token->{tag_name}}) {
6485    ## TODO: The following is wrong?
6486              !!!parse-error (type => 'unmatched end tag',
6487                              text => $token->{tag_name}, token => $token);
6488                  
6489              ## have an element in table scope
6490              my $i;
6491              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6492                my $node = $self->{open_elements}->[$_];
6493                if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6494                  !!!cp ('t292');
6495                  $i = $_;
6496                  last INSCOPE;
6497                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6498                  !!!cp ('t293');
6499                  last INSCOPE;
6500              }              }
6501            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6502              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {            unless (defined $i) {
6503                ## have an element in table scope              !!!cp ('t294');
6504                my $i;              ## Ignore the token
6505                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!nack ('t294.1');
6506                  my $node = $self->{open_elements}->[$_];              !!!next-token;
6507                  if ($node->[1] eq $token->{tag_name}) {              next B;
6508                    $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;  
               }  
6509                                
6510                ## generate implied end tags            ## As if </select>
6511                if ({            ## have an element in table scope
6512                     dd => 1, dt => 1, li => 1, p => 1,            undef $i;
6513                     td => ($token->{tag_name} eq 'th'),            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6514                     th => ($token->{tag_name} eq 'td'),              my $node = $self->{open_elements}->[$_];
6515                     tr => 1,              if ($node->[1] & SELECT_EL) {
6516                    }->{$self->{open_elements}->[-1]->[1]}) {                !!!cp ('t295');
6517                  !!!back-token;                $i = $_;
6518                  $token = {type => 'end tag',                last INSCOPE;
6519                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6520                  redo B;  ## ISSUE: Can this state be reached?
6521                }                !!!cp ('t296');
6522                  last INSCOPE;
6523                }
6524              } # INSCOPE
6525              unless (defined $i) {
6526                !!!cp ('t297');
6527    ## TODO: The following error type is correct?
6528                !!!parse-error (type => 'unmatched end tag',
6529                                text => 'select', token => $token);
6530                ## Ignore the </select> token
6531                !!!nack ('t297.1');
6532                !!!next-token; ## TODO: ok?
6533                next B;
6534              }
6535                  
6536              !!!cp ('t298');
6537              splice @{$self->{open_elements}}, $i;
6538    
6539                if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            $self->_reset_insertion_mode;
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
6540    
6541                splice @{$self->{open_elements}}, $i;            !!!ack-later;
6542              ## reprocess
6543              next B;
6544            } else {
6545              !!!cp ('t299');
6546              !!!parse-error (type => 'in select:/',
6547                              text => $token->{tag_name}, token => $token);
6548              ## Ignore the token
6549              !!!nack ('t299.3');
6550              !!!next-token;
6551              next B;
6552            }
6553          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6554            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6555                    @{$self->{open_elements}} == 1) { # redundant, maybe
6556              !!!cp ('t299.1');
6557              !!!parse-error (type => 'in body:#eof', token => $token);
6558            } else {
6559              !!!cp ('t299.2');
6560            }
6561    
6562                $clear_up_to_marker->();          ## Stop parsing.
6563            last B;
6564          } else {
6565            die "$0: $token->{type}: Unknown token type";
6566          }
6567        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6568          if ($token->{type} == CHARACTER_TOKEN) {
6569            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6570              my $data = $1;
6571              ## As if in body
6572              $reconstruct_active_formatting_elements->($insert_to_current);
6573                  
6574              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6575              
6576              unless (length $token->{data}) {
6577                !!!cp ('t300');
6578                !!!next-token;
6579                next B;
6580              }
6581            }
6582            
6583            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6584              !!!cp ('t301');
6585              !!!parse-error (type => 'after html:#text', token => $token);
6586              #
6587            } else {
6588              !!!cp ('t302');
6589              ## "after body" insertion mode
6590              !!!parse-error (type => 'after body:#text', token => $token);
6591              #
6592            }
6593    
6594                $self->{insertion_mode} = 'in row';          $self->{insertion_mode} = IN_BODY_IM;
6595            ## reprocess
6596            next B;
6597          } elsif ($token->{type} == START_TAG_TOKEN) {
6598            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6599              !!!cp ('t303');
6600              !!!parse-error (type => 'after html',
6601                              text => $token->{tag_name}, token => $token);
6602              #
6603            } else {
6604              !!!cp ('t304');
6605              ## "after body" insertion mode
6606              !!!parse-error (type => 'after body',
6607                              text => $token->{tag_name}, token => $token);
6608              #
6609            }
6610    
6611                !!!next-token;          $self->{insertion_mode} = IN_BODY_IM;
6612                redo B;          !!!ack-later;
6613              } elsif ({          ## reprocess
6614                        body => 1, caption => 1, col => 1,          next B;
6615                        colgroup => 1, html => 1,        } elsif ($token->{type} == END_TAG_TOKEN) {
6616                       }->{$token->{tag_name}}) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6617                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t305');
6618                ## Ignore the token            !!!parse-error (type => 'after html:/',
6619                !!!next-token;                            text => $token->{tag_name}, token => $token);
6620                redo B;            
6621              } elsif ({            $self->{insertion_mode} = IN_BODY_IM;
6622                        table => 1, tbody => 1, tfoot => 1,            ## Reprocess.
6623                        thead => 1, tr => 1,            next B;
6624                       }->{$token->{tag_name}}) {          } else {
6625                ## have an element in table scope            !!!cp ('t306');
6626                my $i;          }
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   ## NOTE: There is exactly one |td| or |th| element  
                   ## in scope in the stack of open elements by definition.  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6627    
6628                ## Close the cell          ## "after body" insertion mode
6629                !!!back-token; # </?>          if ($token->{tag_name} eq 'html') {
6630                $token = {type => 'end tag', tag_name => $tn};            if (defined $self->{inner_html_node}) {
6631                redo B;              !!!cp ('t307');
6632              } else {              !!!parse-error (type => 'unmatched end tag',
6633                #                              text => 'html', token => $token);
6634              }              ## Ignore the token
6635                !!!next-token;
6636                next B;
6637            } else {            } else {
6638              #              !!!cp ('t308');
6639                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6640                !!!next-token;
6641                next B;
6642            }            }
6643            } else {
6644              !!!cp ('t309');
6645              !!!parse-error (type => 'after body:/',
6646                              text => $token->{tag_name}, token => $token);
6647    
6648              $self->{insertion_mode} = IN_BODY_IM;
6649              ## reprocess
6650              next B;
6651            }
6652          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6653            !!!cp ('t309.2');
6654            ## Stop parsing
6655            last B;
6656          } else {
6657            die "$0: $token->{type}: Unknown token type";
6658          }
6659        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6660          if ($token->{type} == CHARACTER_TOKEN) {
6661            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6662              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6663                        
6664            $in_body->($insert_to_current);            unless (length $token->{data}) {
6665            redo B;              !!!cp ('t310');
         } elsif ($self->{insertion_mode} eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
6666              !!!next-token;              !!!next-token;
6667              redo B;              next B;
6668            } elsif ($token->{type} eq 'comment') {            }
6669              my $comment = $self->{document}->create_comment ($token->{data});          }
6670              $self->{open_elements}->[-1]->[0]->append_child ($comment);          
6671            if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6672              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6673                !!!cp ('t311');
6674                !!!parse-error (type => 'in frameset:#text', token => $token);
6675              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6676                !!!cp ('t312');
6677                !!!parse-error (type => 'after frameset:#text', token => $token);
6678              } else { # "after after frameset"
6679                !!!cp ('t313');
6680                !!!parse-error (type => 'after html:#text', token => $token);
6681              }
6682              
6683              ## Ignore the token.
6684              if (length $token->{data}) {
6685                !!!cp ('t314');
6686                ## reprocess the rest of characters
6687              } else {
6688                !!!cp ('t315');
6689              !!!next-token;              !!!next-token;
6690              redo B;            }
6691            } elsif ($token->{type} eq 'start tag') {            next B;
6692              if ($token->{tag_name} eq 'option') {          }
6693                if ($self->{open_elements}->[-1]->[1] eq 'option') {          
6694                  ## As if </option>          die qq[$0: Character "$token->{data}"];
6695                  pop @{$self->{open_elements}};        } elsif ($token->{type} == START_TAG_TOKEN) {
6696                }          if ($token->{tag_name} eq 'frameset' and
6697                $self->{insertion_mode} == IN_FRAMESET_IM) {
6698                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6699                !!!next-token;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6700                redo B;            !!!nack ('t318.1');
6701              } elsif ($token->{tag_name} eq 'optgroup') {            !!!next-token;
6702                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6703                  ## As if </option>          } elsif ($token->{tag_name} eq 'frame' and
6704                  pop @{$self->{open_elements}};                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6705                }            !!!cp ('t319');
6706              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6707              pop @{$self->{open_elements}};
6708              !!!ack ('t319.1');
6709              !!!next-token;
6710              next B;
6711            } elsif ($token->{tag_name} eq 'noframes') {
6712              !!!cp ('t320');
6713              ## NOTE: As if in head.
6714              $parse_rcdata->(CDATA_CONTENT_MODEL);
6715              next B;
6716    
6717                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6718                  ## As if </optgroup>            ## has no parse error.
6719                  pop @{$self->{open_elements}};          } else {
6720                }            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6721                !!!cp ('t321');
6722                !!!parse-error (type => 'in frameset',
6723                                text => $token->{tag_name}, token => $token);
6724              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6725                !!!cp ('t322');
6726                !!!parse-error (type => 'after frameset',
6727                                text => $token->{tag_name}, token => $token);
6728              } else { # "after after frameset"
6729                !!!cp ('t322.2');
6730                !!!parse-error (type => 'after after frameset',
6731                                text => $token->{tag_name}, token => $token);
6732              }
6733              ## Ignore the token
6734              !!!nack ('t322.1');
6735              !!!next-token;
6736              next B;
6737            }
6738          } elsif ($token->{type} == END_TAG_TOKEN) {
6739            if ($token->{tag_name} eq 'frameset' and
6740                $self->{insertion_mode} == IN_FRAMESET_IM) {
6741              if ($self->{open_elements}->[-1]->[1] & HTML_EL and
6742                  @{$self->{open_elements}} == 1) {
6743                !!!cp ('t325');
6744                !!!parse-error (type => 'unmatched end tag',
6745                                text => $token->{tag_name}, token => $token);
6746                ## Ignore the token
6747                !!!next-token;
6748              } else {
6749                !!!cp ('t326');
6750                pop @{$self->{open_elements}};
6751                !!!next-token;
6752              }
6753    
6754                !!!insert-element ($token->{tag_name}, $token->{attributes});            if (not defined $self->{inner_html_node} and
6755                !!!next-token;                not ($self->{open_elements}->[-1]->[1] & FRAMESET_EL)) {
6756                redo B;              !!!cp ('t327');
6757              } elsif ($token->{tag_name} eq 'select') {              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6758                !!!parse-error (type => 'not closed:select');            } else {
6759                ## As if </select> instead              !!!cp ('t328');
6760                ## have an element in table scope            }
6761                my $i;            next B;
6762                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          } elsif ($token->{tag_name} eq 'html' and
6763                  my $node = $self->{open_elements}->[$_];                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6764                  if ($node->[1] eq $token->{tag_name}) {            !!!cp ('t329');
6765                    $i = $_;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6766                    last INSCOPE;            !!!next-token;
6767                  } elsif ({            next B;
6768                            table => 1, html => 1,          } else {
6769                           }->{$node->[1]}) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6770                    last INSCOPE;              !!!cp ('t330');
6771                  }              !!!parse-error (type => 'in frameset:/',
6772                } # INSCOPE                              text => $token->{tag_name}, token => $token);
6773                unless (defined $i) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6774                  !!!parse-error (type => 'unmatched end tag:select');              !!!cp ('t330.1');
6775                  ## Ignore the token              !!!parse-error (type => 'after frameset:/',
6776                  !!!next-token;                              text => $token->{tag_name}, token => $token);
6777                  redo B;            } else { # "after after html"
6778                }              !!!cp ('t331');
6779                              !!!parse-error (type => 'after after frameset:/',
6780                splice @{$self->{open_elements}}, $i;                              text => $token->{tag_name}, token => $token);
6781              }
6782              ## Ignore the token
6783              !!!next-token;
6784              next B;
6785            }
6786          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6787            unless ($self->{open_elements}->[-1]->[1] & HTML_EL and
6788                    @{$self->{open_elements}} == 1) { # redundant, maybe
6789              !!!cp ('t331.1');
6790              !!!parse-error (type => 'in body:#eof', token => $token);
6791            } else {
6792              !!!cp ('t331.2');
6793            }
6794            
6795            ## Stop parsing
6796            last B;
6797          } else {
6798            die "$0: $token->{type}: Unknown token type";
6799          }
6800        } else {
6801          die "$0: $self->{insertion_mode}: Unknown insertion mode";
6802        }
6803    
6804                $self->_reset_insertion_mode;      ## "in body" insertion mode
6805        if ($token->{type} == START_TAG_TOKEN) {
6806          if ($token->{tag_name} eq 'script') {
6807            !!!cp ('t332');
6808            ## NOTE: This is an "as if in head" code clone
6809            $script_start_tag->();
6810            next B;
6811          } elsif ($token->{tag_name} eq 'style') {
6812            !!!cp ('t333');
6813            ## NOTE: This is an "as if in head" code clone
6814            $parse_rcdata->(CDATA_CONTENT_MODEL);
6815            next B;
6816          } elsif ({
6817                    base => 1, command => 1, eventsource => 1, link => 1,
6818                   }->{$token->{tag_name}}) {
6819            !!!cp ('t334');
6820            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6821            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6822            pop @{$self->{open_elements}};
6823            !!!ack ('t334.1');
6824            !!!next-token;
6825            next B;
6826          } elsif ($token->{tag_name} eq 'meta') {
6827            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6828            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6829            my $meta_el = pop @{$self->{open_elements}};
6830    
6831                !!!next-token;          unless ($self->{confident}) {
6832                redo B;            if ($token->{attributes}->{charset}) {
6833              } else {              !!!cp ('t335');
6834                #              ## NOTE: Whether the encoding is supported or not is handled
6835                ## in the {change_encoding} callback.
6836                $self->{change_encoding}
6837                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6838                
6839                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6840                    ->set_user_data (manakai_has_reference =>
6841                                         $token->{attributes}->{charset}
6842                                             ->{has_reference});
6843              } elsif ($token->{attributes}->{content}) {
6844                if ($token->{attributes}->{content}->{value}
6845                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6846                        [\x09\x0A\x0C\x0D\x20]*=
6847                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6848                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6849                       /x) {
6850                  !!!cp ('t336');
6851                  ## NOTE: Whether the encoding is supported or not is handled
6852                  ## in the {change_encoding} callback.
6853                  $self->{change_encoding}
6854                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6855                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6856                      ->set_user_data (manakai_has_reference =>
6857                                           $token->{attributes}->{content}
6858                                                 ->{has_reference});
6859              }              }
6860            } elsif ($token->{type} eq 'end tag') {            }
6861              if ($token->{tag_name} eq 'optgroup') {          } else {
6862                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($token->{attributes}->{charset}) {
6863                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!cp ('t337');
6864                  ## As if </option>              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6865                  splice @{$self->{open_elements}}, -2;                  ->set_user_data (manakai_has_reference =>
6866                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {                                       $token->{attributes}->{charset}
6867                  pop @{$self->{open_elements}};                                           ->{has_reference});
6868                } else {            }
6869                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            if ($token->{attributes}->{content}) {
6870                  ## Ignore the token              !!!cp ('t338');
6871                }              $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6872                !!!next-token;                  ->set_user_data (manakai_has_reference =>
6873                redo B;                                       $token->{attributes}->{content}
6874              } elsif ($token->{tag_name} eq 'option') {                                           ->{has_reference});
6875                if ($self->{open_elements}->[-1]->[1] eq 'option') {            }
6876                  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;  
               }  
                 
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
6877    
6878                !!!next-token;          !!!ack ('t338.1');
6879                redo B;          !!!next-token;
6880              } elsif ({          next B;
6881                        caption => 1, table => 1, tbody => 1,        } elsif ($token->{tag_name} eq 'title') {
6882                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,          !!!cp ('t341');
6883                       }->{$token->{tag_name}}) {          ## NOTE: This is an "as if in head" code clone
6884                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6885                          next B;
6886                ## have an element in table scope        } elsif ($token->{tag_name} eq 'body') {
6887                my $i;          !!!parse-error (type => 'in body', text => 'body', token => $token);
               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) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## As if </select>  
               ## have an element in table scope  
               undef $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'select') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:select');  
                 ## Ignore the </select> token  
                 !!!next-token; ## TODO: ok?  
                 redo B;  
               }  
6888                                
6889                splice @{$self->{open_elements}}, $i;          if (@{$self->{open_elements}} == 1 or
6890                not ($self->{open_elements}->[1]->[1] & BODY_EL)) {
6891              !!!cp ('t342');
6892              ## Ignore the token
6893            } else {
6894              my $body_el = $self->{open_elements}->[1]->[0];
6895              for my $attr_name (keys %{$token->{attributes}}) {
6896                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6897                  !!!cp ('t343');
6898                  $body_el->set_attribute_ns
6899                    (undef, [undef, $attr_name],
6900                     $token->{attributes}->{$attr_name}->{value});
6901                }
6902              }
6903            }
6904            !!!nack ('t343.1');
6905            !!!next-token;
6906            next B;
6907          } elsif ({
6908                    ## NOTE: Start tags for non-phrasing flow content elements
6909    
6910                $self->_reset_insertion_mode;                  ## NOTE: The normal one
6911                    address => 1, article => 1, aside => 1, blockquote => 1,
6912                    center => 1, datagrid => 1, details => 1, dialog => 1,
6913                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6914                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6915                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6916                    section => 1, ul => 1,
6917                    ## NOTE: As normal, but drops leading newline
6918                    pre => 1, listing => 1,
6919                    ## NOTE: As normal, but interacts with the form element pointer
6920                    form => 1,
6921                    
6922                    table => 1,
6923                    hr => 1,
6924                   }->{$token->{tag_name}}) {
6925            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6926              !!!cp ('t350');
6927              !!!parse-error (type => 'in form:form', token => $token);
6928              ## Ignore the token
6929              !!!nack ('t350.1');
6930              !!!next-token;
6931              next B;
6932            }
6933    
6934                ## reprocess          ## has a p element in scope
6935                redo B;          INSCOPE: for (reverse @{$self->{open_elements}}) {
6936              if ($_->[1] & P_EL) {
6937                !!!cp ('t344');
6938                !!!back-token; # <form>
6939                $token = {type => END_TAG_TOKEN, tag_name => 'p',
6940                          line => $token->{line}, column => $token->{column}};
6941                next B;
6942              } elsif ($_->[1] & SCOPING_EL) {
6943                !!!cp ('t345');
6944                last INSCOPE;
6945              }
6946            } # INSCOPE
6947              
6948            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6949            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
6950              !!!nack ('t346.1');
6951              !!!next-token;
6952              if ($token->{type} == CHARACTER_TOKEN) {
6953                $token->{data} =~ s/^\x0A//;
6954                unless (length $token->{data}) {
6955                  !!!cp ('t346');
6956                  !!!next-token;
6957              } else {              } else {
6958                #                !!!cp ('t349');
6959              }              }
6960            } else {            } else {
6961              #              !!!cp ('t348');
6962            }            }
6963            } elsif ($token->{tag_name} eq 'form') {
6964              !!!cp ('t347.1');
6965              $self->{form_element} = $self->{open_elements}->[-1]->[0];
6966    
6967            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!nack ('t347.2');
           ## Ignore the token  
6968            !!!next-token;            !!!next-token;
6969            redo B;          } elsif ($token->{tag_name} eq 'table') {
6970          } elsif ($self->{insertion_mode} eq 'after body') {            !!!cp ('t382');
6971            if ($token->{type} eq 'character') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
6972              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            
6973                ## As if in body            $self->{insertion_mode} = IN_TABLE_IM;
               $reconstruct_active_formatting_elements->($insert_to_current);  
                 
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
6974    
6975                unless (length $token->{data}) {            !!!nack ('t382.1');
6976                  !!!next-token;            !!!next-token;
6977                  redo B;          } elsif ($token->{tag_name} eq 'hr') {
6978                }            !!!cp ('t386');
6979              }            pop @{$self->{open_elements}};
6980                        
6981              #            !!!nack ('t386.1');
6982              !!!parse-error (type => 'after body:#'.$token->{type});            !!!next-token;
6983            } elsif ($token->{type} eq 'comment') {          } else {
6984              my $comment = $self->{document}->create_comment ($token->{data});            !!!nack ('t347.1');
6985              $self->{open_elements}->[0]->[0]->append_child ($comment);            !!!next-token;
6986              !!!next-token;          }
6987              redo B;          next B;
6988            } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{tag_name} eq 'li') {
6989              !!!parse-error (type => 'after body:'.$token->{tag_name});          ## NOTE: As normal, but imply </li> when there's another <li> ...
6990              #  
6991            } elsif ($token->{type} eq 'end tag') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
6992              if ($token->{tag_name} eq 'html') {            ## Interpreted as <li><foo/></li><li/> (non-conforming)
6993                if (defined $self->{inner_html_node}) {            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
6994                  !!!parse-error (type => 'unmatched end tag:html');            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
6995                  ## Ignore the token            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
6996                  !!!next-token;            ## object (Fx)
6997                  redo B;            ## Generate non-tree (non-conforming)
6998              ## basefont (IE7 (where basefont is non-void)), center (IE),
6999              ## form (IE), hn (IE)
7000            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7001              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7002              ## div (Fx, S)
7003    
7004            my $non_optional;
7005            my $i = -1;
7006    
7007            ## 1.
7008            for my $node (reverse @{$self->{open_elements}}) {
7009              if ($node->[1] & LI_EL) {
7010                ## 2. (a) As if </li>
7011                {
7012                  ## If no </li> - not applied
7013                  #
7014    
7015                  ## Otherwise
7016    
7017                  ## 1. generate implied end tags, except for </li>
7018                  #
7019    
7020                  ## 2. If current node != "li", parse error
7021                  if ($non_optional) {
7022                    !!!parse-error (type => 'not closed',
7023                                    text => $non_optional->[0]->manakai_local_name,
7024                                    token => $token);
7025                    !!!cp ('t355');
7026                } else {                } else {
7027                  $phase = 'trailing end';                  !!!cp ('t356');
                 !!!next-token;  
                 redo B;  
7028                }                }
7029              } else {  
7030                !!!parse-error (type => 'after body:/'.$token->{tag_name});                ## 3. Pop
7031                  splice @{$self->{open_elements}}, $i;
7032              }              }
7033    
7034                last; ## 2. (b) goto 5.
7035              } elsif (
7036                       ## NOTE: not "formatting" and not "phrasing"
7037                       ($node->[1] & SPECIAL_EL or
7038                        $node->[1] & SCOPING_EL) and
7039                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7040    
7041                       (not $node->[1] & ADDRESS_EL) &
7042                       (not $node->[1] & DIV_EL) &
7043                       (not $node->[1] & P_EL)) {
7044                ## 3.
7045                !!!cp ('t357');
7046                last; ## goto 5.
7047              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7048                !!!cp ('t358');
7049                #
7050            } else {            } else {
7051              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t359');
7052                $non_optional ||= $node;
7053                #
7054            }            }
7055              ## 4.
7056              ## goto 2.
7057              $i--;
7058            }
7059    
7060            $self->{insertion_mode} = 'in body';          ## 5. (a) has a |p| element in scope
7061            ## reprocess          INSCOPE: for (reverse @{$self->{open_elements}}) {
7062            redo B;            if ($_->[1] & P_EL) {
7063          } elsif ($self->{insertion_mode} eq 'in frameset') {              !!!cp ('t353');
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
7064    
7065                unless (length $token->{data}) {              ## NOTE: |<p><li>|, for example.
                 !!!next-token;  
                 redo B;  
               }  
             }  
7066    
7067              #              !!!back-token; # <x>
7068            } elsif ($token->{type} eq 'comment') {              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7069              my $comment = $self->{document}->create_comment ($token->{data});                        line => $token->{line}, column => $token->{column}};
7070              $self->{open_elements}->[-1]->[0]->append_child ($comment);              next B;
7071              !!!next-token;            } elsif ($_->[1] & SCOPING_EL) {
7072              redo B;              !!!cp ('t354');
7073            } elsif ($token->{type} eq 'start tag') {              last INSCOPE;
7074              if ($token->{tag_name} eq 'frameset') {            }
7075                !!!insert-element ($token->{tag_name}, $token->{attributes});          } # INSCOPE
7076                !!!next-token;  
7077                redo B;          ## 5. (b) insert
7078              } elsif ($token->{tag_name} eq 'frame') {          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7079                !!!insert-element ($token->{tag_name}, $token->{attributes});          !!!nack ('t359.1');
7080                pop @{$self->{open_elements}};          !!!next-token;
7081                !!!next-token;          next B;
7082                redo B;        } elsif ($token->{tag_name} eq 'dt' or
7083              } elsif ($token->{tag_name} eq 'noframes') {                 $token->{tag_name} eq 'dd') {
7084                $in_body->($insert_to_current);          ## NOTE: As normal, but imply </dt> or </dd> when ...
7085                redo B;  
7086              } else {          my $non_optional;
7087            my $i = -1;
7088    
7089            ## 1.
7090            for my $node (reverse @{$self->{open_elements}}) {
7091              if ($node->[1] & DT_EL or $node->[1] & DD_EL) {
7092                ## 2. (a) As if </li>
7093                {
7094                  ## If no </li> - not applied
7095                #                #
7096              }  
7097            } elsif ($token->{type} eq 'end tag') {                ## Otherwise
7098              if ($token->{tag_name} eq 'frameset') {  
7099                if ($self->{open_elements}->[-1]->[1] eq 'html' and                ## 1. generate implied end tags, except for </dt> or </dd>
7100                    @{$self->{open_elements}} == 1) {                #
7101                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
7102                  ## Ignore the token                ## 2. If current node != "dt"|"dd", parse error
7103                  !!!next-token;                if ($non_optional) {
7104                    !!!parse-error (type => 'not closed',
7105                                    text => $non_optional->[0]->manakai_local_name,
7106                                    token => $token);
7107                    !!!cp ('t355.1');
7108                } else {                } else {
7109                  pop @{$self->{open_elements}};                  !!!cp ('t356.1');
                 !!!next-token;  
7110                }                }
7111                  
7112                ## if not inner_html and                ## 3. Pop
7113                if ($self->{open_elements}->[-1]->[1] ne 'frameset') {                splice @{$self->{open_elements}}, $i;
                 $self->{insertion_mode} = 'after frameset';  
               }  
               redo B;  
             } else {  
               #  
7114              }              }
7115    
7116                last; ## 2. (b) goto 5.
7117              } elsif (
7118                       ## NOTE: not "formatting" and not "phrasing"
7119                       ($node->[1] & SPECIAL_EL or
7120                        $node->[1] & SCOPING_EL) and
7121                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7122    
7123                       (not $node->[1] & ADDRESS_EL) &
7124                       (not $node->[1] & DIV_EL) &
7125                       (not $node->[1] & P_EL)) {
7126                ## 3.
7127                !!!cp ('t357.1');
7128                last; ## goto 5.
7129              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7130                !!!cp ('t358.1');
7131                #
7132            } else {            } else {
7133                !!!cp ('t359.1');
7134                $non_optional ||= $node;
7135              #              #
7136            }            }
7137              ## 4.
7138              ## goto 2.
7139              $i--;
7140            }
7141    
7142            ## 5. (a) has a |p| element in scope
7143            INSCOPE: for (reverse @{$self->{open_elements}}) {
7144              if ($_->[1] & P_EL) {
7145                !!!cp ('t353.1');
7146                !!!back-token; # <x>
7147                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7148                          line => $token->{line}, column => $token->{column}};
7149                next B;
7150              } elsif ($_->[1] & SCOPING_EL) {
7151                !!!cp ('t354.1');
7152                last INSCOPE;
7153              }
7154            } # INSCOPE
7155    
7156            ## 5. (b) insert
7157            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7158            !!!nack ('t359.2');
7159            !!!next-token;
7160            next B;
7161          } elsif ($token->{tag_name} eq 'plaintext') {
7162            ## NOTE: As normal, but effectively ends parsing
7163    
7164            ## has a p element in scope
7165            INSCOPE: for (reverse @{$self->{open_elements}}) {
7166              if ($_->[1] & P_EL) {
7167                !!!cp ('t367');
7168                !!!back-token; # <plaintext>
7169                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7170                          line => $token->{line}, column => $token->{column}};
7171                next B;
7172              } elsif ($_->[1] & SCOPING_EL) {
7173                !!!cp ('t368');
7174                last INSCOPE;
7175              }
7176            } # INSCOPE
7177                        
7178            if (defined $token->{tag_name}) {          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7179              !!!parse-error (type => 'in frameset:'.$token->{tag_name});            
7180            } else {          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7181              !!!parse-error (type => 'in frameset:#'.$token->{type});            
7182            !!!nack ('t368.1');
7183            !!!next-token;
7184            next B;
7185          } elsif ($token->{tag_name} eq 'a') {
7186            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7187              my $node = $active_formatting_elements->[$i];
7188              if ($node->[1] & A_EL) {
7189                !!!cp ('t371');
7190                !!!parse-error (type => 'in a:a', token => $token);
7191                
7192                !!!back-token; # <a>
7193                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7194                          line => $token->{line}, column => $token->{column}};
7195                $formatting_end_tag->($token);
7196                
7197                AFE2: for (reverse 0..$#$active_formatting_elements) {
7198                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7199                    !!!cp ('t372');
7200                    splice @$active_formatting_elements, $_, 1;
7201                    last AFE2;
7202                  }
7203                } # AFE2
7204                OE: for (reverse 0..$#{$self->{open_elements}}) {
7205                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7206                    !!!cp ('t373');
7207                    splice @{$self->{open_elements}}, $_, 1;
7208                    last OE;
7209                  }
7210                } # OE
7211                last AFE;
7212              } elsif ($node->[0] eq '#marker') {
7213                !!!cp ('t374');
7214                last AFE;
7215              }
7216            } # AFE
7217              
7218            $reconstruct_active_formatting_elements->($insert_to_current);
7219    
7220            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7221            push @$active_formatting_elements, $self->{open_elements}->[-1];
7222    
7223            !!!nack ('t374.1');
7224            !!!next-token;
7225            next B;
7226          } elsif ($token->{tag_name} eq 'nobr') {
7227            $reconstruct_active_formatting_elements->($insert_to_current);
7228    
7229            ## has a |nobr| element in scope
7230            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7231              my $node = $self->{open_elements}->[$_];
7232              if ($node->[1] & NOBR_EL) {
7233                !!!cp ('t376');
7234                !!!parse-error (type => 'in nobr:nobr', token => $token);
7235                !!!back-token; # <nobr>
7236                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7237                          line => $token->{line}, column => $token->{column}};
7238                next B;
7239              } elsif ($node->[1] & SCOPING_EL) {
7240                !!!cp ('t377');
7241                last INSCOPE;
7242            }            }
7243            } # INSCOPE
7244            
7245            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7246            push @$active_formatting_elements, $self->{open_elements}->[-1];
7247            
7248            !!!nack ('t377.1');
7249            !!!next-token;
7250            next B;
7251          } elsif ($token->{tag_name} eq 'button') {
7252            ## has a button element in scope
7253            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7254              my $node = $self->{open_elements}->[$_];
7255              if ($node->[1] & BUTTON_EL) {
7256                !!!cp ('t378');
7257                !!!parse-error (type => 'in button:button', token => $token);
7258                !!!back-token; # <button>
7259                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7260                          line => $token->{line}, column => $token->{column}};
7261                next B;
7262              } elsif ($node->[1] & SCOPING_EL) {
7263                !!!cp ('t379');
7264                last INSCOPE;
7265              }
7266            } # INSCOPE
7267              
7268            $reconstruct_active_formatting_elements->($insert_to_current);
7269              
7270            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7271    
7272            ## TODO: associate with $self->{form_element} if defined
7273    
7274            push @$active_formatting_elements, ['#marker', ''];
7275    
7276            !!!nack ('t379.1');
7277            !!!next-token;
7278            next B;
7279          } elsif ({
7280                    xmp => 1,
7281                    iframe => 1,
7282                    noembed => 1,
7283                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7284                    noscript => 0, ## TODO: 1 if scripting is enabled
7285                   }->{$token->{tag_name}}) {
7286            if ($token->{tag_name} eq 'xmp') {
7287              !!!cp ('t381');
7288              $reconstruct_active_formatting_elements->($insert_to_current);
7289            } else {
7290              !!!cp ('t399');
7291            }
7292            ## NOTE: There is an "as if in body" code clone.
7293            $parse_rcdata->(CDATA_CONTENT_MODEL);
7294            next B;
7295          } elsif ($token->{tag_name} eq 'isindex') {
7296            !!!parse-error (type => 'isindex', token => $token);
7297            
7298            if (defined $self->{form_element}) {
7299              !!!cp ('t389');
7300            ## Ignore the token            ## Ignore the token
7301              !!!nack ('t389'); ## NOTE: Not acknowledged.
7302            !!!next-token;            !!!next-token;
7303            redo B;            next B;
7304          } elsif ($self->{insertion_mode} eq 'after frameset') {          } else {
7305            if ($token->{type} eq 'character') {            !!!ack ('t391.1');
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
   
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
7306    
7307              #            my $at = $token->{attributes};
7308            } elsif ($token->{type} eq 'comment') {            my $form_attrs;
7309              my $comment = $self->{document}->create_comment ($token->{data});            $form_attrs->{action} = $at->{action} if $at->{action};
7310              $self->{open_elements}->[-1]->[0]->append_child ($comment);            my $prompt_attr = $at->{prompt};
7311              $at->{name} = {name => 'name', value => 'isindex'};
7312              delete $at->{action};
7313              delete $at->{prompt};
7314              my @tokens = (
7315                            {type => START_TAG_TOKEN, tag_name => 'form',
7316                             attributes => $form_attrs,
7317                             line => $token->{line}, column => $token->{column}},
7318                            {type => START_TAG_TOKEN, tag_name => 'hr',
7319                             line => $token->{line}, column => $token->{column}},
7320                            {type => START_TAG_TOKEN, tag_name => 'p',
7321                             line => $token->{line}, column => $token->{column}},
7322                            {type => START_TAG_TOKEN, tag_name => 'label',
7323                             line => $token->{line}, column => $token->{column}},
7324                           );
7325              if ($prompt_attr) {
7326                !!!cp ('t390');
7327                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7328                               #line => $token->{line}, column => $token->{column},
7329                              };
7330              } else {
7331                !!!cp ('t391');
7332                push @tokens, {type => CHARACTER_TOKEN,
7333                               data => 'This is a searchable index. Insert your search keywords here: ',
7334                               #line => $token->{line}, column => $token->{column},
7335                              }; # SHOULD
7336                ## TODO: make this configurable
7337              }
7338              push @tokens,
7339                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7340                             line => $token->{line}, column => $token->{column}},
7341                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7342                            {type => END_TAG_TOKEN, tag_name => 'label',
7343                             line => $token->{line}, column => $token->{column}},
7344                            {type => END_TAG_TOKEN, tag_name => 'p',
7345                             line => $token->{line}, column => $token->{column}},
7346                            {type => START_TAG_TOKEN, tag_name => 'hr',
7347                             line => $token->{line}, column => $token->{column}},
7348                            {type => END_TAG_TOKEN, tag_name => 'form',
7349                             line => $token->{line}, column => $token->{column}};
7350              !!!back-token (@tokens);
7351              !!!next-token;
7352              next B;
7353            }
7354          } elsif ($token->{tag_name} eq 'textarea') {
7355            my $tag_name = $token->{tag_name};
7356            my $el;
7357            !!!create-element ($el, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
7358            
7359            ## TODO: $self->{form_element} if defined
7360            $self->{content_model} = RCDATA_CONTENT_MODEL;
7361            delete $self->{escape}; # MUST
7362            
7363            $insert->($el);
7364            
7365            my $text = '';
7366            !!!nack ('t392.1');
7367            !!!next-token;
7368            if ($token->{type} == CHARACTER_TOKEN) {
7369              $token->{data} =~ s/^\x0A//;
7370              unless (length $token->{data}) {
7371                !!!cp ('t392');
7372              !!!next-token;              !!!next-token;
7373              redo B;            } else {
7374            } elsif ($token->{type} eq 'start tag') {              !!!cp ('t393');
7375              if ($token->{tag_name} eq 'noframes') {            }
7376                $in_body->($insert_to_current);          } else {
7377                redo B;            !!!cp ('t394');
7378              } else {          }
7379                #          while ($token->{type} == CHARACTER_TOKEN) {
7380              !!!cp ('t395');
7381              $text .= $token->{data};
7382              !!!next-token;
7383            }
7384            if (length $text) {
7385              !!!cp ('t396');
7386              $el->manakai_append_text ($text);
7387            }
7388            
7389            $self->{content_model} = PCDATA_CONTENT_MODEL;
7390            
7391            if ($token->{type} == END_TAG_TOKEN and
7392                $token->{tag_name} eq $tag_name) {
7393              !!!cp ('t397');
7394              ## Ignore the token
7395            } else {
7396              !!!cp ('t398');
7397              !!!parse-error (type => 'in RCDATA:#eof', token => $token);
7398            }
7399            !!!next-token;
7400            next B;
7401          } elsif ($token->{tag_name} eq 'rt' or
7402                   $token->{tag_name} eq 'rp') {
7403            ## has a |ruby| element in scope
7404            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7405              my $node = $self->{open_elements}->[$_];
7406              if ($node->[1] & RUBY_EL) {
7407                !!!cp ('t398.1');
7408                ## generate implied end tags
7409                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7410                  !!!cp ('t398.2');
7411                  pop @{$self->{open_elements}};
7412              }              }
7413            } elsif ($token->{type} eq 'end tag') {              unless ($self->{open_elements}->[-1]->[1] & RUBY_EL) {
7414              if ($token->{tag_name} eq 'html') {                !!!cp ('t398.3');
7415                $phase = 'trailing end';                !!!parse-error (type => 'not closed',
7416                !!!next-token;                                text => $self->{open_elements}->[-1]->[0]
7417                redo B;                                    ->manakai_local_name,
7418              } else {                                token => $token);
7419                #                pop @{$self->{open_elements}}
7420                      while not $self->{open_elements}->[-1]->[1] & RUBY_EL;
7421                }
7422                last INSCOPE;
7423              } elsif ($node->[1] & SCOPING_EL) {
7424                !!!cp ('t398.4');
7425                last INSCOPE;
7426              }
7427            } # INSCOPE
7428    
7429            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7430    
7431            !!!nack ('t398.5');
7432            !!!next-token;
7433            redo B;
7434          } elsif ($token->{tag_name} eq 'math' or
7435                   $token->{tag_name} eq 'svg') {
7436            $reconstruct_active_formatting_elements->($insert_to_current);
7437    
7438            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7439    
7440            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7441    
7442            ## "adjust foreign attributes" - done in insert-element-f
7443            
7444            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7445            
7446            if ($self->{self_closing}) {
7447              pop @{$self->{open_elements}};
7448              !!!ack ('t398.1');
7449            } else {
7450              !!!cp ('t398.2');
7451              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7452              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7453              ## mode, "in body" (not "in foreign content") secondary insertion
7454              ## mode, maybe.
7455            }
7456    
7457            !!!next-token;
7458            next B;
7459          } elsif ({
7460                    caption => 1, col => 1, colgroup => 1, frame => 1,
7461                    frameset => 1, head => 1, option => 1, optgroup => 1,
7462                    tbody => 1, td => 1, tfoot => 1, th => 1,
7463                    thead => 1, tr => 1,
7464                   }->{$token->{tag_name}}) {
7465            !!!cp ('t401');
7466            !!!parse-error (type => 'in body',
7467                            text => $token->{tag_name}, token => $token);
7468            ## Ignore the token
7469            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7470            !!!next-token;
7471            next B;
7472          } elsif ($token->{tag_name} eq 'param' or
7473                   $token->{tag_name} eq 'source') {
7474            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7475            pop @{$self->{open_elements}};
7476    
7477            !!!ack ('t398.5');
7478            !!!next-token;
7479            redo B;
7480          } else {
7481            if ($token->{tag_name} eq 'image') {
7482              !!!cp ('t384');
7483              !!!parse-error (type => 'image', token => $token);
7484              $token->{tag_name} = 'img';
7485            } else {
7486              !!!cp ('t385');
7487            }
7488    
7489            ## NOTE: There is an "as if <br>" code clone.
7490            $reconstruct_active_formatting_elements->($insert_to_current);
7491            
7492            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7493    
7494            if ({
7495                 applet => 1, marquee => 1, object => 1,
7496                }->{$token->{tag_name}}) {
7497              !!!cp ('t380');
7498              push @$active_formatting_elements, ['#marker', ''];
7499              !!!nack ('t380.1');
7500            } elsif ({
7501                      b => 1, big => 1, em => 1, font => 1, i => 1,
7502                      s => 1, small => 1, strike => 1,
7503                      strong => 1, tt => 1, u => 1,
7504                     }->{$token->{tag_name}}) {
7505              !!!cp ('t375');
7506              push @$active_formatting_elements, $self->{open_elements}->[-1];
7507              !!!nack ('t375.1');
7508            } elsif ($token->{tag_name} eq 'input') {
7509              !!!cp ('t388');
7510              ## TODO: associate with $self->{form_element} if defined
7511              pop @{$self->{open_elements}};
7512              !!!ack ('t388.2');
7513            } elsif ({
7514                      area => 1, basefont => 1, bgsound => 1, br => 1,
7515                      embed => 1, img => 1, spacer => 1, wbr => 1,
7516                     }->{$token->{tag_name}}) {
7517              !!!cp ('t388.1');
7518              pop @{$self->{open_elements}};
7519              !!!ack ('t388.3');
7520            } elsif ($token->{tag_name} eq 'select') {
7521              ## TODO: associate with $self->{form_element} if defined
7522            
7523              if ($self->{insertion_mode} & TABLE_IMS or
7524                  $self->{insertion_mode} & BODY_TABLE_IMS or
7525                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7526                !!!cp ('t400.1');
7527                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7528              } else {
7529                !!!cp ('t400.2');
7530                $self->{insertion_mode} = IN_SELECT_IM;
7531              }
7532              !!!nack ('t400.3');
7533            } else {
7534              !!!nack ('t402');
7535            }
7536            
7537            !!!next-token;
7538            next B;
7539          }
7540        } elsif ($token->{type} == END_TAG_TOKEN) {
7541          if ($token->{tag_name} eq 'body') {
7542            ## has a |body| element in scope
7543            my $i;
7544            INSCOPE: {
7545              for (reverse @{$self->{open_elements}}) {
7546                if ($_->[1] & BODY_EL) {
7547                  !!!cp ('t405');
7548                  $i = $_;
7549                  last INSCOPE;
7550                } elsif ($_->[1] & SCOPING_EL) {
7551                  !!!cp ('t405.1');
7552                  last;
7553              }              }
7554              }
7555    
7556              ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7557    
7558              !!!parse-error (type => 'unmatched end tag',
7559                              text => $token->{tag_name}, token => $token);
7560              ## NOTE: Ignore the token.
7561              !!!next-token;
7562              next B;
7563            } # INSCOPE
7564    
7565            for (@{$self->{open_elements}}) {
7566              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7567                !!!cp ('t403');
7568                !!!parse-error (type => 'not closed',
7569                                text => $_->[0]->manakai_local_name,
7570                                token => $token);
7571                last;
7572            } else {            } else {
7573              #              !!!cp ('t404');
7574            }            }
7575                      }
7576            if (defined $token->{tag_name}) {  
7577              !!!parse-error (type => 'after frameset:'.$token->{tag_name});          $self->{insertion_mode} = AFTER_BODY_IM;
7578            !!!next-token;
7579            next B;
7580          } elsif ($token->{tag_name} eq 'html') {
7581            ## TODO: Update this code.  It seems that the code below is not
7582            ## up-to-date, though it has same effect as speced.
7583            if (@{$self->{open_elements}} > 1 and
7584                $self->{open_elements}->[1]->[1] & BODY_EL) {
7585              unless ($self->{open_elements}->[-1]->[1] & BODY_EL) {
7586                !!!cp ('t406');
7587                !!!parse-error (type => 'not closed',
7588                                text => $self->{open_elements}->[1]->[0]
7589                                    ->manakai_local_name,
7590                                token => $token);
7591            } else {            } else {
7592              !!!parse-error (type => 'after frameset:#'.$token->{type});              !!!cp ('t407');
7593            }            }
7594              $self->{insertion_mode} = AFTER_BODY_IM;
7595              ## reprocess
7596              next B;
7597            } else {
7598              !!!cp ('t408');
7599              !!!parse-error (type => 'unmatched end tag',
7600                              text => $token->{tag_name}, token => $token);
7601            ## Ignore the token            ## Ignore the token
7602            !!!next-token;            !!!next-token;
7603            redo B;            next B;
7604            }
7605          } elsif ({
7606                    ## NOTE: End tags for non-phrasing flow content elements
7607    
7608            ## ISSUE: An issue in spec there                  ## NOTE: The normal ones
7609                    address => 1, article => 1, aside => 1, blockquote => 1,
7610                    center => 1, datagrid => 1, details => 1, dialog => 1,
7611                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7612                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7613                    ol => 1, pre => 1, section => 1, ul => 1,
7614    
7615                    ## NOTE: As normal, but ... optional tags
7616                    dd => 1, dt => 1, li => 1,
7617    
7618                    applet => 1, button => 1, marquee => 1, object => 1,
7619                   }->{$token->{tag_name}}) {
7620            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7621            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7622            ## </dd>" code.
7623    
7624            ## has an element in scope
7625            my $i;
7626            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7627              my $node = $self->{open_elements}->[$_];
7628              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7629                !!!cp ('t410');
7630                $i = $_;
7631                last INSCOPE;
7632              } elsif ($node->[1] & SCOPING_EL) {
7633                !!!cp ('t411');
7634                last INSCOPE;
7635              }
7636            } # INSCOPE
7637    
7638            unless (defined $i) { # has an element in scope
7639              !!!cp ('t413');
7640              !!!parse-error (type => 'unmatched end tag',
7641                              text => $token->{tag_name}, token => $token);
7642              ## NOTE: Ignore the token.
7643          } else {          } else {
7644            die "$0: $self->{insertion_mode}: Unknown insertion mode";            ## Step 1. generate implied end tags
7645              while ({
7646                      ## END_TAG_OPTIONAL_EL
7647                      dd => ($token->{tag_name} ne 'dd'),
7648                      dt => ($token->{tag_name} ne 'dt'),
7649                      li => ($token->{tag_name} ne 'li'),
7650                      option => 1,
7651                      optgroup => 1,
7652                      p => 1,
7653                      rt => 1,
7654                      rp => 1,
7655                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7656                !!!cp ('t409');
7657                pop @{$self->{open_elements}};
7658              }
7659    
7660              ## Step 2.
7661              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7662                      ne $token->{tag_name}) {
7663                !!!cp ('t412');
7664                !!!parse-error (type => 'not closed',
7665                                text => $self->{open_elements}->[-1]->[0]
7666                                    ->manakai_local_name,
7667                                token => $token);
7668              } else {
7669                !!!cp ('t414');
7670              }
7671    
7672              ## Step 3.
7673              splice @{$self->{open_elements}}, $i;
7674    
7675              ## Step 4.
7676              $clear_up_to_marker->()
7677                  if {
7678                    applet => 1, button => 1, marquee => 1, object => 1,
7679                  }->{$token->{tag_name}};
7680          }          }
       }  
     } elsif ($phase eq 'trailing end') {  
       ## states in the main stage is preserved yet # MUST  
         
       if ($token->{type} eq 'DOCTYPE') {  
         !!!parse-error (type => 'after html:#DOCTYPE');  
         ## Ignore the token  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'comment') {  
         my $comment = $self->{document}->create_comment ($token->{data});  
         $self->{document}->append_child ($comment);  
7681          !!!next-token;          !!!next-token;
7682          redo B;          next B;
7683        } elsif ($token->{type} eq 'character') {        } elsif ($token->{tag_name} eq 'form') {
7684          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          ## NOTE: As normal, but interacts with the form element pointer
7685            my $data = $1;  
7686            ## As if in the main phase.          undef $self->{form_element};
7687            ## NOTE: The insertion mode in the main phase  
7688            ## just before the phase has been changed to the trailing          ## has an element in scope
7689            ## end phase is either "after body" or "after frameset".          my $i;
7690            $reconstruct_active_formatting_elements->($insert_to_current)          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7691              if $phase eq 'main';            my $node = $self->{open_elements}->[$_];
7692              if ($node->[1] & FORM_EL) {
7693                !!!cp ('t418');
7694                $i = $_;
7695                last INSCOPE;
7696              } elsif ($node->[1] & SCOPING_EL) {
7697                !!!cp ('t419');
7698                last INSCOPE;
7699              }
7700            } # INSCOPE
7701    
7702            unless (defined $i) { # has an element in scope
7703              !!!cp ('t421');
7704              !!!parse-error (type => 'unmatched end tag',
7705                              text => $token->{tag_name}, token => $token);
7706              ## NOTE: Ignore the token.
7707            } else {
7708              ## Step 1. generate implied end tags
7709              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7710                !!!cp ('t417');
7711                pop @{$self->{open_elements}};
7712              }
7713                        
7714            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 2.
7715              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7716                      ne $token->{tag_name}) {
7717                !!!cp ('t417.1');
7718                !!!parse-error (type => 'not closed',
7719                                text => $self->{open_elements}->[-1]->[0]
7720                                    ->manakai_local_name,
7721                                token => $token);
7722              } else {
7723                !!!cp ('t420');
7724              }  
7725                        
7726            unless (length $token->{data}) {            ## Step 3.
7727              !!!next-token;            splice @{$self->{open_elements}}, $i;
7728              redo B;          }
7729    
7730            !!!next-token;
7731            next B;
7732          } elsif ({
7733                    ## NOTE: As normal, except acts as a closer for any ...
7734                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7735                   }->{$token->{tag_name}}) {
7736            ## has an element in scope
7737            my $i;
7738            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7739              my $node = $self->{open_elements}->[$_];
7740              if ($node->[1] & HEADING_EL) {
7741                !!!cp ('t423');
7742                $i = $_;
7743                last INSCOPE;
7744              } elsif ($node->[1] & SCOPING_EL) {
7745                !!!cp ('t424');
7746                last INSCOPE;
7747              }
7748            } # INSCOPE
7749    
7750            unless (defined $i) { # has an element in scope
7751              !!!cp ('t425.1');
7752              !!!parse-error (type => 'unmatched end tag',
7753                              text => $token->{tag_name}, token => $token);
7754              ## NOTE: Ignore the token.
7755            } else {
7756              ## Step 1. generate implied end tags
7757              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7758                !!!cp ('t422');
7759                pop @{$self->{open_elements}};
7760            }            }
7761              
7762              ## Step 2.
7763              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7764                      ne $token->{tag_name}) {
7765                !!!cp ('t425');
7766                !!!parse-error (type => 'unmatched end tag',
7767                                text => $token->{tag_name}, token => $token);
7768              } else {
7769                !!!cp ('t426');
7770              }
7771    
7772              ## Step 3.
7773              splice @{$self->{open_elements}}, $i;
7774          }          }
7775            
7776            !!!next-token;
7777            next B;
7778          } elsif ($token->{tag_name} eq 'p') {
7779            ## NOTE: As normal, except </p> implies <p> and ...
7780    
7781          !!!parse-error (type => 'after html:#character');          ## has an element in scope
7782          $phase = 'main';          my $non_optional;
7783          ## reprocess          my $i;
7784          redo B;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7785        } elsif ($token->{type} eq 'start tag' or            my $node = $self->{open_elements}->[$_];
7786                 $token->{type} eq 'end tag') {            if ($node->[1] & P_EL) {
7787          !!!parse-error (type => 'after html:'.$token->{tag_name});              !!!cp ('t410.1');
7788          $phase = 'main';              $i = $_;
7789          ## reprocess              last INSCOPE;
7790          redo B;            } elsif ($node->[1] & SCOPING_EL) {
7791        } elsif ($token->{type} eq 'end-of-file') {              !!!cp ('t411.1');
7792          ## Stop parsing              last INSCOPE;
7793          last B;            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7794                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7795                !!!cp ('t411.2');
7796                #
7797              } else {
7798                !!!cp ('t411.3');
7799                $non_optional ||= $node;
7800                #
7801              }
7802            } # INSCOPE
7803    
7804            if (defined $i) {
7805              ## 1. Generate implied end tags
7806              #
7807    
7808              ## 2. If current node != "p", parse error
7809              if ($non_optional) {
7810                !!!cp ('t412.1');
7811                !!!parse-error (type => 'not closed',
7812                                text => $non_optional->[0]->manakai_local_name,
7813                                token => $token);
7814              } else {
7815                !!!cp ('t414.1');
7816              }
7817    
7818              ## 3. Pop
7819              splice @{$self->{open_elements}}, $i;
7820            } else {
7821              !!!cp ('t413.1');
7822              !!!parse-error (type => 'unmatched end tag',
7823                              text => $token->{tag_name}, token => $token);
7824    
7825              !!!cp ('t415.1');
7826              ## As if <p>, then reprocess the current token
7827              my $el;
7828              !!!create-element ($el, $HTML_NS, 'p',, $token);
7829              $insert->($el);
7830              ## NOTE: Not inserted into |$self->{open_elements}|.
7831            }
7832    
7833            !!!next-token;
7834            next B;
7835          } elsif ({
7836                    a => 1,
7837                    b => 1, big => 1, em => 1, font => 1, i => 1,
7838                    nobr => 1, s => 1, small => 1, strike => 1,
7839                    strong => 1, tt => 1, u => 1,
7840                   }->{$token->{tag_name}}) {
7841            !!!cp ('t427');
7842            $formatting_end_tag->($token);
7843            next B;
7844          } elsif ($token->{tag_name} eq 'br') {
7845            !!!cp ('t428');
7846            !!!parse-error (type => 'unmatched end tag',
7847                            text => 'br', token => $token);
7848    
7849            ## As if <br>
7850            $reconstruct_active_formatting_elements->($insert_to_current);
7851            
7852            my $el;
7853            !!!create-element ($el, $HTML_NS, 'br',, $token);
7854            $insert->($el);
7855            
7856            ## Ignore the token.
7857            !!!next-token;
7858            next B;
7859        } else {        } else {
7860          die "$0: $token->{type}: Unknown token";          if ($token->{tag_name} eq 'sarcasm') {
7861              sleep 0.001; # take a deep breath
7862            }
7863    
7864            ## Step 1
7865            my $node_i = -1;
7866            my $node = $self->{open_elements}->[$node_i];
7867    
7868            ## Step 2
7869            S2: {
7870              my $node_tag_name = $node->[0]->manakai_local_name;
7871              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7872              if ($node_tag_name eq $token->{tag_name}) {
7873                ## Step 1
7874                ## generate implied end tags
7875                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7876                  !!!cp ('t430');
7877                  ## NOTE: |<ruby><rt></ruby>|.
7878                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7879                  ## which seems wrong.
7880                  pop @{$self->{open_elements}};
7881                  $node_i++;
7882                }
7883            
7884                ## Step 2
7885                my $current_tag_name
7886                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7887                $current_tag_name =~ tr/A-Z/a-z/;
7888                if ($current_tag_name ne $token->{tag_name}) {
7889                  !!!cp ('t431');
7890                  ## NOTE: <x><y></x>
7891                  !!!parse-error (type => 'not closed',
7892                                  text => $self->{open_elements}->[-1]->[0]
7893                                      ->manakai_local_name,
7894                                  token => $token);
7895                } else {
7896                  !!!cp ('t432');
7897                }
7898                
7899                ## Step 3
7900                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7901    
7902                !!!next-token;
7903                last S2;
7904              } else {
7905                ## Step 3
7906                if (not ($node->[1] & FORMATTING_EL) and
7907                    #not $phrasing_category->{$node->[1]} and
7908                    ($node->[1] & SPECIAL_EL or
7909                     $node->[1] & SCOPING_EL)) {
7910                  !!!cp ('t433');
7911                  !!!parse-error (type => 'unmatched end tag',
7912                                  text => $token->{tag_name}, token => $token);
7913                  ## Ignore the token
7914                  !!!next-token;
7915                  last S2;
7916    
7917                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7918                  ## 9.27, "a" is a child of <dd> (conforming).  In
7919                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7920                  ## "a" is a child of both <body> and <dd>.
7921                }
7922                
7923                !!!cp ('t434');
7924              }
7925              
7926              ## Step 4
7927              $node_i--;
7928              $node = $self->{open_elements}->[$node_i];
7929              
7930              ## Step 5;
7931              redo S2;
7932            } # S2
7933            next B;
7934        }        }
7935      }      }
7936        next B;
7937      } continue { # B
7938        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
7939          ## NOTE: The code below is executed in cases where it does not have
7940          ## to be, but it it is harmless even in those cases.
7941          ## has an element in scope
7942          INSCOPE: {
7943            for (reverse 0..$#{$self->{open_elements}}) {
7944              my $node = $self->{open_elements}->[$_];
7945              if ($node->[1] & FOREIGN_EL) {
7946                last INSCOPE;
7947              } elsif ($node->[1] & SCOPING_EL) {
7948                last;
7949              }
7950            }
7951            
7952            ## NOTE: No foreign element in scope.
7953            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
7954          } # INSCOPE
7955        }
7956    } # B    } # B
7957    
7958    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 5100  sub _tree_construction_main ($) { Line 7960  sub _tree_construction_main ($) {
7960    ## TODO: script stuffs    ## TODO: script stuffs
7961  } # _tree_construct_main  } # _tree_construct_main
7962    
7963  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
7964    my $class = shift;    my $class = shift;
7965    my $node = shift;    my $node = shift;
7966    my $s = \$_[0];    #my $s = \$_[0];
7967    my $onerror = $_[1];    my $onerror = $_[1];
7968      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
7969    
7970      ## ISSUE: Should {confident} be true?
7971    
7972    my $nt = $node->node_type;    my $nt = $node->node_type;
7973    if ($nt == 9) {    if ($nt == 9) {
# Line 5121  sub set_inner_html ($$$) { Line 7984  sub set_inner_html ($$$) {
7984      }      }
7985    
7986      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
7987      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
7988    } elsif ($nt == 1) {    } elsif ($nt == 1) {
7989      ## TODO: If non-html element      ## TODO: If non-html element
7990    
7991      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
7992    
7993    ## TODO: Support for $get_wrapper
7994    
7995      ## Step 1 # MUST      ## Step 1 # MUST
7996      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
7997      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5134  sub set_inner_html ($$$) { Line 7999  sub set_inner_html ($$$) {
7999      my $p = $class->new;      my $p = $class->new;
8000      $p->{document} = $doc;      $p->{document} = $doc;
8001    
8002      ## Step 9 # MUST      ## Step 8 # MUST
8003      my $i = 0;      my $i = 0;
8004      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8005      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8006      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8007        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8008        $input = $get_wrapper->($input);
8009        $p->{set_nc} = sub {
8010        my $self = shift;        my $self = shift;
8011    
8012        pop @{$self->{prev_input_character}};        my $char = '';
8013        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8014            $char = $self->{next_nc};
8015            delete $self->{next_nc};
8016            $self->{nc} = ord $char;
8017          } else {
8018            $self->{char_buffer} = '';
8019            $self->{char_buffer_pos} = 0;
8020            
8021            my $count = $input->manakai_read_until
8022                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8023                 $self->{char_buffer_pos});
8024            if ($count) {
8025              $self->{line_prev} = $self->{line};
8026              $self->{column_prev} = $self->{column};
8027              $self->{column}++;
8028              $self->{nc}
8029                  = ord substr ($self->{char_buffer},
8030                                $self->{char_buffer_pos}++, 1);
8031              return;
8032            }
8033            
8034            if ($input->read ($char, 1)) {
8035              $self->{nc} = ord $char;
8036            } else {
8037              $self->{nc} = -1;
8038              return;
8039            }
8040          }
8041    
8042        $self->{next_input_character} = -1 and return if $i >= length $$s;        ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8043        $self->{next_input_character} = ord substr $$s, $i++, 1;        $p->{column}++;
8044        $column++;  
8045          if ($self->{nc} == 0x000A) { # LF
8046        if ($self->{next_input_character} == 0x000A) { # LF          $p->{line}++;
8047          $line++;          $p->{column} = 0;
8048          $column = 0;          !!!cp ('i1');
8049        } elsif ($self->{next_input_character} == 0x000D) { # CR        } elsif ($self->{nc} == 0x000D) { # CR
8050          $i++ if substr ($$s, $i, 1) eq "\x0A";  ## TODO: support for abort/streaming
8051          $self->{next_input_character} = 0x000A; # LF # MUST          my $next = '';
8052          $line++;          if ($input->read ($next, 1) and $next ne "\x0A") {
8053          $column = 0;            $self->{next_nc} = $next;
8054        } elsif ($self->{next_input_character} > 0x10FFFF) {          }
8055          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0x000A; # LF # MUST
8056        } elsif ($self->{next_input_character} == 0x0000) { # NULL          $p->{line}++;
8057            $p->{column} = 0;
8058            !!!cp ('i2');
8059          } elsif ($self->{nc} == 0x0000) { # NULL
8060            !!!cp ('i4');
8061          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8062          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8063        }        }
8064      };      };
8065      $p->{prev_input_character} = [-1, -1, -1];  
8066      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8067              #my ($scalar, $specials_range, $offset) = @_;
8068          return 0 if defined $p->{next_nc};
8069    
8070          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8071          my $offset = $_[2] || 0;
8072          
8073          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8074            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8075            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8076              substr ($_[0], $offset)
8077                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8078              my $count = $+[0] - $-[0];
8079              if ($count) {
8080                $p->{column} += $count;
8081                $p->{char_buffer_pos} += $count;
8082                $p->{line_prev} = $p->{line};
8083                $p->{column_prev} = $p->{column} - 1;
8084                $p->{nc} = -1;
8085              }
8086              return $count;
8087            } else {
8088              return 0;
8089            }
8090          } else {
8091            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8092            if ($count) {
8093              $p->{column} += $count;
8094              $p->{column_prev} += $count;
8095              $p->{nc} = -1;
8096            }
8097            return $count;
8098          }
8099        }; # $p->{read_until}
8100    
8101      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8102        my (%opt) = @_;        my (%opt) = @_;
8103        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8104          my $column = $opt{column};
8105          if (defined $opt{token} and defined $opt{token}->{line}) {
8106            $line = $opt{token}->{line};
8107            $column = $opt{token}->{column};
8108          }
8109          warn "Parse error ($opt{type}) at line $line column $column\n";
8110      };      };
8111      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8112        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8113      };      };
8114            
8115        my $char_onerror = sub {
8116          my (undef, $type, %opt) = @_;
8117          $ponerror->(layer => 'encode',
8118                      line => $p->{line}, column => $p->{column} + 1,
8119                      %opt, type => $type);
8120        }; # $char_onerror
8121        $input->onerror ($char_onerror);
8122    
8123      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8124      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8125    
8126      ## Step 2      ## Step 2
8127      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8128      $p->{content_model_flag} = {      $p->{content_model} = {
8129        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
8130        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
8131        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
8132        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
8133        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
8134        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
8135        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
8136        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
8137        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
8138        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
8139      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
8140         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
8141            unless defined $p->{content_model};
8142            ## ISSUE: What is "the name of the element"? local name?
8143    
8144      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8145          ## TODO: Foreign element OK?
8146    
8147      ## Step 4      ## Step 3
8148      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8149        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8150    
8151      ## Step 5 # MUST      ## Step 4 # MUST
8152      $doc->append_child ($root);      $doc->append_child ($root);
8153    
8154      ## Step 6 # MUST      ## Step 5 # MUST
8155      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8156    
8157      undef $p->{head_element};      undef $p->{head_element};
8158    
8159      ## Step 7 # MUST      ## Step 6 # MUST
8160      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8161    
8162      ## Step 8 # MUST      ## Step 7 # MUST
8163      my $anode = $node;      my $anode = $node;
8164      AN: while (defined $anode) {      AN: while (defined $anode) {
8165        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8166          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8167          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8168            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8169                !!!cp ('i5');
8170              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8171              last AN;              last AN;
8172            }            }
# Line 5225  sub set_inner_html ($$$) { Line 8175  sub set_inner_html ($$$) {
8175        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8176      } # AN      } # AN
8177            
8178      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8179      {      {
8180        my $self = $p;        my $self = $p;
8181        !!!next-token;        !!!next-token;
8182      }      }
8183      $p->_tree_construction_main;      $p->_tree_construction_main;
8184    
8185      ## Step 11 # MUST      ## Step 10 # MUST
8186      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8187      for (@cn) {      for (@cn) {
8188        $node->remove_child ($_);        $node->remove_child ($_);
8189      }      }
8190      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8191    
8192      ## Step 12 # MUST      ## Step 11 # MUST
8193      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8194      for (@cn) {      for (@cn) {
8195        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5249  sub set_inner_html ($$$) { Line 8198  sub set_inner_html ($$$) {
8198      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8199    
8200      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8201    
8202        delete $p->{parse_error}; # delete loop
8203    } else {    } else {
8204      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";
8205    }    }
# Line 5256  sub set_inner_html ($$$) { Line 8207  sub set_inner_html ($$$) {
8207    
8208  } # tree construction stage  } # tree construction stage
8209    
8210  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8211    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  
8212    
8213  1;  1;
8214  # $Date$  # $Date$

Legend:
Removed from v.1.27  
changed lines
  Added in v.1.200

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24