/[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.35 by wakaba, Mon Jul 16 03:21:04 2007 UTC revision 1.207 by wakaba, Mon Oct 13 08:27:44 2008 UTC
# Line 1  Line 1 
1  package Whatpm::HTML;  package Whatpm::HTML;
2  use strict;  use strict;
3  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};  our $VERSION=do{my @r=(q$Revision$=~/\d+/g);sprintf "%d."."%02d" x $#r,@r};
4    use Error qw(:try);
5    
6    ## NOTE: This module don't check all HTML5 parse errors; character
7    ## encoding related parse errors are expected to be handled by relevant
8    ## modules.
9    ## Parse errors for control characters that are not allowed in HTML5
10    ## documents, for surrogate code points, and for noncharacter code
11    ## points, as well as U+FFFD substitions for characters whose code points
12    ## is higher than U+10FFFF may be detected by combining the parser with
13    ## the checker implemented by Whatpm::Charset::UnicodeChecker (for its
14    ## usage example, see |t/HTML-tree.t| in the Whatpm package or the
15    ## WebHACC::Language::HTML module in the WebHACC package).
16    
17  ## ISSUE:  ## ISSUE:
18  ## var doc = implementation.createDocument (null, null, null);  ## var doc = implementation.createDocument (null, null, null);
19  ## doc.write ('');  ## doc.write ('');
20  ## alert (doc.compatMode);  ## alert (doc.compatMode);
21    
22  ## ISSUE: HTML5 revision 967 says that the encoding layer MUST NOT  require IO::Handle;
23  ## strip BOM and the HTML layer MUST ignore it.  Whether we can do it  
24  ## is not yet clear.  my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
25  ## "{U+FEFF}..." in UTF-16BE/UTF-16LE is three or four characters?  my $MML_NS = q<http://www.w3.org/1998/Math/MathML>;
26  ## "{U+FEFF}..." in GB18030?  my $SVG_NS = q<http://www.w3.org/2000/svg>;
27    my $XLINK_NS = q<http://www.w3.org/1999/xlink>;
28  my $permitted_slash_tag_name = {  my $XML_NS = q<http://www.w3.org/XML/1998/namespace>;
29    base => 1,  my $XMLNS_NS = q<http://www.w3.org/2000/xmlns/>;
30    link => 1,  
31    meta => 1,  ## Bits 12-15
32    hr => 1,  sub SPECIAL_EL () { 0b1_000000000000000 }
33    br => 1,  sub SCOPING_EL () { 0b1_00000000000000 }
34    img=> 1,  sub FORMATTING_EL () { 0b1_0000000000000 }
35    embed => 1,  sub PHRASING_EL () { 0b1_000000000000 }
36    param => 1,  
37    area => 1,  ## Bits 10-11
38    col => 1,  sub FOREIGN_EL () { 0b1_00000000000 }
39    input => 1,  sub FOREIGN_FLOW_CONTENT_EL () { 0b1_0000000000 }
40    
41    ## Bits 6-9
42    sub TABLE_SCOPING_EL () { 0b1_000000000 }
43    sub TABLE_ROWS_SCOPING_EL () { 0b1_00000000 }
44    sub TABLE_ROW_SCOPING_EL () { 0b1_0000000 }
45    sub TABLE_ROWS_EL () { 0b1_000000 }
46    
47    ## Bit 5
48    sub ADDRESS_DIV_P_EL () { 0b1_00000 }
49    
50    ## NOTE: Used in </body> and EOF algorithms.
51    ## Bit 4
52    sub ALL_END_TAG_OPTIONAL_EL () { 0b1_0000 }
53    
54    ## NOTE: Used in "generate implied end tags" algorithm.
55    ## NOTE: There is a code where a modified version of
56    ## END_TAG_OPTIONAL_EL is used in "generate implied end tags"
57    ## implementation (search for the algorithm name).
58    ## Bit 3
59    sub END_TAG_OPTIONAL_EL () { 0b1_000 }
60    
61    ## Bits 0-2
62    
63    sub MISC_SPECIAL_EL () { SPECIAL_EL | 0b000 }
64    sub FORM_EL () { SPECIAL_EL | 0b001 }
65    sub FRAMESET_EL () { SPECIAL_EL | 0b010 }
66    sub HEADING_EL () { SPECIAL_EL | 0b011 }
67    sub SELECT_EL () { SPECIAL_EL | 0b100 }
68    sub SCRIPT_EL () { SPECIAL_EL | 0b101 }
69    
70    sub ADDRESS_DIV_EL () { SPECIAL_EL | ADDRESS_DIV_P_EL | 0b001 }
71    sub BODY_EL () { SPECIAL_EL | ALL_END_TAG_OPTIONAL_EL | 0b001 }
72    
73    sub DTDD_EL () {
74      SPECIAL_EL |
75      END_TAG_OPTIONAL_EL |
76      ALL_END_TAG_OPTIONAL_EL |
77      0b010
78    }
79    sub LI_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b100
84    }
85    sub P_EL () {
86      SPECIAL_EL |
87      ADDRESS_DIV_P_EL |
88      END_TAG_OPTIONAL_EL |
89      ALL_END_TAG_OPTIONAL_EL |
90      0b001
91    }
92    
93    sub TABLE_ROW_EL () {
94      SPECIAL_EL |
95      TABLE_ROWS_EL |
96      TABLE_ROW_SCOPING_EL |
97      ALL_END_TAG_OPTIONAL_EL |
98      0b001
99    }
100    sub TABLE_ROW_GROUP_EL () {
101      SPECIAL_EL |
102      TABLE_ROWS_EL |
103      TABLE_ROWS_SCOPING_EL |
104      ALL_END_TAG_OPTIONAL_EL |
105      0b001
106    }
107    
108    sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
109    sub BUTTON_EL () { SCOPING_EL | 0b001 }
110    sub CAPTION_EL () { SCOPING_EL | 0b010 }
111    sub HTML_EL () {
112      SCOPING_EL |
113      TABLE_SCOPING_EL |
114      TABLE_ROWS_SCOPING_EL |
115      TABLE_ROW_SCOPING_EL |
116      ALL_END_TAG_OPTIONAL_EL |
117      0b001
118    }
119    sub TABLE_EL () {
120      SCOPING_EL |
121      TABLE_ROWS_EL |
122      TABLE_SCOPING_EL |
123      0b001
124    }
125    sub TABLE_CELL_EL () {
126      SCOPING_EL |
127      TABLE_ROW_SCOPING_EL |
128      ALL_END_TAG_OPTIONAL_EL |
129      0b001
130    }
131    
132    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
133    sub A_EL () { FORMATTING_EL | 0b001 }
134    sub NOBR_EL () { FORMATTING_EL | 0b010 }
135    
136    sub RUBY_EL () { PHRASING_EL | 0b001 }
137    
138    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
139    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
140    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
141    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
142    
143    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
144    
145    my $el_category = {
146      a => A_EL,
147      address => ADDRESS_DIV_EL,
148      applet => MISC_SCOPING_EL,
149      area => MISC_SPECIAL_EL,
150      article => MISC_SPECIAL_EL,
151      aside => MISC_SPECIAL_EL,
152      b => FORMATTING_EL,
153      base => MISC_SPECIAL_EL,
154      basefont => MISC_SPECIAL_EL,
155      bgsound => MISC_SPECIAL_EL,
156      big => FORMATTING_EL,
157      blockquote => MISC_SPECIAL_EL,
158      body => BODY_EL,
159      br => MISC_SPECIAL_EL,
160      button => BUTTON_EL,
161      caption => CAPTION_EL,
162      center => MISC_SPECIAL_EL,
163      col => MISC_SPECIAL_EL,
164      colgroup => MISC_SPECIAL_EL,
165      command => MISC_SPECIAL_EL,
166      datagrid => MISC_SPECIAL_EL,
167      dd => DTDD_EL,
168      details => MISC_SPECIAL_EL,
169      dialog => MISC_SPECIAL_EL,
170      dir => MISC_SPECIAL_EL,
171      div => ADDRESS_DIV_EL,
172      dl => MISC_SPECIAL_EL,
173      dt => DTDD_EL,
174      em => FORMATTING_EL,
175      embed => MISC_SPECIAL_EL,
176      eventsource => MISC_SPECIAL_EL,
177      fieldset => MISC_SPECIAL_EL,
178      figure => MISC_SPECIAL_EL,
179      font => FORMATTING_EL,
180      footer => MISC_SPECIAL_EL,
181      form => FORM_EL,
182      frame => MISC_SPECIAL_EL,
183      frameset => FRAMESET_EL,
184      h1 => HEADING_EL,
185      h2 => HEADING_EL,
186      h3 => HEADING_EL,
187      h4 => HEADING_EL,
188      h5 => HEADING_EL,
189      h6 => HEADING_EL,
190      head => MISC_SPECIAL_EL,
191      header => MISC_SPECIAL_EL,
192      hr => MISC_SPECIAL_EL,
193      html => HTML_EL,
194      i => FORMATTING_EL,
195      iframe => MISC_SPECIAL_EL,
196      img => MISC_SPECIAL_EL,
197      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
198      input => MISC_SPECIAL_EL,
199      isindex => MISC_SPECIAL_EL,
200      li => LI_EL,
201      link => MISC_SPECIAL_EL,
202      listing => MISC_SPECIAL_EL,
203      marquee => MISC_SCOPING_EL,
204      menu => MISC_SPECIAL_EL,
205      meta => MISC_SPECIAL_EL,
206      nav => MISC_SPECIAL_EL,
207      nobr => NOBR_EL,
208      noembed => MISC_SPECIAL_EL,
209      noframes => MISC_SPECIAL_EL,
210      noscript => MISC_SPECIAL_EL,
211      object => MISC_SCOPING_EL,
212      ol => MISC_SPECIAL_EL,
213      optgroup => OPTGROUP_EL,
214      option => OPTION_EL,
215      p => P_EL,
216      param => MISC_SPECIAL_EL,
217      plaintext => MISC_SPECIAL_EL,
218      pre => MISC_SPECIAL_EL,
219      rp => RUBY_COMPONENT_EL,
220      rt => RUBY_COMPONENT_EL,
221      ruby => RUBY_EL,
222      s => FORMATTING_EL,
223      script => MISC_SPECIAL_EL,
224      select => SELECT_EL,
225      section => MISC_SPECIAL_EL,
226      small => FORMATTING_EL,
227      spacer => MISC_SPECIAL_EL,
228      strike => FORMATTING_EL,
229      strong => FORMATTING_EL,
230      style => MISC_SPECIAL_EL,
231      table => TABLE_EL,
232      tbody => TABLE_ROW_GROUP_EL,
233      td => TABLE_CELL_EL,
234      textarea => MISC_SPECIAL_EL,
235      tfoot => TABLE_ROW_GROUP_EL,
236      th => TABLE_CELL_EL,
237      thead => TABLE_ROW_GROUP_EL,
238      title => MISC_SPECIAL_EL,
239      tr => TABLE_ROW_EL,
240      tt => FORMATTING_EL,
241      u => FORMATTING_EL,
242      ul => MISC_SPECIAL_EL,
243      wbr => MISC_SPECIAL_EL,
244    };
245    
246    my $el_category_f = {
247      $MML_NS => {
248        'annotation-xml' => MML_AXML_EL,
249        mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
250        mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
251        mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
252        ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
253        mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
254      },
255      $SVG_NS => {
256        foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257        desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258        title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259      },
260      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
261    };
262    
263    my $svg_attr_name = {
264      attributename => 'attributeName',
265      attributetype => 'attributeType',
266      basefrequency => 'baseFrequency',
267      baseprofile => 'baseProfile',
268      calcmode => 'calcMode',
269      clippathunits => 'clipPathUnits',
270      contentscripttype => 'contentScriptType',
271      contentstyletype => 'contentStyleType',
272      diffuseconstant => 'diffuseConstant',
273      edgemode => 'edgeMode',
274      externalresourcesrequired => 'externalResourcesRequired',
275      filterres => 'filterRes',
276      filterunits => 'filterUnits',
277      glyphref => 'glyphRef',
278      gradienttransform => 'gradientTransform',
279      gradientunits => 'gradientUnits',
280      kernelmatrix => 'kernelMatrix',
281      kernelunitlength => 'kernelUnitLength',
282      keypoints => 'keyPoints',
283      keysplines => 'keySplines',
284      keytimes => 'keyTimes',
285      lengthadjust => 'lengthAdjust',
286      limitingconeangle => 'limitingConeAngle',
287      markerheight => 'markerHeight',
288      markerunits => 'markerUnits',
289      markerwidth => 'markerWidth',
290      maskcontentunits => 'maskContentUnits',
291      maskunits => 'maskUnits',
292      numoctaves => 'numOctaves',
293      pathlength => 'pathLength',
294      patterncontentunits => 'patternContentUnits',
295      patterntransform => 'patternTransform',
296      patternunits => 'patternUnits',
297      pointsatx => 'pointsAtX',
298      pointsaty => 'pointsAtY',
299      pointsatz => 'pointsAtZ',
300      preservealpha => 'preserveAlpha',
301      preserveaspectratio => 'preserveAspectRatio',
302      primitiveunits => 'primitiveUnits',
303      refx => 'refX',
304      refy => 'refY',
305      repeatcount => 'repeatCount',
306      repeatdur => 'repeatDur',
307      requiredextensions => 'requiredExtensions',
308      requiredfeatures => 'requiredFeatures',
309      specularconstant => 'specularConstant',
310      specularexponent => 'specularExponent',
311      spreadmethod => 'spreadMethod',
312      startoffset => 'startOffset',
313      stddeviation => 'stdDeviation',
314      stitchtiles => 'stitchTiles',
315      surfacescale => 'surfaceScale',
316      systemlanguage => 'systemLanguage',
317      tablevalues => 'tableValues',
318      targetx => 'targetX',
319      targety => 'targetY',
320      textlength => 'textLength',
321      viewbox => 'viewBox',
322      viewtarget => 'viewTarget',
323      xchannelselector => 'xChannelSelector',
324      ychannelselector => 'yChannelSelector',
325      zoomandpan => 'zoomAndPan',
326  };  };
327    
328  my $c1_entity_char = {  my $foreign_attr_xname = {
329      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
330      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
331      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
332      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
333      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
334      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
335      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
336      'xml:base' => [$XML_NS, ['xml', 'base']],
337      'xml:lang' => [$XML_NS, ['xml', 'lang']],
338      'xml:space' => [$XML_NS, ['xml', 'space']],
339      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
340      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
341    };
342    
343    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
344    
345    my $charref_map = {
346      0x0D => 0x000A,
347    0x80 => 0x20AC,    0x80 => 0x20AC,
348    0x81 => 0xFFFD,    0x81 => 0xFFFD,
349    0x82 => 0x201A,    0x82 => 0x201A,
# Line 60  my $c1_entity_char = { Line 376  my $c1_entity_char = {
376    0x9D => 0xFFFD,    0x9D => 0xFFFD,
377    0x9E => 0x017E,    0x9E => 0x017E,
378    0x9F => 0x0178,    0x9F => 0x0178,
379  }; # $c1_entity_char  }; # $charref_map
380    $charref_map->{$_} = 0xFFFD
381        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
382            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
383            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
384            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
385            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
386            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
387            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
388    
389  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
390    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
391    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
392    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  sub parse_byte_string ($$$$;$) {
393    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    my $self = shift;
394    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    my $charset_name = shift;
395    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
396    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
397    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  } # parse_byte_string
398    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
399    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  sub parse_byte_stream ($$$$;$$) {
400  };    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
401  my $scoping_category = {    my $self = ref $_[0] ? shift : shift->new;
402    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $charset_name = shift;
403    table => 1, td => 1, th => 1,    my $byte_stream = $_[0];
404  };  
405  my $formatting_category = {    my $onerror = $_[2] || sub {
406    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,      my (%opt) = @_;
407    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,      warn "Parse error ($opt{type})\n";
408  };    };
409  # $phrasing_category: all other elements    $self->{parse_error} = $onerror; # updated later by parse_char_string
410    
411      my $get_wrapper = $_[3] || sub ($) {
412        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
413      };
414    
415      ## HTML5 encoding sniffing algorithm
416      require Message::Charset::Info;
417      my $charset;
418      my $buffer;
419      my ($char_stream, $e_status);
420    
421      SNIFFING: {
422        ## NOTE: By setting |allow_fallback| option true when the
423        ## |get_decode_handle| method is invoked, we ignore what the HTML5
424        ## spec requires, i.e. unsupported encoding should be ignored.
425          ## TODO: We should not do this unless the parser is invoked
426          ## in the conformance checking mode, in which this behavior
427          ## would be useful.
428    
429        ## Step 1
430        if (defined $charset_name) {
431          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
432              ## TODO: Is this ok?  Transfer protocol's parameter should be
433              ## interpreted in its semantics?
434    
435          ($char_stream, $e_status) = $charset->get_decode_handle
436              ($byte_stream, allow_error_reporting => 1,
437               allow_fallback => 1);
438          if ($char_stream) {
439            $self->{confident} = 1;
440            last SNIFFING;
441          } else {
442            !!!parse-error (type => 'charset:not supported',
443                            layer => 'encode',
444                            line => 1, column => 1,
445                            value => $charset_name,
446                            level => $self->{level}->{uncertain});
447          }
448        }
449    
450        ## Step 2
451        my $byte_buffer = '';
452        for (1..1024) {
453          my $char = $byte_stream->getc;
454          last unless defined $char;
455          $byte_buffer .= $char;
456        } ## TODO: timeout
457    
458        ## Step 3
459        if ($byte_buffer =~ /^\xFE\xFF/) {
460          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
461          ($char_stream, $e_status) = $charset->get_decode_handle
462              ($byte_stream, allow_error_reporting => 1,
463               allow_fallback => 1, byte_buffer => \$byte_buffer);
464          $self->{confident} = 1;
465          last SNIFFING;
466        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
467          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
468          ($char_stream, $e_status) = $charset->get_decode_handle
469              ($byte_stream, allow_error_reporting => 1,
470               allow_fallback => 1, byte_buffer => \$byte_buffer);
471          $self->{confident} = 1;
472          last SNIFFING;
473        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
474          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
475          ($char_stream, $e_status) = $charset->get_decode_handle
476              ($byte_stream, allow_error_reporting => 1,
477               allow_fallback => 1, byte_buffer => \$byte_buffer);
478          $self->{confident} = 1;
479          last SNIFFING;
480        }
481    
482        ## Step 4
483        ## TODO: <meta charset>
484    
485        ## Step 5
486        ## TODO: from history
487    
488        ## Step 6
489        require Whatpm::Charset::UniversalCharDet;
490        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
491            ($byte_buffer);
492        if (defined $charset_name) {
493          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
494    
495          require Whatpm::Charset::DecodeHandle;
496          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
497              ($byte_stream);
498          ($char_stream, $e_status) = $charset->get_decode_handle
499              ($buffer, allow_error_reporting => 1,
500               allow_fallback => 1, byte_buffer => \$byte_buffer);
501          if ($char_stream) {
502            $buffer->{buffer} = $byte_buffer;
503            !!!parse-error (type => 'sniffing:chardet',
504                            text => $charset_name,
505                            level => $self->{level}->{info},
506                            layer => 'encode',
507                            line => 1, column => 1);
508            $self->{confident} = 0;
509            last SNIFFING;
510          }
511        }
512    
513        ## Step 7: default
514        ## TODO: Make this configurable.
515        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
516            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
517            ## detectable in the step 6.
518        require Whatpm::Charset::DecodeHandle;
519        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
520            ($byte_stream);
521        ($char_stream, $e_status)
522            = $charset->get_decode_handle ($buffer,
523                                           allow_error_reporting => 1,
524                                           allow_fallback => 1,
525                                           byte_buffer => \$byte_buffer);
526        $buffer->{buffer} = $byte_buffer;
527        !!!parse-error (type => 'sniffing:default',
528                        text => 'windows-1252',
529                        level => $self->{level}->{info},
530                        line => 1, column => 1,
531                        layer => 'encode');
532        $self->{confident} = 0;
533      } # SNIFFING
534    
535      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
536        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
537        !!!parse-error (type => 'chardecode:fallback',
538                        #text => $self->{input_encoding},
539                        level => $self->{level}->{uncertain},
540                        line => 1, column => 1,
541                        layer => 'encode');
542      } elsif (not ($e_status &
543                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
544        $self->{input_encoding} = $charset->get_iana_name;
545        !!!parse-error (type => 'chardecode:no error',
546                        text => $self->{input_encoding},
547                        level => $self->{level}->{uncertain},
548                        line => 1, column => 1,
549                        layer => 'encode');
550      } else {
551        $self->{input_encoding} = $charset->get_iana_name;
552      }
553    
554      $self->{change_encoding} = sub {
555        my $self = shift;
556        $charset_name = shift;
557        my $token = shift;
558    
559        $charset = Message::Charset::Info->get_by_html_name ($charset_name);
560        ($char_stream, $e_status) = $charset->get_decode_handle
561            ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
562             byte_buffer => \ $buffer->{buffer});
563        
564        if ($char_stream) { # if supported
565          ## "Change the encoding" algorithm:
566    
567          ## Step 1    
568          if ($charset->{category} &
569              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
570            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
571            ($char_stream, $e_status) = $charset->get_decode_handle
572                ($byte_stream,
573                 byte_buffer => \ $buffer->{buffer});
574          }
575          $charset_name = $charset->get_iana_name;
576          
577          ## Step 2
578          if (defined $self->{input_encoding} and
579              $self->{input_encoding} eq $charset_name) {
580            !!!parse-error (type => 'charset label:matching',
581                            text => $charset_name,
582                            level => $self->{level}->{info});
583            $self->{confident} = 1;
584            return;
585          }
586    
587          !!!parse-error (type => 'charset label detected',
588                          text => $self->{input_encoding},
589                          value => $charset_name,
590                          level => $self->{level}->{warn},
591                          token => $token);
592          
593          ## Step 3
594          # if (can) {
595            ## change the encoding on the fly.
596            #$self->{confident} = 1;
597            #return;
598          # }
599          
600          ## Step 4
601          throw Whatpm::HTML::RestartParser ();
602        }
603      }; # $self->{change_encoding}
604    
605      my $char_onerror = sub {
606        my (undef, $type, %opt) = @_;
607        !!!parse-error (layer => 'encode',
608                        line => $self->{line}, column => $self->{column} + 1,
609                        %opt, type => $type);
610        if ($opt{octets}) {
611          ${$opt{octets}} = "\x{FFFD}"; # relacement character
612        }
613      };
614    
615      my $wrapped_char_stream = $get_wrapper->($char_stream);
616      $wrapped_char_stream->onerror ($char_onerror);
617    
618      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
619      my $return;
620      try {
621        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
622      } catch Whatpm::HTML::RestartParser with {
623        ## NOTE: Invoked after {change_encoding}.
624    
625        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
626          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
627          !!!parse-error (type => 'chardecode:fallback',
628                          level => $self->{level}->{uncertain},
629                          #text => $self->{input_encoding},
630                          line => 1, column => 1,
631                          layer => 'encode');
632        } elsif (not ($e_status &
633                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
634          $self->{input_encoding} = $charset->get_iana_name;
635          !!!parse-error (type => 'chardecode:no error',
636                          text => $self->{input_encoding},
637                          level => $self->{level}->{uncertain},
638                          line => 1, column => 1,
639                          layer => 'encode');
640        } else {
641          $self->{input_encoding} = $charset->get_iana_name;
642        }
643        $self->{confident} = 1;
644    
645  sub parse_string ($$$;$) {      $wrapped_char_stream = $get_wrapper->($char_stream);
646    my $self = shift->new;      $wrapped_char_stream->onerror ($char_onerror);
647    my $s = \$_[0];  
648        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
649      };
650      return $return;
651    } # parse_byte_stream
652    
653    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
654    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
655    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
656    ## because the core part of our HTML parser expects a string of character,
657    ## not a string of bytes or code units or anything which might contain a BOM.
658    ## Therefore, any parser interface that accepts a string of bytes,
659    ## such as |parse_byte_string| in this module, must ensure that it does
660    ## strip the BOM and never strip any ZWNBSP.
661    
662    sub parse_char_string ($$$;$$) {
663      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
664      my $self = shift;
665      my $s = ref $_[0] ? $_[0] : \($_[0]);
666      require Whatpm::Charset::DecodeHandle;
667      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
668      return $self->parse_char_stream ($input, @_[1..$#_]);
669    } # parse_char_string
670    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
671    
672    sub parse_char_stream ($$$;$$) {
673      my $self = ref $_[0] ? shift : shift->new;
674      my $input = $_[0];
675    $self->{document} = $_[1];    $self->{document} = $_[1];
676      @{$self->{document}->child_nodes} = ();
677    
678    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
679    
680    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
681    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
682    my $column = 0;        if defined $self->{input_encoding};
683    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
684    
685      $self->{line_prev} = $self->{line} = 1;
686      $self->{column_prev} = -1;
687      $self->{column} = 0;
688      $self->{set_nc} = sub {
689      my $self = shift;      my $self = shift;
690    
691      pop @{$self->{prev_input_character}};      my $char = '';
692      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
693          $char = $self->{next_nc};
694          delete $self->{next_nc};
695          $self->{nc} = ord $char;
696        } else {
697          $self->{char_buffer} = '';
698          $self->{char_buffer_pos} = 0;
699    
700      $self->{next_input_character} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
701      $self->{next_input_character} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
702      $column++;        if ($count) {
703            $self->{line_prev} = $self->{line};
704            $self->{column_prev} = $self->{column};
705            $self->{column}++;
706            $self->{nc}
707                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
708            return;
709          }
710    
711          if ($input->read ($char, 1)) {
712            $self->{nc} = ord $char;
713          } else {
714            $self->{nc} = -1;
715            return;
716          }
717        }
718    
719        ($self->{line_prev}, $self->{column_prev})
720            = ($self->{line}, $self->{column});
721        $self->{column}++;
722            
723      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
724        $line++;        !!!cp ('j1');
725        $column = 0;        $self->{line}++;
726      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
727        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
728        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
729        $line++;  ## TODO: support for abort/streaming
730        $column = 0;        my $next = '';
731      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
732        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
733      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
734          $self->{nc} = 0x000A; # LF # MUST
735          $self->{line}++;
736          $self->{column} = 0;
737        } elsif ($self->{nc} == 0x0000) { # NULL
738          !!!cp ('j4');
739        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
740        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
741      }      }
742    };    };
743    $self->{prev_input_character} = [-1, -1, -1];  
744    $self->{next_input_character} = -1;    $self->{read_until} = sub {
745        #my ($scalar, $specials_range, $offset) = @_;
746        return 0 if defined $self->{next_nc};
747    
748        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
749        my $offset = $_[2] || 0;
750    
751        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
752          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
753          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
754            substr ($_[0], $offset)
755                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
756            my $count = $+[0] - $-[0];
757            if ($count) {
758              $self->{column} += $count;
759              $self->{char_buffer_pos} += $count;
760              $self->{line_prev} = $self->{line};
761              $self->{column_prev} = $self->{column} - 1;
762              $self->{nc} = -1;
763            }
764            return $count;
765          } else {
766            return 0;
767          }
768        } else {
769          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
770          if ($count) {
771            $self->{column} += $count;
772            $self->{line_prev} = $self->{line};
773            $self->{column_prev} = $self->{column} - 1;
774            $self->{nc} = -1;
775          }
776          return $count;
777        }
778      }; # $self->{read_until}
779    
780    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
781      my (%opt) = @_;      my (%opt) = @_;
782      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
783        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
784        warn "Parse error ($opt{type}) at line $line column $column\n";
785    };    };
786    $self->{parse_error} = sub {    $self->{parse_error} = sub {
787      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
788    };    };
789    
790      my $char_onerror = sub {
791        my (undef, $type, %opt) = @_;
792        !!!parse-error (layer => 'encode',
793                        line => $self->{line}, column => $self->{column} + 1,
794                        %opt, type => $type);
795      }; # $char_onerror
796    
797      if ($_[3]) {
798        $input = $_[3]->($input);
799        $input->onerror ($char_onerror);
800      } else {
801        $input->onerror ($char_onerror) unless defined $input->onerror;
802      }
803    
804    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
805    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
806    $self->_construct_tree;    $self->_construct_tree;
807    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
808    
809      delete $self->{parse_error}; # remove loop
810    
811    return $self->{document};    return $self->{document};
812  } # parse_string  } # parse_char_stream
813    
814  sub new ($) {  sub new ($) {
815    my $class = shift;    my $class = shift;
816    my $self = bless {}, $class;    my $self = bless {
817    $self->{set_next_input_character} = sub {      level => {must => 'm',
818      $self->{next_input_character} = -1;                should => 's',
819                  warn => 'w',
820                  info => 'i',
821                  uncertain => 'u'},
822      }, $class;
823      $self->{set_nc} = sub {
824        $self->{nc} = -1;
825    };    };
826    $self->{parse_error} = sub {    $self->{parse_error} = sub {
827      #      #
828    };    };
829      $self->{change_encoding} = sub {
830        # if ($_[0] is a supported encoding) {
831        #   run "change the encoding" algorithm;
832        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
833        # }
834      };
835      $self->{application_cache_selection} = sub {
836        #
837      };
838    return $self;    return $self;
839  } # new  } # new
840    
841    sub CM_ENTITY () { 0b001 } # & markup in data
842    sub CM_LIMITED_MARKUP () { 0b010 } # < markup in data (limited)
843    sub CM_FULL_MARKUP () { 0b100 } # < markup in data (any)
844    
845    sub PLAINTEXT_CONTENT_MODEL () { 0 }
846    sub CDATA_CONTENT_MODEL () { CM_LIMITED_MARKUP }
847    sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
848    sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
849    
850    sub DATA_STATE () { 0 }
851    #sub ENTITY_DATA_STATE () { 1 }
852    sub TAG_OPEN_STATE () { 2 }
853    sub CLOSE_TAG_OPEN_STATE () { 3 }
854    sub TAG_NAME_STATE () { 4 }
855    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
856    sub ATTRIBUTE_NAME_STATE () { 6 }
857    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
858    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
859    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
860    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
861    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
862    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
863    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
864    sub COMMENT_START_STATE () { 14 }
865    sub COMMENT_START_DASH_STATE () { 15 }
866    sub COMMENT_STATE () { 16 }
867    sub COMMENT_END_STATE () { 17 }
868    sub COMMENT_END_DASH_STATE () { 18 }
869    sub BOGUS_COMMENT_STATE () { 19 }
870    sub DOCTYPE_STATE () { 20 }
871    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
872    sub DOCTYPE_NAME_STATE () { 22 }
873    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
874    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
875    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
876    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
877    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
878    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
879    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
880    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
881    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
882    sub BOGUS_DOCTYPE_STATE () { 32 }
883    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
884    sub SELF_CLOSING_START_TAG_STATE () { 34 }
885    sub CDATA_SECTION_STATE () { 35 }
886    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
887    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
888    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
889    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
890    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
891    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
892    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
893    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
894    ## NOTE: "Entity data state", "entity in attribute value state", and
895    ## "consume a character reference" algorithm are jointly implemented
896    ## using the following six states:
897    sub ENTITY_STATE () { 44 }
898    sub ENTITY_HASH_STATE () { 45 }
899    sub NCR_NUM_STATE () { 46 }
900    sub HEXREF_X_STATE () { 47 }
901    sub HEXREF_HEX_STATE () { 48 }
902    sub ENTITY_NAME_STATE () { 49 }
903    sub PCDATA_STATE () { 50 } # "data state" in the spec
904    
905    sub DOCTYPE_TOKEN () { 1 }
906    sub COMMENT_TOKEN () { 2 }
907    sub START_TAG_TOKEN () { 3 }
908    sub END_TAG_TOKEN () { 4 }
909    sub END_OF_FILE_TOKEN () { 5 }
910    sub CHARACTER_TOKEN () { 6 }
911    
912    sub AFTER_HTML_IMS () { 0b100 }
913    sub HEAD_IMS ()       { 0b1000 }
914    sub BODY_IMS ()       { 0b10000 }
915    sub BODY_TABLE_IMS () { 0b100000 }
916    sub TABLE_IMS ()      { 0b1000000 }
917    sub ROW_IMS ()        { 0b10000000 }
918    sub BODY_AFTER_IMS () { 0b100000000 }
919    sub FRAME_IMS ()      { 0b1000000000 }
920    sub SELECT_IMS ()     { 0b10000000000 }
921    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
922        ## NOTE: "in foreign content" insertion mode is special; it is combined
923        ## with the secondary insertion mode.  In this parser, they are stored
924        ## together in the bit-or'ed form.
925    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
926        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
927        ## combined with the original insertion mode.  In thie parser,
928        ## they are stored together in the bit-or'ed form.
929    
930    ## NOTE: "initial" and "before html" insertion modes have no constants.
931    
932    ## NOTE: "after after body" insertion mode.
933    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
934    
935    ## NOTE: "after after frameset" insertion mode.
936    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
937    
938    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
939    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
940    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
941    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
942    sub IN_BODY_IM () { BODY_IMS }
943    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
944    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
945    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
946    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
947    sub IN_TABLE_IM () { TABLE_IMS }
948    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
949    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
950    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
951    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
952    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
953    sub IN_COLUMN_GROUP_IM () { 0b10 }
954    
955  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
956    
957  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
958    my $self = shift;    my $self = shift;
959    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
960    $self->{content_model_flag} = 'PCDATA'; # be    #$self->{s_kwd}; # state keyword - initialized when used
961    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    #$self->{entity__value}; # initialized when used
962    undef $self->{current_attribute};    #$self->{entity__match}; # initialized when used
963    undef $self->{last_emitted_start_tag_name};    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
964    undef $self->{last_attribute_value_state};    undef $self->{ct}; # current token
965    $self->{char} = [];    undef $self->{ca}; # current attribute
966    # $self->{next_input_character}    undef $self->{last_stag_name}; # last emitted start tag name
967      #$self->{prev_state}; # initialized when used
968      delete $self->{self_closing};
969      $self->{char_buffer} = '';
970      $self->{char_buffer_pos} = 0;
971      $self->{nc} = -1; # next input character
972      #$self->{next_nc}
973    !!!next-input-character;    !!!next-input-character;
974    $self->{token} = [];    $self->{token} = [];
975    # $self->{escape}    # $self->{escape}
976  } # _initialize_tokenizer  } # _initialize_tokenizer
977    
978  ## A token has:  ## A token has:
979  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
980  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
981  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
982  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
983  ##   ->{system_identifier} (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
984  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{sysid} (DOCTYPE_TOKEN)
985  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
986  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
987    ##        ->{name}
988    ##        ->{value}
989    ##        ->{has_reference} == 1 or 0
990    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
991    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
992    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
993    ##     while the token is pushed back to the stack.
994    
995  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
996    
# Line 185  sub _initialize_tokenizer ($) { Line 1000  sub _initialize_tokenizer ($) {
1000  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
1001  ## and removed from the list.  ## and removed from the list.
1002    
1003    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1004    ## (This requirement was dropped from HTML5 spec, unfortunately.)
1005    
1006    my $is_space = {
1007      0x0009 => 1, # CHARACTER TABULATION (HT)
1008      0x000A => 1, # LINE FEED (LF)
1009      #0x000B => 0, # LINE TABULATION (VT)
1010      0x000C => 1, # FORM FEED (FF)
1011      #0x000D => 1, # CARRIAGE RETURN (CR)
1012      0x0020 => 1, # SPACE (SP)
1013    };
1014    
1015  sub _get_next_token ($) {  sub _get_next_token ($) {
1016    my $self = shift;    my $self = shift;
1017    
1018      if ($self->{self_closing}) {
1019        !!!parse-error (type => 'nestc', token => $self->{ct});
1020        ## NOTE: The |self_closing| flag is only set by start tag token.
1021        ## In addition, when a start tag token is emitted, it is always set to
1022        ## |ct|.
1023        delete $self->{self_closing};
1024      }
1025    
1026    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1027        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1028      return shift @{$self->{token}};      return shift @{$self->{token}};
1029    }    }
1030    
1031    A: {    A: {
1032      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1033        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1034          if ($self->{content_model_flag} eq 'PCDATA' or  
1035              $self->{content_model_flag} eq 'RCDATA') {        if ($self->{nc} == 0x0026) { # &
1036            $self->{state} = 'entity data';          !!!cp (0.1);
1037            ## NOTE: In the spec, the tokenizer is switched to the
1038            ## "entity data state".  In this implementation, the tokenizer
1039            ## is switched to the |ENTITY_STATE|, which is an implementation
1040            ## of the "consume a character reference" algorithm.
1041            $self->{entity_add} = -1;
1042            $self->{prev_state} = DATA_STATE;
1043            $self->{state} = ENTITY_STATE;
1044            !!!next-input-character;
1045            redo A;
1046          } elsif ($self->{nc} == 0x003C) { # <
1047            !!!cp (0.2);
1048            $self->{state} = TAG_OPEN_STATE;
1049            !!!next-input-character;
1050            redo A;
1051          } elsif ($self->{nc} == -1) {
1052            !!!cp (0.3);
1053            !!!emit ({type => END_OF_FILE_TOKEN,
1054                      line => $self->{line}, column => $self->{column}});
1055            last A; ## TODO: ok?
1056          } else {
1057            !!!cp (0.4);
1058            #
1059          }
1060    
1061          # Anything else
1062          my $token = {type => CHARACTER_TOKEN,
1063                       data => chr $self->{nc},
1064                       line => $self->{line}, column => $self->{column},
1065                      };
1066          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1067    
1068          ## Stay in the state.
1069          !!!next-input-character;
1070          !!!emit ($token);
1071          redo A;
1072        } elsif ($self->{state} == DATA_STATE) {
1073          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1074          if ($self->{nc} == 0x0026) { # &
1075            $self->{s_kwd} = '';
1076            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1077                not $self->{escape}) {
1078              !!!cp (1);
1079              ## NOTE: In the spec, the tokenizer is switched to the
1080              ## "entity data state".  In this implementation, the tokenizer
1081              ## is switched to the |ENTITY_STATE|, which is an implementation
1082              ## of the "consume a character reference" algorithm.
1083              $self->{entity_add} = -1;
1084              $self->{prev_state} = DATA_STATE;
1085              $self->{state} = ENTITY_STATE;
1086            !!!next-input-character;            !!!next-input-character;
1087            redo A;            redo A;
1088          } else {          } else {
1089              !!!cp (2);
1090            #            #
1091          }          }
1092        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1093          if ($self->{content_model_flag} eq 'RCDATA' or          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1094              $self->{content_model_flag} eq 'CDATA') {            $self->{s_kwd} .= '-';
1095            unless ($self->{escape}) {            
1096              if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '<!--') {
1097                  $self->{prev_input_character}->[1] == 0x0021 and # !              !!!cp (3);
1098                  $self->{prev_input_character}->[2] == 0x003C) { # <              $self->{escape} = 1; # unless $self->{escape};
1099                $self->{escape} = 1;              $self->{s_kwd} = '--';
1100              }              #
1101              } elsif ($self->{s_kwd} eq '---') {
1102                !!!cp (4);
1103                $self->{s_kwd} = '--';
1104                #
1105              } else {
1106                !!!cp (5);
1107                #
1108            }            }
1109          }          }
1110                    
1111          #          #
1112        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1113          if ($self->{content_model_flag} eq 'PCDATA' or          if (length $self->{s_kwd}) {
1114              (($self->{content_model_flag} eq 'CDATA' or            !!!cp (5.1);
1115                $self->{content_model_flag} eq 'RCDATA') and            $self->{s_kwd} .= '!';
1116              #
1117            } else {
1118              !!!cp (5.2);
1119              #$self->{s_kwd} = '';
1120              #
1121            }
1122            #
1123          } elsif ($self->{nc} == 0x003C) { # <
1124            if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1125                (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1126               not $self->{escape})) {               not $self->{escape})) {
1127            $self->{state} = 'tag open';            !!!cp (6);
1128              $self->{state} = TAG_OPEN_STATE;
1129            !!!next-input-character;            !!!next-input-character;
1130            redo A;            redo A;
1131          } else {          } else {
1132              !!!cp (7);
1133              $self->{s_kwd} = '';
1134            #            #
1135          }          }
1136        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1137          if ($self->{escape} and          if ($self->{escape} and
1138              ($self->{content_model_flag} eq 'RCDATA' or              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1139               $self->{content_model_flag} eq 'CDATA')) {            if ($self->{s_kwd} eq '--') {
1140            if ($self->{prev_input_character}->[0] == 0x002D and # -              !!!cp (8);
               $self->{prev_input_character}->[1] == 0x002D) { # -  
1141              delete $self->{escape};              delete $self->{escape};
1142              } else {
1143                !!!cp (9);
1144            }            }
1145            } else {
1146              !!!cp (10);
1147          }          }
1148                    
1149            $self->{s_kwd} = '';
1150          #          #
1151        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1152          !!!emit ({type => 'end-of-file'});          !!!cp (11);
1153            $self->{s_kwd} = '';
1154            !!!emit ({type => END_OF_FILE_TOKEN,
1155                      line => $self->{line}, column => $self->{column}});
1156          last A; ## TODO: ok?          last A; ## TODO: ok?
1157          } else {
1158            !!!cp (12);
1159            $self->{s_kwd} = '';
1160            #
1161        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
   
       !!!emit ($token);  
1162    
1163        redo A;        # Anything else
1164      } elsif ($self->{state} eq 'entity data') {        my $token = {type => CHARACTER_TOKEN,
1165        ## (cannot happen in CDATA state)                     data => chr $self->{nc},
1166                             line => $self->{line}, column => $self->{column},
1167        my $token = $self->_tokenize_attempt_to_consume_an_entity (0);                    };
1168          if ($self->{read_until}->($token->{data}, q[-!<>&],
1169        $self->{state} = 'data';                                  length $token->{data})) {
1170        # next-input-character is already done          $self->{s_kwd} = '';
1171          }
1172    
1173        unless (defined $token) {        ## Stay in the data state.
1174          !!!emit ({type => 'character', data => '&'});        if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1175            !!!cp (13);
1176            $self->{state} = PCDATA_STATE;
1177        } else {        } else {
1178          !!!emit ($token);          !!!cp (14);
1179            ## Stay in the state.
1180        }        }
1181          !!!next-input-character;
1182          !!!emit ($token);
1183        redo A;        redo A;
1184      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1185        if ($self->{content_model_flag} eq 'RCDATA' or        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1186            $self->{content_model_flag} eq 'CDATA') {          if ($self->{nc} == 0x002F) { # /
1187          if ($self->{next_input_character} == 0x002F) { # /            !!!cp (15);
1188            !!!next-input-character;            !!!next-input-character;
1189            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1190            redo A;            redo A;
1191            } elsif ($self->{nc} == 0x0021) { # !
1192              !!!cp (15.1);
1193              $self->{s_kwd} = '<' unless $self->{escape};
1194              #
1195          } else {          } else {
1196            ## reconsume            !!!cp (16);
1197            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1198          }          }
1199        } elsif ($self->{content_model_flag} eq 'PCDATA') {  
1200          if ($self->{next_input_character} == 0x0021) { # !          ## reconsume
1201            $self->{state} = 'markup declaration open';          $self->{state} = DATA_STATE;
1202            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1203                      line => $self->{line_prev},
1204                      column => $self->{column_prev},
1205                     });
1206            redo A;
1207          } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1208            if ($self->{nc} == 0x0021) { # !
1209              !!!cp (17);
1210              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1211            !!!next-input-character;            !!!next-input-character;
1212            redo A;            redo A;
1213          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1214            $self->{state} = 'close tag open';            !!!cp (18);
1215              $self->{state} = CLOSE_TAG_OPEN_STATE;
1216            !!!next-input-character;            !!!next-input-character;
1217            redo A;            redo A;
1218          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1219                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1220            $self->{current_token}            !!!cp (19);
1221              = {type => 'start tag',            $self->{ct}
1222                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1223            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1224                   line => $self->{line_prev},
1225                   column => $self->{column_prev}};
1226              $self->{state} = TAG_NAME_STATE;
1227            !!!next-input-character;            !!!next-input-character;
1228            redo A;            redo A;
1229          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1230                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1231            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1232                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1233            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1234                                        line => $self->{line_prev},
1235                                        column => $self->{column_prev}};
1236              $self->{state} = TAG_NAME_STATE;
1237            !!!next-input-character;            !!!next-input-character;
1238            redo A;            redo A;
1239          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1240            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1241            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1242                              line => $self->{line_prev},
1243                              column => $self->{column_prev});
1244              $self->{state} = DATA_STATE;
1245            !!!next-input-character;            !!!next-input-character;
1246    
1247            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1248                        line => $self->{line_prev},
1249                        column => $self->{column_prev},
1250                       });
1251    
1252            redo A;            redo A;
1253          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1254            !!!parse-error (type => 'pio');            !!!cp (22);
1255            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1256            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1257                              column => $self->{column_prev});
1258              $self->{state} = BOGUS_COMMENT_STATE;
1259              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1260                                        line => $self->{line_prev},
1261                                        column => $self->{column_prev},
1262                                       };
1263              ## $self->{nc} is intentionally left as is
1264            redo A;            redo A;
1265          } else {          } else {
1266            !!!parse-error (type => 'bare stago');            !!!cp (23);
1267            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1268                              line => $self->{line_prev},
1269                              column => $self->{column_prev});
1270              $self->{state} = DATA_STATE;
1271            ## reconsume            ## reconsume
1272    
1273            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1274                        line => $self->{line_prev},
1275                        column => $self->{column_prev},
1276                       });
1277    
1278            redo A;            redo A;
1279          }          }
1280        } else {        } else {
1281          die "$0: $self->{content_model_flag}: Unknown content model flag";          die "$0: $self->{content_model} in tag open";
1282        }        }
1283      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1284        if ($self->{content_model_flag} eq 'RCDATA' or        ## NOTE: The "close tag open state" in the spec is implemented as
1285            $self->{content_model_flag} eq 'CDATA') {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
1286          if (defined $self->{last_emitted_start_tag_name}) {  
1287            ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1288            my @next_char;        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1289            TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {          if (defined $self->{last_stag_name}) {
1290              push @next_char, $self->{next_input_character};            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1291              my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);            $self->{s_kwd} = '';
1292              my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;            ## Reconsume.
1293              if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {            redo A;
               !!!next-input-character;  
               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...  
           }  
1294          } else {          } else {
1295            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1296            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1297            $self->{state} = 'data';            !!!cp (28);
1298            !!!emit ({type => 'character', data => '</'});            $self->{state} = DATA_STATE;
1299              ## Reconsume.
1300              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1301                        line => $l, column => $c,
1302                       });
1303            redo A;            redo A;
1304          }          }
1305        }        }
1306          
1307        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1308            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1309          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1310                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1311          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1312          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1313          redo A;                 line => $l, column => $c};
1314        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1315                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1316          $self->{current_token} = {type => 'end tag',          redo A;
1317                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1318          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1319          !!!next-input-character;          !!!cp (30);
1320          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1321        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1322          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1323          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1324            !!!next-input-character;
1325            redo A;
1326          } elsif ($self->{nc} == 0x003E) { # >
1327            !!!cp (31);
1328            !!!parse-error (type => 'empty end tag',
1329                            line => $self->{line_prev}, ## "<" in "</>"
1330                            column => $self->{column_prev} - 1);
1331            $self->{state} = DATA_STATE;
1332          !!!next-input-character;          !!!next-input-character;
1333          redo A;          redo A;
1334        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1335            !!!cp (32);
1336          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1337          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1338          # reconsume          # reconsume
1339    
1340          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1341                      line => $l, column => $c,
1342                     });
1343    
1344          redo A;          redo A;
1345        } else {        } else {
1346            !!!cp (33);
1347          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1348          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1349          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1350          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1351                                      column => $self->{column_prev} - 1,
1352                                     };
1353            ## NOTE: $self->{nc} is intentionally left as is.
1354            ## Although the "anything else" case of the spec not explicitly
1355            ## states that the next input character is to be reconsumed,
1356            ## it will be included to the |data| of the comment token
1357            ## generated from the bogus end tag, as defined in the
1358            ## "bogus comment state" entry.
1359            redo A;
1360          }
1361        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1362          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1363          if (length $ch) {
1364            my $CH = $ch;
1365            $ch =~ tr/a-z/A-Z/;
1366            my $nch = chr $self->{nc};
1367            if ($nch eq $ch or $nch eq $CH) {
1368              !!!cp (24);
1369              ## Stay in the state.
1370              $self->{s_kwd} .= $nch;
1371              !!!next-input-character;
1372              redo A;
1373            } else {
1374              !!!cp (25);
1375              $self->{state} = DATA_STATE;
1376              ## Reconsume.
1377              !!!emit ({type => CHARACTER_TOKEN,
1378                        data => '</' . $self->{s_kwd},
1379                        line => $self->{line_prev},
1380                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1381                       });
1382              redo A;
1383            }
1384          } else { # after "<{tag-name}"
1385            unless ($is_space->{$self->{nc}} or
1386                    {
1387                     0x003E => 1, # >
1388                     0x002F => 1, # /
1389                     -1 => 1, # EOF
1390                    }->{$self->{nc}}) {
1391              !!!cp (26);
1392              ## Reconsume.
1393              $self->{state} = DATA_STATE;
1394              !!!emit ({type => CHARACTER_TOKEN,
1395                        data => '</' . $self->{s_kwd},
1396                        line => $self->{line_prev},
1397                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1398                       });
1399              redo A;
1400            } else {
1401              !!!cp (27);
1402              $self->{ct}
1403                  = {type => END_TAG_TOKEN,
1404                     tag_name => $self->{last_stag_name},
1405                     line => $self->{line_prev},
1406                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1407              $self->{state} = TAG_NAME_STATE;
1408              ## Reconsume.
1409              redo A;
1410            }
1411        }        }
1412      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1413        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1414            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
1415            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1416            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1417            $self->{next_input_character} == 0x0020) { # SP          redo A;
1418          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
1419          !!!next-input-character;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1420          redo A;            !!!cp (35);
1421        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{last_stag_name} = $self->{ct}->{tag_name};
1422          if ($self->{current_token}->{type} eq 'start tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1423            $self->{current_token}->{first_start_tag}            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1424                = not defined $self->{last_emitted_start_tag_name};            #if ($self->{ct}->{attributes}) {
1425            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            #  ## NOTE: This should never be reached.
1426          } elsif ($self->{current_token}->{type} eq 'end tag') {            #  !!! cp (36);
1427            $self->{content_model_flag} = 'PCDATA'; # MUST            #  !!! parse-error (type => 'end tag attribute');
1428            if ($self->{current_token}->{attributes}) {            #} else {
1429              !!!parse-error (type => 'end tag attribute');              !!!cp (37);
1430            }            #}
1431          } else {          } else {
1432            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1433          }          }
1434          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1435          !!!next-input-character;          !!!next-input-character;
1436    
1437          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1438    
1439          redo A;          redo A;
1440        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1441                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1442          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1443            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1444            # start tag or end tag            # start tag or end tag
1445          ## Stay in this state          ## Stay in this state
1446          !!!next-input-character;          !!!next-input-character;
1447          redo A;          redo A;
1448        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1449          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1450          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1451            $self->{current_token}->{first_start_tag}            !!!cp (39);
1452                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1453            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1454          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1455            $self->{content_model_flag} = 'PCDATA'; # MUST            #if ($self->{ct}->{attributes}) {
1456            if ($self->{current_token}->{attributes}) {            #  ## NOTE: This state should never be reached.
1457              !!!parse-error (type => 'end tag attribute');            #  !!! cp (40);
1458            }            #  !!! parse-error (type => 'end tag attribute');
1459              #} else {
1460                !!!cp (41);
1461              #}
1462          } else {          } else {
1463            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1464          }          }
1465          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1466          # reconsume          # reconsume
1467    
1468          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1469    
1470          redo A;          redo A;
1471        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1472            !!!cp (42);
1473            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1474          !!!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  
1475          redo A;          redo A;
1476        } else {        } else {
1477          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1478            $self->{ct}->{tag_name} .= chr $self->{nc};
1479            # start tag or end tag            # start tag or end tag
1480          ## Stay in the state          ## Stay in the state
1481          !!!next-input-character;          !!!next-input-character;
1482          redo A;          redo A;
1483        }        }
1484      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1485        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1486            $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  
1487          ## Stay in the state          ## Stay in the state
1488          !!!next-input-character;          !!!next-input-character;
1489          redo A;          redo A;
1490        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1491          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1492            $self->{current_token}->{first_start_tag}            !!!cp (46);
1493                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1494            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1495          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1496            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1497            if ($self->{current_token}->{attributes}) {              !!!cp (47);
1498              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1499              } else {
1500                !!!cp (48);
1501            }            }
1502          } else {          } else {
1503            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1504          }          }
1505          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1506          !!!next-input-character;          !!!next-input-character;
1507    
1508          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1509    
1510          redo A;          redo A;
1511        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1512                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1513          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1514                                value => ''};          $self->{ca}
1515          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1516                   value => '',
1517                   line => $self->{line}, column => $self->{column}};
1518            $self->{state} = ATTRIBUTE_NAME_STATE;
1519          !!!next-input-character;          !!!next-input-character;
1520          redo A;          redo A;
1521        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1522            !!!cp (50);
1523            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1524          !!!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  
1525          redo A;          redo A;
1526        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1527          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1528          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1529            $self->{current_token}->{first_start_tag}            !!!cp (52);
1530                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1531            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1532          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1533            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1534            if ($self->{current_token}->{attributes}) {              !!!cp (53);
1535              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1536              } else {
1537                !!!cp (54);
1538            }            }
1539          } else {          } else {
1540            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1541          }          }
1542          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1543          # reconsume          # reconsume
1544    
1545          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1546    
1547          redo A;          redo A;
1548        } else {        } else {
1549          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1550                                value => ''};               0x0022 => 1, # "
1551          $self->{state} = 'attribute name';               0x0027 => 1, # '
1552                 0x003D => 1, # =
1553                }->{$self->{nc}}) {
1554              !!!cp (55);
1555              !!!parse-error (type => 'bad attribute name');
1556            } else {
1557              !!!cp (56);
1558            }
1559            $self->{ca}
1560                = {name => chr ($self->{nc}),
1561                   value => '',
1562                   line => $self->{line}, column => $self->{column}};
1563            $self->{state} = ATTRIBUTE_NAME_STATE;
1564          !!!next-input-character;          !!!next-input-character;
1565          redo A;          redo A;
1566        }        }
1567      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1568        my $before_leave = sub {        my $before_leave = sub {
1569          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1570              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1571            !!!parse-error (type => 'dupulicate attribute');            !!!cp (57);
1572            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1573              ## Discard $self->{ca} # MUST
1574          } else {          } else {
1575            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}            !!!cp (58);
1576              = $self->{current_attribute};            $self->{ct}->{attributes}->{$self->{ca}->{name}}
1577                = $self->{ca};
1578          }          }
1579        }; # $before_leave        }; # $before_leave
1580    
1581        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1582            $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  
1583          $before_leave->();          $before_leave->();
1584          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1585          !!!next-input-character;          !!!next-input-character;
1586          redo A;          redo A;
1587        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1588            !!!cp (60);
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1594          $before_leave->();          $before_leave->();
1595          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1596            $self->{current_token}->{first_start_tag}            !!!cp (61);
1597                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1598            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1599          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (62);
1600            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1601            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1602              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1603            }            }
1604          } else {          } else {
1605            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1606          }          }
1607          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1608          !!!next-input-character;          !!!next-input-character;
1609    
1610          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1611    
1612          redo A;          redo A;
1613        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1614                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1615          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1616            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1617          ## Stay in the state          ## Stay in the state
1618          !!!next-input-character;          !!!next-input-character;
1619          redo A;          redo A;
1620        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1621            !!!cp (64);
1622          $before_leave->();          $before_leave->();
1623            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1624          !!!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  
1625          redo A;          redo A;
1626        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1627          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1628          $before_leave->();          $before_leave->();
1629          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1630            $self->{current_token}->{first_start_tag}            !!!cp (66);
1631                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1632            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1633          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1634            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1635            if ($self->{current_token}->{attributes}) {              !!!cp (67);
1636              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1637              } else {
1638                ## NOTE: This state should never be reached.
1639                !!!cp (68);
1640            }            }
1641          } else {          } else {
1642            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1643          }          }
1644          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1645          # reconsume          # reconsume
1646    
1647          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1648    
1649          redo A;          redo A;
1650        } else {        } else {
1651          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1652                $self->{nc} == 0x0027) { # '
1653              !!!cp (69);
1654              !!!parse-error (type => 'bad attribute name');
1655            } else {
1656              !!!cp (70);
1657            }
1658            $self->{ca}->{name} .= chr ($self->{nc});
1659          ## Stay in the state          ## Stay in the state
1660          !!!next-input-character;          !!!next-input-character;
1661          redo A;          redo A;
1662        }        }
1663      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1664        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1665            $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  
1666          ## Stay in the state          ## Stay in the state
1667          !!!next-input-character;          !!!next-input-character;
1668          redo A;          redo A;
1669        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1670          $self->{state} = 'before attribute value';          !!!cp (72);
1671            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1672          !!!next-input-character;          !!!next-input-character;
1673          redo A;          redo A;
1674        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1675          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1676            $self->{current_token}->{first_start_tag}            !!!cp (73);
1677                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1678            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1679          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1680            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1681            if ($self->{current_token}->{attributes}) {              !!!cp (74);
1682              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1683              } else {
1684                ## NOTE: This state should never be reached.
1685                !!!cp (75);
1686            }            }
1687          } else {          } else {
1688            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1689          }          }
1690          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1691          !!!next-input-character;          !!!next-input-character;
1692    
1693          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1694    
1695          redo A;          redo A;
1696        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1697                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1698          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1699                                value => ''};          $self->{ca}
1700          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1701                   value => '',
1702                   line => $self->{line}, column => $self->{column}};
1703            $self->{state} = ATTRIBUTE_NAME_STATE;
1704          !!!next-input-character;          !!!next-input-character;
1705          redo A;          redo A;
1706        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1707            !!!cp (77);
1708            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1709          !!!next-input-character;          !!!next-input-character;
         if ($self->{next_input_character} == 0x003E and # >  
             $self->{current_token}->{type} eq 'start tag' and  
             $permitted_slash_tag_name->{$self->{current_token}->{tag_name}}) {  
           # permitted slash  
           #  
         } else {  
           !!!parse-error (type => 'nestc');  
           ## TODO: Different error type for <aa / bb> than <aa/>  
         }  
         $self->{state} = 'before attribute name';  
         # next-input-character is already done  
1710          redo A;          redo A;
1711        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1712          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1713          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1714            $self->{current_token}->{first_start_tag}            !!!cp (79);
1715                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1716            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1717          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1718            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1719            if ($self->{current_token}->{attributes}) {              !!!cp (80);
1720              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1721              } else {
1722                ## NOTE: This state should never be reached.
1723                !!!cp (81);
1724            }            }
1725          } else {          } else {
1726            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1727          }          }
1728          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1729          # reconsume          # reconsume
1730    
1731          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1732    
1733          redo A;          redo A;
1734        } else {        } else {
1735          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1736                                value => ''};              $self->{nc} == 0x0027) { # '
1737          $self->{state} = 'attribute name';            !!!cp (78);
1738              !!!parse-error (type => 'bad attribute name');
1739            } else {
1740              !!!cp (82);
1741            }
1742            $self->{ca}
1743                = {name => chr ($self->{nc}),
1744                   value => '',
1745                   line => $self->{line}, column => $self->{column}};
1746            $self->{state} = ATTRIBUTE_NAME_STATE;
1747          !!!next-input-character;          !!!next-input-character;
1748          redo A;                  redo A;        
1749        }        }
1750      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1751        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1752            $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        
1753          ## Stay in the state          ## Stay in the state
1754          !!!next-input-character;          !!!next-input-character;
1755          redo A;          redo A;
1756        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1757          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1758            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1759          !!!next-input-character;          !!!next-input-character;
1760          redo A;          redo A;
1761        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1762          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1763            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1764          ## reconsume          ## reconsume
1765          redo A;          redo A;
1766        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1767          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1768            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1769          !!!next-input-character;          !!!next-input-character;
1770          redo A;          redo A;
1771        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1772          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1773            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1774                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1775            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1776          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1777            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1778            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1779                !!!cp (88);
1780              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1781              } else {
1782                ## NOTE: This state should never be reached.
1783                !!!cp (89);
1784            }            }
1785          } else {          } else {
1786            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1787          }          }
1788          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1789          !!!next-input-character;          !!!next-input-character;
1790    
1791          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1792    
1793          redo A;          redo A;
1794        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1795          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1796          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1797            $self->{current_token}->{first_start_tag}            !!!cp (90);
1798                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1799            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1800          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1801            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1802            if ($self->{current_token}->{attributes}) {              !!!cp (91);
1803              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1804              } else {
1805                ## NOTE: This state should never be reached.
1806                !!!cp (92);
1807            }            }
1808          } else {          } else {
1809            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1810          }          }
1811          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1812          ## reconsume          ## reconsume
1813    
1814          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1815    
1816          redo A;          redo A;
1817        } else {        } else {
1818          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1819          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1820              !!!parse-error (type => 'bad attribute value');
1821            } else {
1822              !!!cp (94);
1823            }
1824            $self->{ca}->{value} .= chr ($self->{nc});
1825            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1826          !!!next-input-character;          !!!next-input-character;
1827          redo A;          redo A;
1828        }        }
1829      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1830        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1831          $self->{state} = 'before attribute name';          !!!cp (95);
1832            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1833          !!!next-input-character;          !!!next-input-character;
1834          redo A;          redo A;
1835        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1836          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1837          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1838            ## "entity in attribute value state".  In this implementation, the
1839            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1840            ## implementation of the "consume a character reference" algorithm.
1841            $self->{prev_state} = $self->{state};
1842            $self->{entity_add} = 0x0022; # "
1843            $self->{state} = ENTITY_STATE;
1844          !!!next-input-character;          !!!next-input-character;
1845          redo A;          redo A;
1846        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1847          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1848          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1849            $self->{current_token}->{first_start_tag}            !!!cp (97);
1850                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1851            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1852          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1853            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1854            if ($self->{current_token}->{attributes}) {              !!!cp (98);
1855              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1856              } else {
1857                ## NOTE: This state should never be reached.
1858                !!!cp (99);
1859            }            }
1860          } else {          } else {
1861            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1862          }          }
1863          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1864          ## reconsume          ## reconsume
1865    
1866          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1867    
1868          redo A;          redo A;
1869        } else {        } else {
1870          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1871            $self->{ca}->{value} .= chr ($self->{nc});
1872            $self->{read_until}->($self->{ca}->{value},
1873                                  q["&],
1874                                  length $self->{ca}->{value});
1875    
1876          ## Stay in the state          ## Stay in the state
1877          !!!next-input-character;          !!!next-input-character;
1878          redo A;          redo A;
1879        }        }
1880      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1881        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1882          $self->{state} = 'before attribute name';          !!!cp (101);
1883            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1884            !!!next-input-character;
1885            redo A;
1886          } elsif ($self->{nc} == 0x0026) { # &
1887            !!!cp (102);
1888            ## NOTE: In the spec, the tokenizer is switched to the
1889            ## "entity in attribute value state".  In this implementation, the
1890            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1891            ## implementation of the "consume a character reference" algorithm.
1892            $self->{entity_add} = 0x0027; # '
1893            $self->{prev_state} = $self->{state};
1894            $self->{state} = ENTITY_STATE;
1895          !!!next-input-character;          !!!next-input-character;
1896          redo A;          redo A;
1897        } 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) {  
1898          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1899          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1900            $self->{current_token}->{first_start_tag}            !!!cp (103);
1901                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1902            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1903          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1904            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1905            if ($self->{current_token}->{attributes}) {              !!!cp (104);
1906              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1907              } else {
1908                ## NOTE: This state should never be reached.
1909                !!!cp (105);
1910            }            }
1911          } else {          } else {
1912            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1913          }          }
1914          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1915          ## reconsume          ## reconsume
1916    
1917          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1918    
1919          redo A;          redo A;
1920        } else {        } else {
1921          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1922            $self->{ca}->{value} .= chr ($self->{nc});
1923            $self->{read_until}->($self->{ca}->{value},
1924                                  q['&],
1925                                  length $self->{ca}->{value});
1926    
1927          ## Stay in the state          ## Stay in the state
1928          !!!next-input-character;          !!!next-input-character;
1929          redo A;          redo A;
1930        }        }
1931      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1932        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1933            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1934            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1935            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1936            $self->{next_input_character} == 0x0020) { # SP          redo A;
1937          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1938          !!!next-input-character;          !!!cp (108);
1939          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1940        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1941          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1942          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1943          !!!next-input-character;          $self->{entity_add} = -1;
1944          redo A;          $self->{prev_state} = $self->{state};
1945        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1946          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1947            $self->{current_token}->{first_start_tag}          redo A;
1948                = not defined $self->{last_emitted_start_tag_name};        } elsif ($self->{nc} == 0x003E) { # >
1949            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1950          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (109);
1951            $self->{content_model_flag} = 'PCDATA'; # MUST            $self->{last_stag_name} = $self->{ct}->{tag_name};
1952            if ($self->{current_token}->{attributes}) {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1953              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1954              if ($self->{ct}->{attributes}) {
1955                !!!cp (110);
1956              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1957              } else {
1958                ## NOTE: This state should never be reached.
1959                !!!cp (111);
1960            }            }
1961          } else {          } else {
1962            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1963          }          }
1964          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1965          !!!next-input-character;          !!!next-input-character;
1966    
1967          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1968    
1969          redo A;          redo A;
1970        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1971          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1972          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1973            $self->{current_token}->{first_start_tag}            !!!cp (112);
1974                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1975            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1976          } elsif ($self->{current_token}->{type} eq 'end tag') {            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1977            $self->{content_model_flag} = 'PCDATA'; # MUST            if ($self->{ct}->{attributes}) {
1978            if ($self->{current_token}->{attributes}) {              !!!cp (113);
1979              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1980              } else {
1981                ## NOTE: This state should never be reached.
1982                !!!cp (114);
1983            }            }
1984          } else {          } else {
1985            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1986          }          }
1987          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1988          ## reconsume          ## reconsume
1989    
1990          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1991    
1992          redo A;          redo A;
1993        } else {        } else {
1994          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
1995                 0x0022 => 1, # "
1996                 0x0027 => 1, # '
1997                 0x003D => 1, # =
1998                }->{$self->{nc}}) {
1999              !!!cp (115);
2000              !!!parse-error (type => 'bad attribute value');
2001            } else {
2002              !!!cp (116);
2003            }
2004            $self->{ca}->{value} .= chr ($self->{nc});
2005            $self->{read_until}->($self->{ca}->{value},
2006                                  q["'=& >],
2007                                  length $self->{ca}->{value});
2008    
2009          ## Stay in the state          ## Stay in the state
2010          !!!next-input-character;          !!!next-input-character;
2011          redo A;          redo A;
2012        }        }
2013      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2014        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        if ($is_space->{$self->{nc}}) {
2015            !!!cp (118);
2016            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2017            !!!next-input-character;
2018            redo A;
2019          } elsif ($self->{nc} == 0x003E) { # >
2020            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2021              !!!cp (119);
2022              $self->{last_stag_name} = $self->{ct}->{tag_name};
2023            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2024              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2025              if ($self->{ct}->{attributes}) {
2026                !!!cp (120);
2027                !!!parse-error (type => 'end tag attribute');
2028              } else {
2029                ## NOTE: This state should never be reached.
2030                !!!cp (121);
2031              }
2032            } else {
2033              die "$0: $self->{ct}->{type}: Unknown token type";
2034            }
2035            $self->{state} = DATA_STATE;
2036            !!!next-input-character;
2037    
2038            !!!emit ($self->{ct}); # start tag or end tag
2039    
2040        unless (defined $token) {          redo A;
2041          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2042            !!!cp (122);
2043            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2044            !!!next-input-character;
2045            redo A;
2046          } elsif ($self->{nc} == -1) {
2047            !!!parse-error (type => 'unclosed tag');
2048            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2049              !!!cp (122.3);
2050              $self->{last_stag_name} = $self->{ct}->{tag_name};
2051            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2052              if ($self->{ct}->{attributes}) {
2053                !!!cp (122.1);
2054                !!!parse-error (type => 'end tag attribute');
2055              } else {
2056                ## NOTE: This state should never be reached.
2057                !!!cp (122.2);
2058              }
2059            } else {
2060              die "$0: $self->{ct}->{type}: Unknown token type";
2061            }
2062            $self->{state} = DATA_STATE;
2063            ## Reconsume.
2064            !!!emit ($self->{ct}); # start tag or end tag
2065            redo A;
2066        } else {        } else {
2067          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2068          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2069            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2070            ## reconsume
2071            redo A;
2072        }        }
2073        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2074          if ($self->{nc} == 0x003E) { # >
2075            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2076              !!!cp ('124.2');
2077              !!!parse-error (type => 'nestc', token => $self->{ct});
2078              ## TODO: Different type than slash in start tag
2079              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2080              if ($self->{ct}->{attributes}) {
2081                !!!cp ('124.4');
2082                !!!parse-error (type => 'end tag attribute');
2083              } else {
2084                !!!cp ('124.5');
2085              }
2086              ## TODO: Test |<title></title/>|
2087            } else {
2088              !!!cp ('124.3');
2089              $self->{self_closing} = 1;
2090            }
2091    
2092        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2093        # 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  
2094    
2095            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2096    
2097            redo A;          redo A;
2098          } elsif ($self->{nc} == -1) {
2099            !!!parse-error (type => 'unclosed tag');
2100            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2101              !!!cp (124.7);
2102              $self->{last_stag_name} = $self->{ct}->{tag_name};
2103            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2104              if ($self->{ct}->{attributes}) {
2105                !!!cp (124.5);
2106                !!!parse-error (type => 'end tag attribute');
2107              } else {
2108                ## NOTE: This state should never be reached.
2109                !!!cp (124.6);
2110              }
2111          } else {          } else {
2112            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2113          }          }
2114        } # BC          $self->{state} = DATA_STATE;
2115      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2116            !!!emit ($self->{ct}); # start tag or end tag
2117            redo A;
2118          } else {
2119            !!!cp ('124.4');
2120            !!!parse-error (type => 'nestc');
2121            ## TODO: This error type is wrong.
2122            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2123            ## Reconsume.
2124            redo A;
2125          }
2126        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2127        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2128    
2129        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2130        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2131                
2132        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2133            !!!cp (124);
2134            $self->{state} = DATA_STATE;
2135          !!!next-input-character;          !!!next-input-character;
2136          push @next_char, $self->{next_input_character};  
2137          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2138            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2139            $self->{state} = 'comment start';        } elsif ($self->{nc} == -1) {
2140            !!!next-input-character;          !!!cp (125);
2141            redo A;          $self->{state} = DATA_STATE;
2142          }          ## reconsume
2143        } elsif ($self->{next_input_character} == 0x0044 or # D  
2144                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2145            redo A;
2146          } else {
2147            !!!cp (126);
2148            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2149            $self->{read_until}->($self->{ct}->{data},
2150                                  q[>],
2151                                  length $self->{ct}->{data});
2152    
2153            ## Stay in the state.
2154          !!!next-input-character;          !!!next-input-character;
2155          push @next_char, $self->{next_input_character};          redo A;
2156          if ($self->{next_input_character} == 0x004F or # O        }
2157              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2158            !!!next-input-character;        ## (only happen if PCDATA state)
2159            push @next_char, $self->{next_input_character};        
2160            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2161                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2162              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2163              push @next_char, $self->{next_input_character};          !!!next-input-character;
2164              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2165                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2166                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2167                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2168                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2169                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2170                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2171                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2172                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2173                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2174                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2175                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2176                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2177                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2178                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2179                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2180                      !!!next-input-character;          redo A;
2181                      redo A;        } else {
2182                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2183        }        }
2184    
2185        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2186        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2187        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2188        $self->{state} = 'bogus comment';        ## Reconsume.
2189          $self->{state} = BOGUS_COMMENT_STATE;
2190          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2191                                    line => $self->{line_prev},
2192                                    column => $self->{column_prev} - 1,
2193                                   };
2194        redo A;        redo A;
2195              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2196        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2197        ## 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);
2198      } elsif ($self->{state} eq 'comment start') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2199        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2200          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2201                                     };
2202            $self->{state} = COMMENT_START_STATE;
2203          !!!next-input-character;          !!!next-input-character;
2204          redo A;          redo A;
2205        } elsif ($self->{next_input_character} == 0x003E) { # >        } else {
2206            !!!cp (128);
2207            !!!parse-error (type => 'bogus comment',
2208                            line => $self->{line_prev},
2209                            column => $self->{column_prev} - 2);
2210            $self->{state} = BOGUS_COMMENT_STATE;
2211            ## Reconsume.
2212            $self->{ct} = {type => COMMENT_TOKEN,
2213                                      data => '-',
2214                                      line => $self->{line_prev},
2215                                      column => $self->{column_prev} - 2,
2216                                     };
2217            redo A;
2218          }
2219        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2220          ## ASCII case-insensitive.
2221          if ($self->{nc} == [
2222                undef,
2223                0x004F, # O
2224                0x0043, # C
2225                0x0054, # T
2226                0x0059, # Y
2227                0x0050, # P
2228              ]->[length $self->{s_kwd}] or
2229              $self->{nc} == [
2230                undef,
2231                0x006F, # o
2232                0x0063, # c
2233                0x0074, # t
2234                0x0079, # y
2235                0x0070, # p
2236              ]->[length $self->{s_kwd}]) {
2237            !!!cp (131);
2238            ## Stay in the state.
2239            $self->{s_kwd} .= chr $self->{nc};
2240            !!!next-input-character;
2241            redo A;
2242          } elsif ((length $self->{s_kwd}) == 6 and
2243                   ($self->{nc} == 0x0045 or # E
2244                    $self->{nc} == 0x0065)) { # e
2245            !!!cp (129);
2246            $self->{state} = DOCTYPE_STATE;
2247            $self->{ct} = {type => DOCTYPE_TOKEN,
2248                                      quirks => 1,
2249                                      line => $self->{line_prev},
2250                                      column => $self->{column_prev} - 7,
2251                                     };
2252            !!!next-input-character;
2253            redo A;
2254          } else {
2255            !!!cp (132);        
2256            !!!parse-error (type => 'bogus comment',
2257                            line => $self->{line_prev},
2258                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2259            $self->{state} = BOGUS_COMMENT_STATE;
2260            ## Reconsume.
2261            $self->{ct} = {type => COMMENT_TOKEN,
2262                                      data => $self->{s_kwd},
2263                                      line => $self->{line_prev},
2264                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2265                                     };
2266            redo A;
2267          }
2268        } elsif ($self->{state} == MD_CDATA_STATE) {
2269          if ($self->{nc} == {
2270                '[' => 0x0043, # C
2271                '[C' => 0x0044, # D
2272                '[CD' => 0x0041, # A
2273                '[CDA' => 0x0054, # T
2274                '[CDAT' => 0x0041, # A
2275              }->{$self->{s_kwd}}) {
2276            !!!cp (135.1);
2277            ## Stay in the state.
2278            $self->{s_kwd} .= chr $self->{nc};
2279            !!!next-input-character;
2280            redo A;
2281          } elsif ($self->{s_kwd} eq '[CDATA' and
2282                   $self->{nc} == 0x005B) { # [
2283            !!!cp (135.2);
2284            $self->{ct} = {type => CHARACTER_TOKEN,
2285                                      data => '',
2286                                      line => $self->{line_prev},
2287                                      column => $self->{column_prev} - 7};
2288            $self->{state} = CDATA_SECTION_STATE;
2289            !!!next-input-character;
2290            redo A;
2291          } else {
2292            !!!cp (135.3);
2293            !!!parse-error (type => 'bogus comment',
2294                            line => $self->{line_prev},
2295                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2296            $self->{state} = BOGUS_COMMENT_STATE;
2297            ## Reconsume.
2298            $self->{ct} = {type => COMMENT_TOKEN,
2299                                      data => $self->{s_kwd},
2300                                      line => $self->{line_prev},
2301                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2302                                     };
2303            redo A;
2304          }
2305        } elsif ($self->{state} == COMMENT_START_STATE) {
2306          if ($self->{nc} == 0x002D) { # -
2307            !!!cp (137);
2308            $self->{state} = COMMENT_START_DASH_STATE;
2309            !!!next-input-character;
2310            redo A;
2311          } elsif ($self->{nc} == 0x003E) { # >
2312            !!!cp (138);
2313          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2314          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316    
2317          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2318    
2319          redo A;          redo A;
2320        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2321            !!!cp (139);
2322          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2323          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2324          ## reconsume          ## reconsume
2325    
2326          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2327    
2328          redo A;          redo A;
2329        } else {        } else {
2330          $self->{current_token}->{data} # comment          !!!cp (140);
2331              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2332          $self->{state} = 'comment';              .= chr ($self->{nc});
2333            $self->{state} = COMMENT_STATE;
2334          !!!next-input-character;          !!!next-input-character;
2335          redo A;          redo A;
2336        }        }
2337      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2338        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2339          $self->{state} = 'comment end';          !!!cp (141);
2340            $self->{state} = COMMENT_END_STATE;
2341          !!!next-input-character;          !!!next-input-character;
2342          redo A;          redo A;
2343        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2344            !!!cp (142);
2345          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2346          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2347          !!!next-input-character;          !!!next-input-character;
2348    
2349          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2350    
2351          redo A;          redo A;
2352        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2353            !!!cp (143);
2354          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2355          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2356          ## reconsume          ## reconsume
2357    
2358          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2359    
2360          redo A;          redo A;
2361        } else {        } else {
2362          $self->{current_token}->{data} # comment          !!!cp (144);
2363              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2364          $self->{state} = 'comment';              .= '-' . chr ($self->{nc});
2365            $self->{state} = COMMENT_STATE;
2366          !!!next-input-character;          !!!next-input-character;
2367          redo A;          redo A;
2368        }        }
2369      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2370        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2371          $self->{state} = 'comment end dash';          !!!cp (145);
2372            $self->{state} = COMMENT_END_DASH_STATE;
2373          !!!next-input-character;          !!!next-input-character;
2374          redo A;          redo A;
2375        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2376            !!!cp (146);
2377          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2378          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2379          ## reconsume          ## reconsume
2380    
2381          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2382    
2383          redo A;          redo A;
2384        } else {        } else {
2385          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2386            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2387            $self->{read_until}->($self->{ct}->{data},
2388                                  q[-],
2389                                  length $self->{ct}->{data});
2390    
2391          ## Stay in the state          ## Stay in the state
2392          !!!next-input-character;          !!!next-input-character;
2393          redo A;          redo A;
2394        }        }
2395      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2396        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2397          $self->{state} = 'comment end';          !!!cp (148);
2398            $self->{state} = COMMENT_END_STATE;
2399          !!!next-input-character;          !!!next-input-character;
2400          redo A;          redo A;
2401        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2402            !!!cp (149);
2403          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2404          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2405          ## reconsume          ## reconsume
2406    
2407          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2408    
2409          redo A;          redo A;
2410        } else {        } else {
2411          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2412          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2413            $self->{state} = COMMENT_STATE;
2414          !!!next-input-character;          !!!next-input-character;
2415          redo A;          redo A;
2416        }        }
2417      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2418        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2419          $self->{state} = 'data';          !!!cp (151);
2420            $self->{state} = DATA_STATE;
2421          !!!next-input-character;          !!!next-input-character;
2422    
2423          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2424    
2425          redo A;          redo A;
2426        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2427          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2428          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2429                            line => $self->{line_prev},
2430                            column => $self->{column_prev});
2431            $self->{ct}->{data} .= '-'; # comment
2432          ## Stay in the state          ## Stay in the state
2433          !!!next-input-character;          !!!next-input-character;
2434          redo A;          redo A;
2435        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2436            !!!cp (153);
2437          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2438          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2439          ## reconsume          ## reconsume
2440    
2441          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2442    
2443          redo A;          redo A;
2444        } else {        } else {
2445          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2446          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2447          $self->{state} = 'comment';                          line => $self->{line_prev},
2448                            column => $self->{column_prev});
2449            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2450            $self->{state} = COMMENT_STATE;
2451          !!!next-input-character;          !!!next-input-character;
2452          redo A;          redo A;
2453        }        }
2454      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2455        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2456            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2457            $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';  
2458          !!!next-input-character;          !!!next-input-character;
2459          redo A;          redo A;
2460        } else {        } else {
2461            !!!cp (156);
2462          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2463          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2464          ## reconsume          ## reconsume
2465          redo A;          redo A;
2466        }        }
2467      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2468        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2469            $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  
2470          ## Stay in the state          ## Stay in the state
2471          !!!next-input-character;          !!!next-input-character;
2472          redo A;          redo A;
2473        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2474            !!!cp (158);
2475          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2476          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2477          !!!next-input-character;          !!!next-input-character;
2478    
2479          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2480    
2481          redo A;          redo A;
2482        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2483            !!!cp (159);
2484          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2485          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2486          ## reconsume          ## reconsume
2487    
2488          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2489    
2490          redo A;          redo A;
2491        } else {        } else {
2492          $self->{current_token}          !!!cp (160);
2493              = {type => 'DOCTYPE',          $self->{ct}->{name} = chr $self->{nc};
2494                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
2495                 correct => 1};          $self->{state} = DOCTYPE_NAME_STATE;
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = 'DOCTYPE name';  
2496          !!!next-input-character;          !!!next-input-character;
2497          redo A;          redo A;
2498        }        }
2499      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2500  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2501        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2502            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
2503            $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';  
2504          !!!next-input-character;          !!!next-input-character;
2505          redo A;          redo A;
2506        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2507          $self->{state} = 'data';          !!!cp (162);
2508            $self->{state} = DATA_STATE;
2509          !!!next-input-character;          !!!next-input-character;
2510    
2511          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2512    
2513          redo A;          redo A;
2514        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2515            !!!cp (163);
2516          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2517          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2518          ## reconsume          ## reconsume
2519    
2520          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2521          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2522    
2523          redo A;          redo A;
2524        } else {        } else {
2525          $self->{current_token}->{name}          !!!cp (164);
2526            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2527              .= chr ($self->{nc}); # DOCTYPE
2528          ## Stay in the state          ## Stay in the state
2529          !!!next-input-character;          !!!next-input-character;
2530          redo A;          redo A;
2531        }        }
2532      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2533        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2534            $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  
2535          ## Stay in the state          ## Stay in the state
2536          !!!next-input-character;          !!!next-input-character;
2537          redo A;          redo A;
2538        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2539          $self->{state} = 'data';          !!!cp (166);
2540            $self->{state} = DATA_STATE;
2541          !!!next-input-character;          !!!next-input-character;
2542    
2543          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2544    
2545          redo A;          redo A;
2546        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2547            !!!cp (167);
2548          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2549          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2550          ## reconsume          ## reconsume
2551    
2552          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2553          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2554    
2555          redo A;          redo A;
2556        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2557                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2558            $self->{state} = PUBLIC_STATE;
2559            $self->{s_kwd} = chr $self->{nc};
2560          !!!next-input-character;          !!!next-input-character;
2561          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2562              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2563            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2564            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2565                $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  
2566          !!!next-input-character;          !!!next-input-character;
2567          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;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2568        } else {        } else {
2569            !!!cp (180);
2570            !!!parse-error (type => 'string after DOCTYPE name');
2571            $self->{ct}->{quirks} = 1;
2572    
2573            $self->{state} = BOGUS_DOCTYPE_STATE;
2574          !!!next-input-character;          !!!next-input-character;
2575          #          redo A;
2576        }        }
2577        } elsif ($self->{state} == PUBLIC_STATE) {
2578        !!!parse-error (type => 'string after DOCTYPE name');        ## ASCII case-insensitive
2579        $self->{state} = 'bogus DOCTYPE';        if ($self->{nc} == [
2580        # next-input-character is already done              undef,
2581        redo A;              0x0055, # U
2582      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {              0x0042, # B
2583        if ({              0x004C, # L
2584              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,              0x0049, # I
2585              #0x000D => 1, # HT, LF, VT, FF, SP, CR            ]->[length $self->{s_kwd}] or
2586            }->{$self->{next_input_character}}) {            $self->{nc} == [
2587                undef,
2588                0x0075, # u
2589                0x0062, # b
2590                0x006C, # l
2591                0x0069, # i
2592              ]->[length $self->{s_kwd}]) {
2593            !!!cp (175);
2594            ## Stay in the state.
2595            $self->{s_kwd} .= chr $self->{nc};
2596            !!!next-input-character;
2597            redo A;
2598          } elsif ((length $self->{s_kwd}) == 5 and
2599                   ($self->{nc} == 0x0043 or # C
2600                    $self->{nc} == 0x0063)) { # c
2601            !!!cp (168);
2602            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2603            !!!next-input-character;
2604            redo A;
2605          } else {
2606            !!!cp (169);
2607            !!!parse-error (type => 'string after DOCTYPE name',
2608                            line => $self->{line_prev},
2609                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2610            $self->{ct}->{quirks} = 1;
2611    
2612            $self->{state} = BOGUS_DOCTYPE_STATE;
2613            ## Reconsume.
2614            redo A;
2615          }
2616        } elsif ($self->{state} == SYSTEM_STATE) {
2617          ## ASCII case-insensitive
2618          if ($self->{nc} == [
2619                undef,
2620                0x0059, # Y
2621                0x0053, # S
2622                0x0054, # T
2623                0x0045, # E
2624              ]->[length $self->{s_kwd}] or
2625              $self->{nc} == [
2626                undef,
2627                0x0079, # y
2628                0x0073, # s
2629                0x0074, # t
2630                0x0065, # e
2631              ]->[length $self->{s_kwd}]) {
2632            !!!cp (170);
2633            ## Stay in the state.
2634            $self->{s_kwd} .= chr $self->{nc};
2635            !!!next-input-character;
2636            redo A;
2637          } elsif ((length $self->{s_kwd}) == 5 and
2638                   ($self->{nc} == 0x004D or # M
2639                    $self->{nc} == 0x006D)) { # m
2640            !!!cp (171);
2641            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2642            !!!next-input-character;
2643            redo A;
2644          } else {
2645            !!!cp (172);
2646            !!!parse-error (type => 'string after DOCTYPE name',
2647                            line => $self->{line_prev},
2648                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2649            $self->{ct}->{quirks} = 1;
2650    
2651            $self->{state} = BOGUS_DOCTYPE_STATE;
2652            ## Reconsume.
2653            redo A;
2654          }
2655        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2656          if ($is_space->{$self->{nc}}) {
2657            !!!cp (181);
2658          ## Stay in the state          ## Stay in the state
2659          !!!next-input-character;          !!!next-input-character;
2660          redo A;          redo A;
2661        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2662          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2663          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2664            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2668          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2669          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2670            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2674            !!!cp (184);
2675          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2676    
2677          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2678          !!!next-input-character;          !!!next-input-character;
2679    
2680          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2681          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2682    
2683          redo A;          redo A;
2684        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2685            !!!cp (185);
2686          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2687    
2688          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2689          ## reconsume          ## reconsume
2690    
2691          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2692          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2693    
2694          redo A;          redo A;
2695        } else {        } else {
2696            !!!cp (186);
2697          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2698          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2699    
2700            $self->{state} = BOGUS_DOCTYPE_STATE;
2701          !!!next-input-character;          !!!next-input-character;
2702          redo A;          redo A;
2703        }        }
2704      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2705        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2706          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (187);
2707            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2708          !!!next-input-character;          !!!next-input-character;
2709          redo A;          redo A;
2710        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2711            !!!cp (188);
2712          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2713    
2714          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2715            !!!next-input-character;
2716    
2717            $self->{ct}->{quirks} = 1;
2718            !!!emit ($self->{ct}); # DOCTYPE
2719    
2720            redo A;
2721          } elsif ($self->{nc} == -1) {
2722            !!!cp (189);
2723            !!!parse-error (type => 'unclosed PUBLIC literal');
2724    
2725            $self->{state} = DATA_STATE;
2726          ## reconsume          ## reconsume
2727    
2728          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2729          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2730    
2731          redo A;          redo A;
2732        } else {        } else {
2733          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2734              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2735                .= chr $self->{nc};
2736            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2737                                  length $self->{ct}->{pubid});
2738    
2739          ## Stay in the state          ## Stay in the state
2740          !!!next-input-character;          !!!next-input-character;
2741          redo A;          redo A;
2742        }        }
2743      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2744        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2745          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2746            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2747            !!!next-input-character;
2748            redo A;
2749          } elsif ($self->{nc} == 0x003E) { # >
2750            !!!cp (192);
2751            !!!parse-error (type => 'unclosed PUBLIC literal');
2752    
2753            $self->{state} = DATA_STATE;
2754          !!!next-input-character;          !!!next-input-character;
2755    
2756            $self->{ct}->{quirks} = 1;
2757            !!!emit ($self->{ct}); # DOCTYPE
2758    
2759          redo A;          redo A;
2760        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2761            !!!cp (193);
2762          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2763    
2764          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2765          ## reconsume          ## reconsume
2766    
2767          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2768          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2769    
2770          redo A;          redo A;
2771        } else {        } else {
2772          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2773              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2774                .= chr $self->{nc};
2775            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2776                                  length $self->{ct}->{pubid});
2777    
2778          ## Stay in the state          ## Stay in the state
2779          !!!next-input-character;          !!!next-input-character;
2780          redo A;          redo A;
2781        }        }
2782      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2783        if ({        if ($is_space->{$self->{nc}}) {
2784              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2785          ## Stay in the state          ## Stay in the state
2786          !!!next-input-character;          !!!next-input-character;
2787          redo A;          redo A;
2788        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2789          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2790          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2791            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2795          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2796          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2797            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2801          $self->{state} = 'data';          !!!cp (198);
2802            $self->{state} = DATA_STATE;
2803          !!!next-input-character;          !!!next-input-character;
2804    
2805          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2806    
2807          redo A;          redo A;
2808        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2809            !!!cp (199);
2810          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2811    
2812          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2813          ## reconsume          ## reconsume
2814    
2815          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2816          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2817    
2818          redo A;          redo A;
2819        } else {        } else {
2820            !!!cp (200);
2821          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2822          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2823    
2824            $self->{state} = BOGUS_DOCTYPE_STATE;
2825          !!!next-input-character;          !!!next-input-character;
2826          redo A;          redo A;
2827        }        }
2828      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2829        if ({        if ($is_space->{$self->{nc}}) {
2830              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2831          ## Stay in the state          ## Stay in the state
2832          !!!next-input-character;          !!!next-input-character;
2833          redo A;          redo A;
2834        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2835          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2836          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2837            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2841          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2842          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2843            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2847            !!!cp (204);
2848          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2849          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851    
2852          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2853          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2854    
2855          redo A;          redo A;
2856        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2857            !!!cp (205);
2858          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2859    
2860          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2861          ## reconsume          ## reconsume
2862    
2863          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2864          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2865    
2866          redo A;          redo A;
2867        } else {        } else {
2868            !!!cp (206);
2869          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2870          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2871    
2872            $self->{state} = BOGUS_DOCTYPE_STATE;
2873          !!!next-input-character;          !!!next-input-character;
2874          redo A;          redo A;
2875        }        }
2876      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2877        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2878          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2879            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2880          !!!next-input-character;          !!!next-input-character;
2881          redo A;          redo A;
2882        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2883            !!!cp (208);
2884            !!!parse-error (type => 'unclosed SYSTEM literal');
2885    
2886            $self->{state} = DATA_STATE;
2887            !!!next-input-character;
2888    
2889            $self->{ct}->{quirks} = 1;
2890            !!!emit ($self->{ct}); # DOCTYPE
2891    
2892            redo A;
2893          } elsif ($self->{nc} == -1) {
2894            !!!cp (209);
2895          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2896    
2897          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2898          ## reconsume          ## reconsume
2899    
2900          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2901          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2902    
2903          redo A;          redo A;
2904        } else {        } else {
2905          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2906              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2907                .= chr $self->{nc};
2908            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2909                                  length $self->{ct}->{sysid});
2910    
2911          ## Stay in the state          ## Stay in the state
2912          !!!next-input-character;          !!!next-input-character;
2913          redo A;          redo A;
2914        }        }
2915      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2916        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2917          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2918            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2919          !!!next-input-character;          !!!next-input-character;
2920          redo A;          redo A;
2921        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2922            !!!cp (212);
2923          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2924    
2925          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2926            !!!next-input-character;
2927    
2928            $self->{ct}->{quirks} = 1;
2929            !!!emit ($self->{ct}); # DOCTYPE
2930    
2931            redo A;
2932          } elsif ($self->{nc} == -1) {
2933            !!!cp (213);
2934            !!!parse-error (type => 'unclosed SYSTEM literal');
2935    
2936            $self->{state} = DATA_STATE;
2937          ## reconsume          ## reconsume
2938    
2939          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2940          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2941    
2942          redo A;          redo A;
2943        } else {        } else {
2944          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2945              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2946                .= chr $self->{nc};
2947            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2948                                  length $self->{ct}->{sysid});
2949    
2950          ## Stay in the state          ## Stay in the state
2951          !!!next-input-character;          !!!next-input-character;
2952          redo A;          redo A;
2953        }        }
2954      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2955        if ({        if ($is_space->{$self->{nc}}) {
2956              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2957          ## Stay in the state          ## Stay in the state
2958          !!!next-input-character;          !!!next-input-character;
2959          redo A;          redo A;
2960        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2961          $self->{state} = 'data';          !!!cp (216);
2962            $self->{state} = DATA_STATE;
2963          !!!next-input-character;          !!!next-input-character;
2964    
2965          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2966    
2967          redo A;          redo A;
2968        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2969            !!!cp (217);
2970          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2971            $self->{state} = DATA_STATE;
         $self->{state} = 'data';  
2972          ## reconsume          ## reconsume
2973    
2974          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2975          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2976    
2977          redo A;          redo A;
2978        } else {        } else {
2979            !!!cp (218);
2980          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2981          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2982    
2983            $self->{state} = BOGUS_DOCTYPE_STATE;
2984          !!!next-input-character;          !!!next-input-character;
2985          redo A;          redo A;
2986        }        }
2987      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2988        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2989          $self->{state} = 'data';          !!!cp (219);
2990            $self->{state} = DATA_STATE;
2991          !!!next-input-character;          !!!next-input-character;
2992    
2993          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
2994    
2995          redo A;          redo A;
2996        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2997          !!!parse-error (type => 'unclosed DOCTYPE');          !!!cp (220);
2998          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2999          ## reconsume          ## reconsume
3000    
3001          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
3002    
3003          redo A;          redo A;
3004        } else {        } else {
3005            !!!cp (221);
3006            my $s = '';
3007            $self->{read_until}->($s, q[>], 0);
3008    
3009          ## Stay in the state          ## Stay in the state
3010          !!!next-input-character;          !!!next-input-character;
3011          redo A;          redo A;
3012        }        }
3013      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3014        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3015      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3016    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3017          
3018    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3019  } # _get_next_token          !!!cp (221.1);
3020            $self->{state} = CDATA_SECTION_MSE1_STATE;
3021            !!!next-input-character;
3022            redo A;
3023          } elsif ($self->{nc} == -1) {
3024            $self->{state} = DATA_STATE;
3025            !!!next-input-character;
3026            if (length $self->{ct}->{data}) { # character
3027              !!!cp (221.2);
3028              !!!emit ($self->{ct}); # character
3029            } else {
3030              !!!cp (221.3);
3031              ## No token to emit. $self->{ct} is discarded.
3032            }        
3033            redo A;
3034          } else {
3035            !!!cp (221.4);
3036            $self->{ct}->{data} .= chr $self->{nc};
3037            $self->{read_until}->($self->{ct}->{data},
3038                                  q<]>,
3039                                  length $self->{ct}->{data});
3040    
3041  sub _tokenize_attempt_to_consume_an_entity ($$) {          ## Stay in the state.
3042    my ($self, $in_attr) = @_;          !!!next-input-character;
3043            redo A;
3044          }
3045    
3046    if ({        ## ISSUE: "text tokens" in spec.
3047         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3048         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3049        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3050      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3051      ## No error          !!!next-input-character;
3052      return undef;          redo A;
3053    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3054      !!!next-input-character;          !!!cp (221.6);
3055      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3056          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3057        my $code;          ## Reconsume.
3058        X: {          redo A;
3059          my $x_char = $self->{next_input_character};        }
3060          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3061          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3062              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3063            $code ||= 0;          !!!next-input-character;
3064            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3065            $code += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3066            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;  
3067          } else {          } else {
3068            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3069              ## No token to emit. $self->{ct} is discarded.
3070          }          }
3071            redo A;
3072          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3073            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3074            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3075          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
3076            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!next-input-character;
3077            $code = 0xFFFD;          redo A;
3078          } elsif ($code == 0x000D) {        } else {
3079            !!!parse-error (type => 'CR character reference');          !!!cp (221.11);
3080            $code = 0x000A;          $self->{ct}->{data} .= ']]'; # character
3081          } elsif (0x80 <= $code and $code <= 0x9F) {          $self->{state} = CDATA_SECTION_STATE;
3082            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          ## Reconsume.
3083            $code = $c1_entity_char->{$code};          redo A;
3084          }        }
3085        } elsif ($self->{state} == ENTITY_STATE) {
3086          return {type => 'character', data => chr $code};        if ($is_space->{$self->{nc}} or
3087        } # X            {
3088      } elsif (0x0030 <= $self->{next_input_character} and              0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3089               $self->{next_input_character} <= 0x0039) { # 0..9              $self->{entity_add} => 1,
3090        my $code = $self->{next_input_character} - 0x0030;            }->{$self->{nc}}) {
3091        !!!next-input-character;          !!!cp (1001);
3092                  ## Don't consume
3093        while (0x0030 <= $self->{next_input_character} and          ## No error
3094                  $self->{next_input_character} <= 0x0039) { # 0..9          ## Return nothing.
3095          $code *= 10;          #
3096          $code += $self->{next_input_character} - 0x0030;        } elsif ($self->{nc} == 0x0023) { # #
3097                    !!!cp (999);
3098            $self->{state} = ENTITY_HASH_STATE;
3099            $self->{s_kwd} = '#';
3100            !!!next-input-character;
3101            redo A;
3102          } elsif ((0x0041 <= $self->{nc} and
3103                    $self->{nc} <= 0x005A) or # A..Z
3104                   (0x0061 <= $self->{nc} and
3105                    $self->{nc} <= 0x007A)) { # a..z
3106            !!!cp (998);
3107            require Whatpm::_NamedEntityList;
3108            $self->{state} = ENTITY_NAME_STATE;
3109            $self->{s_kwd} = chr $self->{nc};
3110            $self->{entity__value} = $self->{s_kwd};
3111            $self->{entity__match} = 0;
3112          !!!next-input-character;          !!!next-input-character;
3113            redo A;
3114          } else {
3115            !!!cp (1027);
3116            !!!parse-error (type => 'bare ero');
3117            ## Return nothing.
3118            #
3119        }        }
3120    
3121        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3122          ## reference" algorithm.  In other word, there is an "&" character
3123          ## that does not introduce a character reference, which would be
3124          ## appended to the parent element or the attribute value in later
3125          ## process of the tokenizer.
3126    
3127          if ($self->{prev_state} == DATA_STATE) {
3128            !!!cp (997);
3129            $self->{state} = $self->{prev_state};
3130            ## Reconsume.
3131            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3132                      line => $self->{line_prev},
3133                      column => $self->{column_prev},
3134                     });
3135            redo A;
3136          } else {
3137            !!!cp (996);
3138            $self->{ca}->{value} .= '&';
3139            $self->{state} = $self->{prev_state};
3140            ## Reconsume.
3141            redo A;
3142          }
3143        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3144          if ($self->{nc} == 0x0078 or # x
3145              $self->{nc} == 0x0058) { # X
3146            !!!cp (995);
3147            $self->{state} = HEXREF_X_STATE;
3148            $self->{s_kwd} .= chr $self->{nc};
3149            !!!next-input-character;
3150            redo A;
3151          } elsif (0x0030 <= $self->{nc} and
3152                   $self->{nc} <= 0x0039) { # 0..9
3153            !!!cp (994);
3154            $self->{state} = NCR_NUM_STATE;
3155            $self->{s_kwd} = $self->{nc} - 0x0030;
3156          !!!next-input-character;          !!!next-input-character;
3157            redo A;
3158        } else {        } else {
3159            !!!parse-error (type => 'bare nero',
3160                            line => $self->{line_prev},
3161                            column => $self->{column_prev} - 1);
3162    
3163            ## NOTE: According to the spec algorithm, nothing is returned,
3164            ## and then "&#" is appended to the parent element or the attribute
3165            ## value in the later processing.
3166    
3167            if ($self->{prev_state} == DATA_STATE) {
3168              !!!cp (1019);
3169              $self->{state} = $self->{prev_state};
3170              ## Reconsume.
3171              !!!emit ({type => CHARACTER_TOKEN,
3172                        data => '&#',
3173                        line => $self->{line_prev},
3174                        column => $self->{column_prev} - 1,
3175                       });
3176              redo A;
3177            } else {
3178              !!!cp (993);
3179              $self->{ca}->{value} .= '&#';
3180              $self->{state} = $self->{prev_state};
3181              ## Reconsume.
3182              redo A;
3183            }
3184          }
3185        } elsif ($self->{state} == NCR_NUM_STATE) {
3186          if (0x0030 <= $self->{nc} and
3187              $self->{nc} <= 0x0039) { # 0..9
3188            !!!cp (1012);
3189            $self->{s_kwd} *= 10;
3190            $self->{s_kwd} += $self->{nc} - 0x0030;
3191            
3192            ## Stay in the state.
3193            !!!next-input-character;
3194            redo A;
3195          } elsif ($self->{nc} == 0x003B) { # ;
3196            !!!cp (1013);
3197            !!!next-input-character;
3198            #
3199          } else {
3200            !!!cp (1014);
3201          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3202            ## Reconsume.
3203            #
3204        }        }
3205    
3206        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3207          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3208          my $c = $self->{column_prev};
3209          if ($charref_map->{$code}) {
3210            !!!cp (1015);
3211            !!!parse-error (type => 'invalid character reference',
3212                            text => (sprintf 'U+%04X', $code),
3213                            line => $l, column => $c);
3214            $code = $charref_map->{$code};
3215          } elsif ($code > 0x10FFFF) {
3216            !!!cp (1016);
3217            !!!parse-error (type => 'invalid character reference',
3218                            text => (sprintf 'U-%08X', $code),
3219                            line => $l, column => $c);
3220          $code = 0xFFFD;          $code = 0xFFFD;
3221          }
3222    
3223          if ($self->{prev_state} == DATA_STATE) {
3224            !!!cp (992);
3225            $self->{state} = $self->{prev_state};
3226            ## Reconsume.
3227            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3228                      line => $l, column => $c,
3229                     });
3230            redo A;
3231          } else {
3232            !!!cp (991);
3233            $self->{ca}->{value} .= chr $code;
3234            $self->{ca}->{has_reference} = 1;
3235            $self->{state} = $self->{prev_state};
3236            ## Reconsume.
3237            redo A;
3238          }
3239        } elsif ($self->{state} == HEXREF_X_STATE) {
3240          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3241              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3242              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3243            # 0..9, A..F, a..f
3244            !!!cp (990);
3245            $self->{state} = HEXREF_HEX_STATE;
3246            $self->{s_kwd} = 0;
3247            ## Reconsume.
3248            redo A;
3249          } else {
3250            !!!parse-error (type => 'bare hcro',
3251                            line => $self->{line_prev},
3252                            column => $self->{column_prev} - 2);
3253    
3254            ## NOTE: According to the spec algorithm, nothing is returned,
3255            ## and then "&#" followed by "X" or "x" is appended to the parent
3256            ## element or the attribute value in the later processing.
3257    
3258            if ($self->{prev_state} == DATA_STATE) {
3259              !!!cp (1005);
3260              $self->{state} = $self->{prev_state};
3261              ## Reconsume.
3262              !!!emit ({type => CHARACTER_TOKEN,
3263                        data => '&' . $self->{s_kwd},
3264                        line => $self->{line_prev},
3265                        column => $self->{column_prev} - length $self->{s_kwd},
3266                       });
3267              redo A;
3268            } else {
3269              !!!cp (989);
3270              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3271              $self->{state} = $self->{prev_state};
3272              ## Reconsume.
3273              redo A;
3274            }
3275          }
3276        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3277          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3278            # 0..9
3279            !!!cp (1002);
3280            $self->{s_kwd} *= 0x10;
3281            $self->{s_kwd} += $self->{nc} - 0x0030;
3282            ## Stay in the state.
3283            !!!next-input-character;
3284            redo A;
3285          } elsif (0x0061 <= $self->{nc} and
3286                   $self->{nc} <= 0x0066) { # a..f
3287            !!!cp (1003);
3288            $self->{s_kwd} *= 0x10;
3289            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3290            ## Stay in the state.
3291            !!!next-input-character;
3292            redo A;
3293          } elsif (0x0041 <= $self->{nc} and
3294                   $self->{nc} <= 0x0046) { # A..F
3295            !!!cp (1004);
3296            $self->{s_kwd} *= 0x10;
3297            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3298            ## Stay in the state.
3299            !!!next-input-character;
3300            redo A;
3301          } elsif ($self->{nc} == 0x003B) { # ;
3302            !!!cp (1006);
3303            !!!next-input-character;
3304            #
3305          } else {
3306            !!!cp (1007);
3307            !!!parse-error (type => 'no refc',
3308                            line => $self->{line},
3309                            column => $self->{column});
3310            ## Reconsume.
3311            #
3312          }
3313    
3314          my $code = $self->{s_kwd};
3315          my $l = $self->{line_prev};
3316          my $c = $self->{column_prev};
3317          if ($charref_map->{$code}) {
3318            !!!cp (1008);
3319            !!!parse-error (type => 'invalid character reference',
3320                            text => (sprintf 'U+%04X', $code),
3321                            line => $l, column => $c);
3322            $code = $charref_map->{$code};
3323        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3324          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3325            !!!parse-error (type => 'invalid character reference',
3326                            text => (sprintf 'U-%08X', $code),
3327                            line => $l, column => $c);
3328          $code = 0xFFFD;          $code = 0xFFFD;
       } elsif ($code == 0x000D) {  
         !!!parse-error (type => 'CR character reference');  
         $code = 0x000A;  
       } elsif (0x80 <= $code and $code <= 0x9F) {  
         !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);  
         $code = $c1_entity_char->{$code};  
3329        }        }
3330          
3331        return {type => 'character', data => chr $code};        if ($self->{prev_state} == DATA_STATE) {
3332      } else {          !!!cp (988);
3333        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3334        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3335        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3336        return undef;                    line => $l, column => $c,
3337      }                   });
3338    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3339              $self->{next_input_character} <= 0x005A) or        } else {
3340             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3341              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3342      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3343      !!!next-input-character;          $self->{state} = $self->{prev_state};
3344            ## Reconsume.
3345      my $value = $entity_name;          redo A;
3346      my $match;        }
3347      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3348      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3349              ## NOTE: Some number greater than the maximum length of entity name
3350      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3351             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3352             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3353               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3354              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3355               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3356              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3357               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3358              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3359        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3360        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3361          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3362            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3363            $match = 1;              $self->{entity__match} = 1;
3364                !!!next-input-character;
3365                #
3366              } else {
3367                !!!cp (1021);
3368                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3369                $self->{entity__match} = -1;
3370                ## Stay in the state.
3371                !!!next-input-character;
3372                redo A;
3373              }
3374            } else {
3375              !!!cp (1022);
3376              $self->{entity__value} .= chr $self->{nc};
3377              $self->{entity__match} *= 2;
3378              ## Stay in the state.
3379            !!!next-input-character;            !!!next-input-character;
3380            last;            redo A;
3381          } elsif (not $in_attr) {          }
3382            $value = $EntityChar->{$entity_name};        }
3383            $match = -1;  
3384          my $data;
3385          my $has_ref;
3386          if ($self->{entity__match} > 0) {
3387            !!!cp (1023);
3388            $data = $self->{entity__value};
3389            $has_ref = 1;
3390            #
3391          } elsif ($self->{entity__match} < 0) {
3392            !!!parse-error (type => 'no refc');
3393            if ($self->{prev_state} != DATA_STATE and # in attribute
3394                $self->{entity__match} < -1) {
3395              !!!cp (1024);
3396              $data = '&' . $self->{s_kwd};
3397              #
3398          } else {          } else {
3399            $value .= chr $self->{next_input_character};            !!!cp (1025);
3400              $data = $self->{entity__value};
3401              $has_ref = 1;
3402              #
3403          }          }
3404        } else {        } else {
3405          $value .= chr $self->{next_input_character};          !!!cp (1026);
3406            !!!parse-error (type => 'bare ero',
3407                            line => $self->{line_prev},
3408                            column => $self->{column_prev} - length $self->{s_kwd});
3409            $data = '&' . $self->{s_kwd};
3410            #
3411          }
3412      
3413          ## NOTE: In these cases, when a character reference is found,
3414          ## it is consumed and a character token is returned, or, otherwise,
3415          ## nothing is consumed and returned, according to the spec algorithm.
3416          ## In this implementation, anything that has been examined by the
3417          ## tokenizer is appended to the parent element or the attribute value
3418          ## as string, either literal string when no character reference or
3419          ## entity-replaced string otherwise, in this stage, since any characters
3420          ## that would not be consumed are appended in the data state or in an
3421          ## appropriate attribute value state anyway.
3422    
3423          if ($self->{prev_state} == DATA_STATE) {
3424            !!!cp (986);
3425            $self->{state} = $self->{prev_state};
3426            ## Reconsume.
3427            !!!emit ({type => CHARACTER_TOKEN,
3428                      data => $data,
3429                      line => $self->{line_prev},
3430                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3431                     });
3432            redo A;
3433          } else {
3434            !!!cp (985);
3435            $self->{ca}->{value} .= $data;
3436            $self->{ca}->{has_reference} = 1 if $has_ref;
3437            $self->{state} = $self->{prev_state};
3438            ## Reconsume.
3439            redo A;
3440        }        }
       !!!next-input-character;  
     }  
       
     if ($match > 0) {  
       return {type => 'character', data => $value};  
     } elsif ($match < 0) {  
       !!!parse-error (type => 'no refc');  
       return {type => 'character', data => $value};  
3441      } else {      } else {
3442        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       return {type => 'character', data => '&'.$value};  
3443      }      }
3444    } else {    } # A  
3445      ## no characters are consumed  
3446      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3447      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3448    
3449  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3450    my $self = shift;    my $self = shift;
# Line 1773  sub _initialize_tree_constructor ($) { Line 3453  sub _initialize_tree_constructor ($) {
3453    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3454    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3455    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3456      $self->{document}->set_user_data (manakai_source_line => 1);
3457      $self->{document}->set_user_data (manakai_source_column => 1);
3458  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3459    
3460  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1792  sub _construct_tree ($) { Line 3474  sub _construct_tree ($) {
3474    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3475    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3476    ## not defined.    ## not defined.
   
   ## Append a character: collect it and all subsequent consecutive  
   ## characters and insert one Text node whose data is concatenation  
   ## of all those characters. # MUST  
3477        
3478    !!!next-token;    !!!next-token;
3479    
   $self->{insertion_mode} = 'before head';  
3480    undef $self->{form_element};    undef $self->{form_element};
3481    undef $self->{head_element};    undef $self->{head_element};
3482      undef $self->{head_element_inserted};
3483    $self->{open_elements} = [];    $self->{open_elements} = [];
3484    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3485      undef $self->{ignore_newline};
3486    
3487      ## NOTE: The "initial" insertion mode.
3488    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3489    
3490      ## NOTE: The "before html" insertion mode.
3491    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3492      $self->{insertion_mode} = BEFORE_HEAD_IM;
3493    
3494      ## NOTE: The "before head" insertion mode and so on.
3495    $self->_tree_construction_main;    $self->_tree_construction_main;
3496  } # _construct_tree  } # _construct_tree
3497    
3498  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3499    my $self = shift;    my $self = shift;
3500    
3501      ## NOTE: "initial" insertion mode
3502    
3503    INITIAL: {    INITIAL: {
3504      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
3505        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3506        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
3507        ## language.        ## language.
3508        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3509        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3510        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3511        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3512            defined $token->{public_identifier} or            defined $token->{sysid}) {
3513            defined $token->{system_identifier}) {          !!!cp ('t1');
3514          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3515        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3516          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3517          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3518          } elsif (defined $token->{pubid}) {
3519            if ($token->{pubid} eq 'XSLT-compat') {
3520              !!!cp ('t1.2');
3521              !!!parse-error (type => 'XSLT-compat', token => $token,
3522                              level => $self->{level}->{should});
3523            } else {
3524              !!!parse-error (type => 'not HTML5', token => $token);
3525            }
3526          } else {
3527            !!!cp ('t3');
3528            #
3529        }        }
3530                
3531        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3532          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3533        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3534            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3535        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3536            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3537        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3538        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3539        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3540                
3541        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3542            !!!cp ('t4');
3543          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3544        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3545          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3546          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3547          if ({          my $prefix = [
3548            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3549            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3550            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3551            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3552            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3553            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3554            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3555            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3556            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3557            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3558            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3559            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3560            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3561            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3562            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3563            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3564            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3565            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3566            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3567            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3568            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3569            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3570            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3571            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3572            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3573            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3574            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3575            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3576            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3577            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3578            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3579            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3580            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3581            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3582            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3583            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3584            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3585            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3586            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3587            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3588            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3589            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3590            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3591            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3592            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3593            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3594            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3595            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3596            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3597            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3598            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3599            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3600            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3601            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3602            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3603            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3604            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3605            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3606            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3607            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3608            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3609            "-//W3C//DTD W3 HTML//EN" => 1,            }
3610            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3611            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3612            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3613            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3614            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3615            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3616            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3617          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3618                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3619            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3620                !!!cp ('t6');
3621              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3622            } else {            } else {
3623                !!!cp ('t7');
3624              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3625            }            }
3626          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3627                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3628              !!!cp ('t8');
3629            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3630            } else {
3631              !!!cp ('t9');
3632          }          }
3633          } else {
3634            !!!cp ('t10');
3635        }        }
3636        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3637          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3638          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3639          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") {
3640              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3641              ## marked as quirks.
3642            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3643              !!!cp ('t11');
3644            } else {
3645              !!!cp ('t12');
3646          }          }
3647          } else {
3648            !!!cp ('t13');
3649        }        }
3650                
3651        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3652        !!!next-token;        !!!next-token;
3653        return;        return;
3654      } elsif ({      } elsif ({
3655                'start tag' => 1,                START_TAG_TOKEN, 1,
3656                'end tag' => 1,                END_TAG_TOKEN, 1,
3657                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
3658               }->{$token->{type}}) {               }->{$token->{type}}) {
3659        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3660          !!!parse-error (type => 'no DOCTYPE', token => $token);
3661        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3662        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3663        ## reprocess        ## reprocess
3664          !!!ack-later;
3665        return;        return;
3666      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3667        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3668          ## Ignore the token          ## Ignore the token
3669    
3670          unless (length $token->{data}) {          unless (length $token->{data}) {
3671            ## Stay in the phase            !!!cp ('t15');
3672              ## Stay in the insertion mode.
3673            !!!next-token;            !!!next-token;
3674            redo INITIAL;            redo INITIAL;
3675            } else {
3676              !!!cp ('t16');
3677          }          }
3678          } else {
3679            !!!cp ('t17');
3680        }        }
3681    
3682        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3683        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3684        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3685        ## reprocess        ## reprocess
3686        return;        return;
3687      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
3688          !!!cp ('t18');
3689        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3690        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3691                
3692        ## Stay in the phase.        ## Stay in the insertion mode.
3693        !!!next-token;        !!!next-token;
3694        redo INITIAL;        redo INITIAL;
3695      } else {      } else {
3696        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
3697      }      }
3698    } # INITIAL    } # INITIAL
3699    
3700      die "$0: _tree_construction_initial: This should be never reached";
3701  } # _tree_construction_initial  } # _tree_construction_initial
3702    
3703  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3704    my $self = shift;    my $self = shift;
3705    
3706      ## NOTE: "before html" insertion mode.
3707        
3708    B: {    B: {
3709        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3710          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3711            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3712          ## Ignore the token          ## Ignore the token
3713          ## Stay in the phase          ## Stay in the insertion mode.
3714          !!!next-token;          !!!next-token;
3715          redo B;          redo B;
3716        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3717            !!!cp ('t20');
3718          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3719          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3720          ## Stay in the phase          ## Stay in the insertion mode.
3721          !!!next-token;          !!!next-token;
3722          redo B;          redo B;
3723        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3724          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3725            ## Ignore the token.            ## Ignore the token.
3726    
3727            unless (length $token->{data}) {            unless (length $token->{data}) {
3728              ## Stay in the phase              !!!cp ('t21');
3729                ## Stay in the insertion mode.
3730              !!!next-token;              !!!next-token;
3731              redo B;              redo B;
3732              } else {
3733                !!!cp ('t22');
3734            }            }
3735            } else {
3736              !!!cp ('t23');
3737          }          }
3738    
3739            $self->{application_cache_selection}->(undef);
3740    
3741          #          #
3742          } elsif ($token->{type} == START_TAG_TOKEN) {
3743            if ($token->{tag_name} eq 'html') {
3744              my $root_element;
3745              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3746              $self->{document}->append_child ($root_element);
3747              push @{$self->{open_elements}},
3748                  [$root_element, $el_category->{html}];
3749    
3750              if ($token->{attributes}->{manifest}) {
3751                !!!cp ('t24');
3752                $self->{application_cache_selection}
3753                    ->($token->{attributes}->{manifest}->{value});
3754                ## ISSUE: Spec is unclear on relative references.
3755                ## According to Hixie (#whatwg 2008-03-19), it should be
3756                ## resolved against the base URI of the document in HTML
3757                ## or xml:base of the element in XHTML.
3758              } else {
3759                !!!cp ('t25');
3760                $self->{application_cache_selection}->(undef);
3761              }
3762    
3763              !!!nack ('t25c');
3764    
3765              !!!next-token;
3766              return; ## Go to the "before head" insertion mode.
3767            } else {
3768              !!!cp ('t25.1');
3769              #
3770            }
3771        } elsif ({        } elsif ({
3772                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3773                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3774                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3775          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3776          #          #
3777        } else {        } else {
3778          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3779        }        }
3780        my $root_element; !!!create-element ($root_element, 'html');  
3781        $self->{document}->append_child ($root_element);      my $root_element;
3782        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3783        ## reprocess      $self->{document}->append_child ($root_element);
3784        #redo B;      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3785        return; ## Go to the main phase.  
3786        $self->{application_cache_selection}->(undef);
3787    
3788        ## NOTE: Reprocess the token.
3789        !!!ack-later;
3790        return; ## Go to the "before head" insertion mode.
3791    } # B    } # B
3792    
3793      die "$0: _tree_construction_root_element: This should never be reached";
3794  } # _tree_construction_root_element  } # _tree_construction_root_element
3795    
3796  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2036  sub _reset_insertion_mode ($) { Line 3805  sub _reset_insertion_mode ($) {
3805            
3806      ## Step 3      ## Step 3
3807      S3: {      S3: {
       ## ISSUE: Oops! "If node is the first node in the stack of open  
       ## elements, then set last to true. If the context element of the  
       ## HTML fragment parsing algorithm is neither a td element nor a  
       ## th element, then set node to the context element. (fragment case)":  
       ## The second "if" is in the scope of the first "if"!?  
3808        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3809          $last = 1;          $last = 1;
3810          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3811            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3812                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3813              #          } else {
3814            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3815          }          }
3816        }        }
3817              
3818        ## Step 4..13        ## Step 4..14
3819        my $new_mode = {        my $new_mode;
3820                        select => 'in select',        if ($node->[1] & FOREIGN_EL) {
3821                        td => 'in cell',          !!!cp ('t28.1');
3822                        th => 'in cell',          ## NOTE: Strictly spaking, the line below only applies to MathML and
3823                        tr => 'in row',          ## SVG elements.  Currently the HTML syntax supports only MathML and
3824                        tbody => 'in table body',          ## SVG elements as foreigners.
3825                        thead => 'in table head',          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3826                        tfoot => 'in table foot',        } elsif ($node->[1] == TABLE_CELL_EL) {
3827                        caption => 'in caption',          if ($last) {
3828                        colgroup => 'in column group',            !!!cp ('t28.2');
3829                        table => 'in table',            #
3830                        head => 'in body', # not in head!          } else {
3831                        body => 'in body',            !!!cp ('t28.3');
3832                        frameset => 'in frameset',            $new_mode = IN_CELL_IM;
3833                       }->{$node->[1]};          }
3834          } else {
3835            !!!cp ('t28.4');
3836            $new_mode = {
3837                          select => IN_SELECT_IM,
3838                          ## NOTE: |option| and |optgroup| do not set
3839                          ## insertion mode to "in select" by themselves.
3840                          tr => IN_ROW_IM,
3841                          tbody => IN_TABLE_BODY_IM,
3842                          thead => IN_TABLE_BODY_IM,
3843                          tfoot => IN_TABLE_BODY_IM,
3844                          caption => IN_CAPTION_IM,
3845                          colgroup => IN_COLUMN_GROUP_IM,
3846                          table => IN_TABLE_IM,
3847                          head => IN_BODY_IM, # not in head!
3848                          body => IN_BODY_IM,
3849                          frameset => IN_FRAMESET_IM,
3850                         }->{$node->[0]->manakai_local_name};
3851          }
3852        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3853                
3854        ## Step 14        ## Step 15
3855        if ($node->[1] eq 'html') {        if ($node->[1] == HTML_EL) {
3856          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3857            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3858              $self->{insertion_mode} = BEFORE_HEAD_IM;
3859          } else {          } else {
3860            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3861              !!!cp ('t30');
3862              $self->{insertion_mode} = AFTER_HEAD_IM;
3863          }          }
3864          return;          return;
3865          } else {
3866            !!!cp ('t31');
3867        }        }
3868                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3869        ## Step 16        ## Step 16
3870          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3871          
3872          ## Step 17
3873        $i--;        $i--;
3874        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3875                
3876        ## Step 17        ## Step 18
3877        redo S3;        redo S3;
3878      } # S3      } # S3
3879    
3880      die "$0: _reset_insertion_mode: This line should never be reached";
3881  } # _reset_insertion_mode  } # _reset_insertion_mode
3882    
3883  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3884    my $self = shift;    my $self = shift;
3885    
   my $previous_insertion_mode;  
   
3886    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3887    
3888    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2114  sub _tree_construction_main ($) { Line 3899  sub _tree_construction_main ($) {
3899      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3900      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3901        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3902            !!!cp ('t32');
3903          return;          return;
3904        }        }
3905      }      }
# Line 2128  sub _tree_construction_main ($) { Line 3914  sub _tree_construction_main ($) {
3914    
3915        ## Step 6        ## Step 6
3916        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3917            !!!cp ('t33_1');
3918          #          #
3919        } else {        } else {
3920          my $in_open_elements;          my $in_open_elements;
3921          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3922            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3923                !!!cp ('t33');
3924              $in_open_elements = 1;              $in_open_elements = 1;
3925              last OE;              last OE;
3926            }            }
3927          }          }
3928          if ($in_open_elements) {          if ($in_open_elements) {
3929              !!!cp ('t34');
3930            #            #
3931          } else {          } else {
3932              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3933              !!!cp ('t35');
3934            redo S4;            redo S4;
3935          }          }
3936        }        }
# Line 2162  sub _tree_construction_main ($) { Line 3953  sub _tree_construction_main ($) {
3953    
3954        ## Step 11        ## Step 11
3955        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3956            !!!cp ('t36');
3957          ## Step 7'          ## Step 7'
3958          $i++;          $i++;
3959          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3960                    
3961          redo S7;          redo S7;
3962        }        }
3963    
3964          !!!cp ('t37');
3965      } # S7      } # S7
3966    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3967    
3968    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3969      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3970        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3971            !!!cp ('t38');
3972          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3973          return;          return;
3974        }        }
3975      }      }
3976    
3977        !!!cp ('t39');
3978    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3979    
3980    my $parse_rcdata = sub ($$) {    my $insert;
3981      my ($content_model_flag, $insert) = @_;  
3982      my $parse_rcdata = sub ($) {
3983        my ($content_model_flag) = @_;
3984    
3985      ## Step 1      ## Step 1
3986      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3987      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3988    
3989      ## Step 2      ## Step 2
3990      $insert->($el); # /context node/->append_child ($el)      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
   
     ## Step 3  
     $self->{content_model_flag} = $content_model_flag; # CDATA or RCDATA  
3991      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3992    
3993      ## Step 4      ## Step 3, 4
3994      my $text = '';      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
     !!!next-token;  
     while ($token->{type} eq 'character') { # or until stop tokenizing  
       $text .= $token->{data};  
       !!!next-token;  
     }  
   
     ## Step 5  
     if (length $text) {  
       my $text = $self->{document}->create_text_node ($text);  
       $el->append_child ($text);  
     }  
3995    
3996      ## Step 6      !!!nack ('t40.1');
     $self->{content_model_flag} = 'PCDATA';  
   
     ## Step 7  
     if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {  
       ## Ignore the token  
     } else {  
       !!!parse-error (type => 'in '.$content_model_flag.':#'.$token->{type});  
     }  
3997      !!!next-token;      !!!next-token;
3998    }; # $parse_rcdata    }; # $parse_rcdata
3999    
4000    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
4001      my $insert = $_[0];      ## Step 1
4002      my $script_el;      my $script_el;
4003      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4004    
4005        ## Step 2
4006      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4007    
4008      $self->{content_model_flag} = 'CDATA';      ## Step 3
4009        ## TODO: Mark as "already executed", if ...
4010    
4011        ## Step 4
4012        $insert->($script_el);
4013    
4014        ## ISSUE: $script_el is not put into the stack
4015        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4016    
4017        ## Step 5
4018        $self->{content_model} = CDATA_CONTENT_MODEL;
4019      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
       
     my $text = '';  
     !!!next-token;  
     while ($token->{type} eq 'character') {  
       $text .= $token->{data};  
       !!!next-token;  
     } # stop if non-character token or tokenizer stops tokenising  
     if (length $text) {  
       $script_el->manakai_append_text ($text);  
     }  
                 
     $self->{content_model_flag} = 'PCDATA';  
4020    
4021      if ($token->{type} eq 'end tag' and      ## Step 6-7
4022          $token->{tag_name} eq 'script') {      $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
       ## Ignore the token  
     } else {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
       ## ISSUE: And ignore?  
       ## TODO: mark as "already executed"  
     }  
       
     if (defined $self->{inner_html_node}) {  
       ## TODO: mark as "already executed"  
     } else {  
       ## TODO: $old_insertion_point = current insertion point  
       ## TODO: insertion point = just before the next input character  
4023    
4024        $insert->($script_el);      !!!nack ('t40.2');
         
       ## TODO: insertion point = $old_insertion_point (might be "undefined")  
         
       ## TODO: if there is a script that will execute as soon as the parser resume, then...  
     }  
       
4025      !!!next-token;      !!!next-token;
4026    }; # $script_start_tag    }; # $script_start_tag
4027    
4028      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4029      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4030      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4031      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4032    
4033    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4034      my $tag_name = shift;      my $end_tag_token = shift;
4035        my $tag_name = $end_tag_token->{tag_name};
4036    
4037        ## NOTE: The adoption agency algorithm (AAA).
4038    
4039      FET: {      FET: {
4040        ## Step 1        ## Step 1
4041        my $formatting_element;        my $formatting_element;
4042        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4043        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4044          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4045              !!!cp ('t52');
4046              last AFE;
4047            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4048                         eq $tag_name) {
4049              !!!cp ('t51');
4050            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4051            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4052            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4053          }          }
4054        } # AFE        } # AFE
4055        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4056          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4057            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4058          ## Ignore the token          ## Ignore the token
4059          !!!next-token;          !!!next-token;
4060          return;          return;
# Line 2296  sub _tree_construction_main ($) { Line 4066  sub _tree_construction_main ($) {
4066          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4067          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4068            if ($in_scope) {            if ($in_scope) {
4069                !!!cp ('t54');
4070              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4071              last INSCOPE;              last INSCOPE;
4072            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4073              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4074                !!!parse-error (type => 'unmatched end tag',
4075                                text => $token->{tag_name},
4076                                token => $end_tag_token);
4077              ## Ignore the token              ## Ignore the token
4078              !!!next-token;              !!!next-token;
4079              return;              return;
4080            }            }
4081          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4082                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4083            $in_scope = 0;            $in_scope = 0;
4084          }          }
4085        } # INSCOPE        } # INSCOPE
4086        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4087          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4088            !!!parse-error (type => 'unmatched end tag',
4089                            text => $token->{tag_name},
4090                            token => $end_tag_token);
4091          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4092          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4093          return;          return;
4094        }        }
4095        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4096          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4097            !!!parse-error (type => 'not closed',
4098                            text => $self->{open_elements}->[-1]->[0]
4099                                ->manakai_local_name,
4100                            token => $end_tag_token);
4101        }        }
4102                
4103        ## Step 2        ## Step 2
# Line 2326  sub _tree_construction_main ($) { Line 4105  sub _tree_construction_main ($) {
4105        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4106        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4107          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4108          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4109              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4110              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4111               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4112              !!!cp ('t59');
4113            $furthest_block = $node;            $furthest_block = $node;
4114            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4115              ## NOTE: The topmost (eldest) node.
4116          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4117              !!!cp ('t60');
4118            last OE;            last OE;
4119          }          }
4120        } # OE        } # OE
4121                
4122        ## Step 3        ## Step 3
4123        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4124            !!!cp ('t61');
4125          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4126          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4127          !!!next-token;          !!!next-token;
# Line 2351  sub _tree_construction_main ($) { Line 4134  sub _tree_construction_main ($) {
4134        ## Step 5        ## Step 5
4135        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4136        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4137            !!!cp ('t62');
4138          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4139        }        }
4140                
# Line 2373  sub _tree_construction_main ($) { Line 4157  sub _tree_construction_main ($) {
4157          S7S2: {          S7S2: {
4158            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4159              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4160                  !!!cp ('t63');
4161                $node_i_in_active = $_;                $node_i_in_active = $_;
4162                last S7S2;                last S7S2;
4163              }              }
# Line 2386  sub _tree_construction_main ($) { Line 4171  sub _tree_construction_main ($) {
4171                    
4172          ## Step 4          ## Step 4
4173          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4174              !!!cp ('t64');
4175            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4176          }          }
4177                    
4178          ## Step 5          ## Step 5
4179          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4180              !!!cp ('t65');
4181            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4182            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4183            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2408  sub _tree_construction_main ($) { Line 4195  sub _tree_construction_main ($) {
4195        } # S7          } # S7  
4196                
4197        ## Step 8        ## Step 8
4198        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4199            my $foster_parent_element;
4200            my $next_sibling;
4201            OE: for (reverse 0..$#{$self->{open_elements}}) {
4202              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4203                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4204                                 if (defined $parent and $parent->node_type == 1) {
4205                                   !!!cp ('t65.1');
4206                                   $foster_parent_element = $parent;
4207                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4208                                 } else {
4209                                   !!!cp ('t65.2');
4210                                   $foster_parent_element
4211                                     = $self->{open_elements}->[$_ - 1]->[0];
4212                                 }
4213                                 last OE;
4214                               }
4215                             } # OE
4216                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4217                               unless defined $foster_parent_element;
4218            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4219            $open_tables->[-1]->[1] = 1; # tainted
4220          } else {
4221            !!!cp ('t65.3');
4222            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4223          }
4224                
4225        ## Step 9        ## Step 9
4226        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2425  sub _tree_construction_main ($) { Line 4237  sub _tree_construction_main ($) {
4237        my $i;        my $i;
4238        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4239          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4240              !!!cp ('t66');
4241            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4242            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4243          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4244              !!!cp ('t67');
4245            $i = $_;            $i = $_;
4246          }          }
4247        } # AFE        } # AFE
# Line 2437  sub _tree_construction_main ($) { Line 4251  sub _tree_construction_main ($) {
4251        undef $i;        undef $i;
4252        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4253          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4254              !!!cp ('t68');
4255            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4256            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4257          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4258              !!!cp ('t69');
4259            $i = $_;            $i = $_;
4260          }          }
4261        } # OE        } # OE
4262        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4263                
4264        ## Step 14        ## Step 14
4265        redo FET;        redo FET;
4266      } # FET      } # FET
4267    }; # $formatting_end_tag    }; # $formatting_end_tag
4268    
4269    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4270      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4271    }; # $insert_to_current    }; # $insert_to_current
4272    
4273    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4274                         my $child = shift;      my $child = shift;
4275                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4276                              table => 1, tbody => 1, tfoot => 1,        # MUST
4277                              thead => 1, tr => 1,        my $foster_parent_element;
4278                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4279                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4280                           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') {  
4281                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4282                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4283                                   !!!cp ('t70');
4284                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4285                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4286                               } else {                               } else {
4287                                   !!!cp ('t71');
4288                                 $foster_parent_element                                 $foster_parent_element
4289                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4290                               }                               }
# Line 2480  sub _tree_construction_main ($) { Line 4295  sub _tree_construction_main ($) {
4295                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4296                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4297                             ($child, $next_sibling);                             ($child, $next_sibling);
4298                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4299                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4300                         }        !!!cp ('t72');
4301          $self->{open_elements}->[-1]->[0]->append_child ($child);
4302        }
4303    }; # $insert_to_foster    }; # $insert_to_foster
4304    
4305    my $in_body = sub {    ## NOTE: Insert a character (MUST): When a character is inserted, if
4306      my $insert = shift;    ## the last node that was inserted by the parser is a Text node and
4307      if ($token->{type} eq 'start tag') {    ## the character has to be inserted after that node, then the
4308        if ($token->{tag_name} eq 'script') {    ## character is appended to the Text node.  However, if any other
4309          ## NOTE: This is an "as if in head" code clone    ## node is inserted by the parser, then a new Text node is created
4310          $script_start_tag->($insert);    ## and the character is appended as that Text node.  If I'm not
4311          return;    ## wrong, for a parser with scripting disabled, there are only two
4312        } elsif ($token->{tag_name} eq 'style') {    ## cases where this occurs.  One is the case where an element node
4313          ## NOTE: This is an "as if in head" code clone    ## is inserted to the |head| element.  This is covered by using the
4314          $parse_rcdata->('CDATA', $insert);    ## |$self->{head_element_inserted}| flag.  Another is the case where
4315          return;    ## an element or comment is inserted into the |table| subtree while
4316        } elsif ({    ## foster parenting happens.  This is covered by using the [2] flag
4317                  base => 1, link => 1,    ## of the |$open_tables| structure.  All other cases are handled
4318                 }->{$token->{tag_name}}) {    ## simply by calling |manakai_append_text| method.
4319          ## NOTE: This is an "as if in head" code clone, only "-t" differs  
4320          !!!insert-element-t ($token->{tag_name}, $token->{attributes});    ## TODO: |<body><script>document.write("a<br>");
4321          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.    ## document.body.removeChild (document.body.lastChild);
4322          !!!next-token;    ## document.write ("b")</script>|
4323          return;  
4324        } elsif ($token->{tag_name} eq 'meta') {    B: while (1) {
4325          ## NOTE: This is an "as if in head" code clone, only "-t" differs      if ($token->{type} == DOCTYPE_TOKEN) {
4326          !!!insert-element-t ($token->{tag_name}, $token->{attributes});        !!!cp ('t73');
4327          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4328          ## Ignore the token
4329          ## Stay in the phase
4330          !!!next-token;
4331          next B;
4332        } elsif ($token->{type} == START_TAG_TOKEN and
4333                 $token->{tag_name} eq 'html') {
4334          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4335            !!!cp ('t79');
4336            !!!parse-error (type => 'after html', text => 'html', token => $token);
4337            $self->{insertion_mode} = AFTER_BODY_IM;
4338          } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4339            !!!cp ('t80');
4340            !!!parse-error (type => 'after html', text => 'html', token => $token);
4341            $self->{insertion_mode} = AFTER_FRAMESET_IM;
4342          } else {
4343            !!!cp ('t81');
4344          }
4345    
4346          unless ($self->{confident}) {        !!!cp ('t82');
4347            my $charset;        !!!parse-error (type => 'not first start tag', token => $token);
4348            if ($token->{attributes}->{charset}) { ## TODO: And if supported        my $top_el = $self->{open_elements}->[0]->[0];
4349              $charset = $token->{attributes}->{charset}->{value};        for my $attr_name (keys %{$token->{attributes}}) {
4350            }          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4351            if ($token->{attributes}->{'http-equiv'}) {            !!!cp ('t84');
4352              ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.            $top_el->set_attribute_ns
4353              if ($token->{attributes}->{'http-equiv'}->{value}              (undef, [undef, $attr_name],
4354                  =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=               $token->{attributes}->{$attr_name}->{value});
                     [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|  
                     ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {  
               $charset = defined $1 ? $1 : defined $2 ? $2 : $3;  
             } ## TODO: And if supported  
           }  
           ## TODO: Change the encoding  
4355          }          }
4356          }
4357          !!!next-token;        !!!nack ('t84.1');
4358          return;        !!!next-token;
4359        } elsif ($token->{tag_name} eq 'title') {        next B;
4360          !!!parse-error (type => 'in body:title');      } elsif ($token->{type} == COMMENT_TOKEN) {
4361          ## NOTE: This is an "as if in head" code clone        my $comment = $self->{document}->create_comment ($token->{data});
4362          $parse_rcdata->('RCDATA', sub {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4363            if (defined $self->{head_element}) {          !!!cp ('t85');
4364              $self->{head_element}->append_child ($_[0]);          $self->{document}->append_child ($comment);
4365            } else {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4366              $insert->($_[0]);          !!!cp ('t86');
4367            }          $self->{open_elements}->[0]->[0]->append_child ($comment);
4368          });        } else {
4369          return;          !!!cp ('t87');
4370        } elsif ($token->{tag_name} eq 'body') {          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4371          !!!parse-error (type => 'in body:body');          $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4372                        }
4373          if (@{$self->{open_elements}} == 1 or        !!!next-token;
4374              $self->{open_elements}->[1]->[1] ne 'body') {        next B;
4375            ## Ignore the token      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4376          if ($token->{type} == CHARACTER_TOKEN) {
4377            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4378            delete $self->{ignore_newline};
4379    
4380            if (length $token->{data}) {
4381              !!!cp ('t43');
4382              $self->{open_elements}->[-1]->[0]->manakai_append_text
4383                  ($token->{data});
4384          } else {          } else {
4385            my $body_el = $self->{open_elements}->[1]->[0];            !!!cp ('t43.1');
           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});  
             }  
           }  
4386          }          }
4387          !!!next-token;          !!!next-token;
4388          return;          next B;
4389        } elsif ({        } elsif ($token->{type} == END_TAG_TOKEN) {
4390                  address => 1, blockquote => 1, center => 1, dir => 1,          delete $self->{ignore_newline};
4391                  div => 1, dl => 1, fieldset => 1, listing => 1,  
4392                  menu => 1, ol => 1, p => 1, ul => 1,          if ($token->{tag_name} eq 'script') {
4393                  pre => 1,            !!!cp ('t50');
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
4394                        
4395          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            ## Para 1-2
4396          if ($token->{tag_name} eq 'pre') {            my $script = pop @{$self->{open_elements}};
4397            !!!next-token;            
4398            if ($token->{type} eq 'character') {            ## Para 3
4399              $token->{data} =~ s/^\x0A//;            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4400              unless (length $token->{data}) {  
4401                !!!next-token;            ## Para 4
4402              }            ## TODO: $old_insertion_point = $current_insertion_point;
4403            }            ## TODO: $current_insertion_point = just before $self->{nc};
4404          } else {  
4405            !!!next-token;            ## Para 5
4406          }            ## TODO: Run the $script->[0].
4407          return;  
4408        } elsif ($token->{tag_name} eq 'form') {            ## Para 6
4409          if (defined $self->{form_element}) {            ## TODO: $current_insertion_point = $old_insertion_point;
4410            !!!parse-error (type => 'in form:form');  
4411            ## Ignore the token            ## Para 7
4412              ## TODO: if ($pending_external_script) {
4413                ## TODO: ...
4414              ## TODO: }
4415    
4416            !!!next-token;            !!!next-token;
4417            return;            next B;
4418          } else {          } else {
4419            ## has a p element in scope            !!!cp ('t42');
4420            INSCOPE: for (reverse @{$self->{open_elements}}) {  
4421              if ($_->[1] eq 'p') {            pop @{$self->{open_elements}};
4422                !!!back-token;  
4423                $token = {type => 'end tag', tag_name => 'p'};            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
               return;  
             } elsif ({  
                       table => 1, caption => 1, td => 1, th => 1,  
                       button => 1, marquee => 1, object => 1, html => 1,  
                      }->{$_->[1]}) {  
               last INSCOPE;  
             }  
           } # INSCOPE  
               
           !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           $self->{form_element} = $self->{open_elements}->[-1]->[0];  
4424            !!!next-token;            !!!next-token;
4425            return;            next B;
4426          }          }
4427        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4428          ## has a p element in scope          delete $self->{ignore_newline};
         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 'li') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'dd' or $token->{tag_name} eq 'dt') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## Step 1  
         my $i = -1;  
         my $node = $self->{open_elements}->[$i];  
         LI: {  
           ## Step 2  
           if ($node->[1] eq 'dt' or $node->[1] eq 'dd') {  
             if ($i != -1) {  
               !!!parse-error (type => 'end tag missing:'.  
                               $self->{open_elements}->[-1]->[1]);  
             }  
             splice @{$self->{open_elements}}, $i;  
             last LI;  
           }  
             
           ## Step 3  
           if (not $formatting_category->{$node->[1]} and  
               #not $phrasing_category->{$node->[1]} and  
               ($special_category->{$node->[1]} or  
                $scoping_category->{$node->[1]}) and  
               $node->[1] ne 'address' and $node->[1] ne 'div') {  
             last LI;  
           }  
             
           ## Step 4  
           $i--;  
           $node = $self->{open_elements}->[$i];  
           redo LI;  
         } # LI  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'plaintext') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         $self->{content_model_flag} = 'PLAINTEXT';  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
         #}  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'a') {  
         AFE: for my $i (reverse 0..$#$active_formatting_elements) {  
           my $node = $active_formatting_elements->[$i];  
           if ($node->[1] eq 'a') {  
             !!!parse-error (type => 'in a:a');  
               
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'a'};  
             $formatting_end_tag->($token->{tag_name});  
               
             AFE2: for (reverse 0..$#$active_formatting_elements) {  
               if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {  
                 splice @$active_formatting_elements, $_, 1;  
                 last AFE2;  
               }  
             } # AFE2  
             OE: for (reverse 0..$#{$self->{open_elements}}) {  
               if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {  
                 splice @{$self->{open_elements}}, $_, 1;  
                 last OE;  
               }  
             } # OE  
             last AFE;  
           } elsif ($node->[0] eq '#marker') {  
             last AFE;  
           }  
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4429    
4430          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          !!!cp ('t44');
4431          push @$active_formatting_elements, $self->{open_elements}->[-1];          !!!parse-error (type => 'not closed',
4432                            text => $self->{open_elements}->[-1]->[0]
4433                                ->manakai_local_name,
4434                            token => $token);
4435    
4436          !!!next-token;          #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
4437          return;          #  ## TODO: Mark as "already executed"
4438        } 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);  
4439    
4440          ## has a |nobr| element in scope          pop @{$self->{open_elements}};
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'nobr') {  
             !!!parse-error (type => 'not closed:nobr');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'nobr'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
4441    
4442            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4443            ## Reprocess.
4444            next B;
4445          } else {
4446            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4447          }
4448        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4449          if ($token->{type} == CHARACTER_TOKEN) {
4450            !!!cp ('t87.1');
4451            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4452          !!!next-token;          !!!next-token;
4453          return;          next B;
4454        } elsif ($token->{tag_name} eq 'marquee' or        } elsif ($token->{type} == START_TAG_TOKEN) {
4455                 $token->{tag_name} eq 'object') {          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4456          $reconstruct_active_formatting_elements->($insert_to_current);               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4457                        not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4458          !!!insert-element-t ($token->{tag_name}, $token->{attributes});              ($token->{tag_name} eq 'svg' and
4459          push @$active_formatting_elements, ['#marker', ''];               $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
4460                      ## NOTE: "using the rules for secondary insertion mode"then"continue"
4461          !!!next-token;            !!!cp ('t87.2');
4462          return;            #
4463        } elsif ($token->{tag_name} eq 'xmp') {          } elsif ({
4464          $reconstruct_active_formatting_elements->($insert_to_current);                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4465          $parse_rcdata->('CDATA', $insert);                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4466          return;                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4467        } elsif ($token->{tag_name} eq 'table') {                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4468          ## has a p element in scope                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4469          INSCOPE: for (reverse @{$self->{open_elements}}) {                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4470            if ($_->[1] eq 'p') {                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4471              !!!back-token;                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4472              $token = {type => 'end tag', tag_name => 'p'};                   }->{$token->{tag_name}}) {
4473              return;            !!!cp ('t87.2');
4474            } elsif ({            !!!parse-error (type => 'not closed',
4475                      table => 1, caption => 1, td => 1, th => 1,                            text => $self->{open_elements}->[-1]->[0]
4476                      button => 1, marquee => 1, object => 1, html => 1,                                ->manakai_local_name,
4477                     }->{$_->[1]}) {                            token => $token);
4478              last INSCOPE;  
4479              pop @{$self->{open_elements}}
4480                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4481    
4482              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4483              ## Reprocess.
4484              next B;
4485            } else {
4486              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4487              my $tag_name = $token->{tag_name};
4488              if ($nsuri eq $SVG_NS) {
4489                $tag_name = {
4490                   altglyph => 'altGlyph',
4491                   altglyphdef => 'altGlyphDef',
4492                   altglyphitem => 'altGlyphItem',
4493                   animatecolor => 'animateColor',
4494                   animatemotion => 'animateMotion',
4495                   animatetransform => 'animateTransform',
4496                   clippath => 'clipPath',
4497                   feblend => 'feBlend',
4498                   fecolormatrix => 'feColorMatrix',
4499                   fecomponenttransfer => 'feComponentTransfer',
4500                   fecomposite => 'feComposite',
4501                   feconvolvematrix => 'feConvolveMatrix',
4502                   fediffuselighting => 'feDiffuseLighting',
4503                   fedisplacementmap => 'feDisplacementMap',
4504                   fedistantlight => 'feDistantLight',
4505                   feflood => 'feFlood',
4506                   fefunca => 'feFuncA',
4507                   fefuncb => 'feFuncB',
4508                   fefuncg => 'feFuncG',
4509                   fefuncr => 'feFuncR',
4510                   fegaussianblur => 'feGaussianBlur',
4511                   feimage => 'feImage',
4512                   femerge => 'feMerge',
4513                   femergenode => 'feMergeNode',
4514                   femorphology => 'feMorphology',
4515                   feoffset => 'feOffset',
4516                   fepointlight => 'fePointLight',
4517                   fespecularlighting => 'feSpecularLighting',
4518                   fespotlight => 'feSpotLight',
4519                   fetile => 'feTile',
4520                   feturbulence => 'feTurbulence',
4521                   foreignobject => 'foreignObject',
4522                   glyphref => 'glyphRef',
4523                   lineargradient => 'linearGradient',
4524                   radialgradient => 'radialGradient',
4525                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4526                   textpath => 'textPath',  
4527                }->{$tag_name} || $tag_name;
4528            }            }
         } # 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';  
         }  
4529    
4530          ## NOTE: There is an "as if <br>" code clone.            ## "adjust SVG attributes" (SVG only) - done in insert-element-f
4531          $reconstruct_active_formatting_elements->($insert_to_current);  
4532                      ## "adjust foreign attributes" - done in insert-element-f
4533          !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
4534          pop @{$self->{open_elements}};            !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
4535            
4536          !!!next-token;            if ($self->{self_closing}) {
4537          return;              pop @{$self->{open_elements}};
4538        } elsif ($token->{tag_name} eq 'hr') {              !!!ack ('t87.3');
         ## 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}};  
4539            } else {            } else {
4540              push @tokens, {type => 'character',              !!!cp ('t87.4');
                            data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD  
             ## TODO: make this configurable  
4541            }            }
4542            push @tokens,  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'};  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'textarea') {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         ## TODO: $self->{form_element} if defined  
         $self->{content_model_flag} = 'RCDATA';  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         if ($token->{type} eq 'character') {  
           $token->{data} =~ s/^\x0A//;  
           unless (length $token->{data}) {  
             !!!next-token;  
           }  
         }  
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
4543            !!!next-token;            !!!next-token;
4544              next B;
4545          }          }
4546          if (length $text) {        } elsif ($token->{type} == END_TAG_TOKEN) {
4547            $el->manakai_append_text ($text);          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4548          }          !!!cp ('t87.5');
4549                    #
4550          $self->{content_model_flag} = 'PCDATA';        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4551                    !!!cp ('t87.6');
4552          if ($token->{type} eq 'end tag' and          !!!parse-error (type => 'not closed',
4553              $token->{tag_name} eq $tag_name) {                          text => $self->{open_elements}->[-1]->[0]
4554            ## Ignore the token                              ->manakai_local_name,
4555          } else {                          token => $token);
4556            !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
4557          }          pop @{$self->{open_elements}}
4558          !!!next-token;              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4559          return;  
4560        } elsif ({          ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4561                  iframe => 1,  
4562                  noembed => 1,          $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4563                  noframes => 1,          ## Reprocess.
4564                  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.  
4565        } else {        } else {
4566          $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;  
4567        }        }
4568      } elsif ($token->{type} eq 'end tag') {      }
       if ($token->{tag_name} eq 'body') {  
         if (@{$self->{open_elements}} > 1 and  
             $self->{open_elements}->[1]->[1] eq 'body') {  
           for (@{$self->{open_elements}}) {  
             unless ({  
                        dd => 1, dt => 1, li => 1, p => 1, td => 1,  
                        th => 1, tr => 1, body => 1, html => 1,  
                      tbody => 1, tfoot => 1, thead => 1,  
                     }->{$_->[1]}) {  
               !!!parse-error (type => 'not closed:'.$_->[1]);  
             }  
           }  
4569    
4570            $self->{insertion_mode} = 'after body';      if ($self->{insertion_mode} & HEAD_IMS) {
4571            !!!next-token;        if ($token->{type} == CHARACTER_TOKEN) {
4572            return;          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4573          } else {            unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4574            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              if ($self->{head_element_inserted}) {
4575            ## Ignore the token                !!!cp ('t88.3');
4576            !!!next-token;                $self->{open_elements}->[-1]->[0]->append_child
4577            return;                  ($self->{document}->create_text_node ($1));
4578          }                delete $self->{head_element_inserted};
4579        } elsif ($token->{tag_name} eq 'html') {                ## NOTE: |</head> <link> |
4580          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {                #
4581            ## ISSUE: There is an issue in the spec.              } else {
4582            if ($self->{open_elements}->[-1]->[1] ne 'body') {                !!!cp ('t88.2');
4583              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4584            }                ## NOTE: |</head> &#x20;|
4585            $self->{insertion_mode} = 'after body';                #
           ## reprocess  
           return;  
         } else {  
           !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
           ## Ignore the token  
           !!!next-token;  
           return;  
         }  
       } elsif ({  
                 address => 1, blockquote => 1, center => 1, dir => 1,  
                 div => 1, dl => 1, fieldset => 1, listing => 1,  
                 menu => 1, ol => 1, pre => 1, ul => 1,  
                 p => 1,  
                 dd => 1, dt => 1, li => 1,  
                 button => 1, marquee => 1, object => 1,  
                }->{$token->{tag_name}}) {  
         ## has an element in scope  
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => ($token->{tag_name} ne 'dd'),  
                  dt => ($token->{tag_name} ne 'dt'),  
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
4586              }              }
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           if (defined $i) {  
             !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4587            } else {            } else {
4588              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t88.1');
4589                ## Ignore the token.
4590                #
4591            }            }
4592          }            unless (length $token->{data}) {
4593                        !!!cp ('t88');
4594          if (defined $i) {              !!!next-token;
4595            splice @{$self->{open_elements}}, $i;              next B;
         } elsif ($token->{tag_name} eq 'p') {  
           ## As if <p>, then reprocess the current token  
           my $el;  
           !!!create-element ($el, 'p');  
           $insert->($el);  
         }  
         $clear_up_to_marker->()  
           if {  
             button => 1, marquee => 1, object => 1,  
           }->{$token->{tag_name}};  
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'form') {  
         ## has an element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq $token->{tag_name}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4596            }            }
4597          } # INSCOPE  ## TODO: set $token->{column} appropriately
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
           pop @{$self->{open_elements}};  
         } else {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4598          }          }
4599    
4600          undef $self->{form_element};          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4601          !!!next-token;            !!!cp ('t89');
4602          return;            ## As if <head>
4603        } elsif ({            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4604                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4605                 }->{$token->{tag_name}}) {            push @{$self->{open_elements}},
4606          ## has an element in scope                [$self->{head_element}, $el_category->{head}];
         my $i;  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ({  
                h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
               }->{$node->[1]}) {  
             ## generate implied end tags  
             if ({  
                  dd => 1, dt => 1, li => 1, p => 1,  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
           
         splice @{$self->{open_elements}}, $i if defined $i;  
         !!!next-token;  
         return;  
       } elsif ({  
                 a => 1,  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 nobr => 1, s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ($token->{tag_name} eq 'br') {  
         !!!parse-error (type => 'unmatched end tag:br');  
4607    
4608          ## As if <br>            ## Reprocess in the "in head" insertion mode...
4609          $reconstruct_active_formatting_elements->($insert_to_current);            pop @{$self->{open_elements}};
           
         my $el;  
         !!!create-element ($el, 'br');  
         $insert->($el);  
           
         ## Ignore the token.  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                 area => 1, basefont => 1, bgsound => 1,  
                 embed => 1, hr => 1, iframe => 1, image => 1,  
                 img => 1, input => 1, isindex => 1, noembed => 1,  
                 noframes => 1, param => 1, select => 1, spacer => 1,  
                 table => 1, textarea => 1, wbr => 1,  
                 noscript => 0, ## TODO: if scripting is enabled  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: Issue on HTML5 new elements in spec  
           
       } else {  
         ## Step 1  
         my $node_i = -1;  
         my $node = $self->{open_elements}->[$node_i];  
4610    
4611          ## Step 2            ## Reprocess in the "after head" insertion mode...
4612          S2: {          } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4613            if ($node->[1] eq $token->{tag_name}) {            !!!cp ('t90');
4614              ## Step 1            ## As if </noscript>
4615              ## generate implied end tags            pop @{$self->{open_elements}};
4616              if ({            !!!parse-error (type => 'in noscript:#text', token => $token);
4617                   dd => 1, dt => 1, li => 1, p => 1,            
4618                   td => 1, th => 1, tr => 1,            ## Reprocess in the "in head" insertion mode...
4619                   tbody => 1, tfoot=> 1, thead => 1,            ## As if </head>
4620                  }->{$self->{open_elements}->[-1]->[1]}) {            pop @{$self->{open_elements}};
               !!!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;  
4621    
4622              ## Reprocess in the "after head" insertion mode...
4623            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4624              !!!cp ('t91');
4625              pop @{$self->{open_elements}};
4626    
4627              ## Reprocess in the "after head" insertion mode...
4628            } else {
4629              !!!cp ('t92');
4630            }
4631    
4632            ## "after head" insertion mode
4633            ## As if <body>
4634            !!!insert-element ('body',, $token);
4635            $self->{insertion_mode} = IN_BODY_IM;
4636            ## reprocess
4637            next B;
4638          } elsif ($token->{type} == START_TAG_TOKEN) {
4639            if ($token->{tag_name} eq 'head') {
4640              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4641                !!!cp ('t93');
4642                !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4643                $self->{open_elements}->[-1]->[0]->append_child
4644                    ($self->{head_element});
4645                push @{$self->{open_elements}},
4646                    [$self->{head_element}, $el_category->{head}];
4647                $self->{insertion_mode} = IN_HEAD_IM;
4648                !!!nack ('t93.1');
4649              !!!next-token;              !!!next-token;
4650              last S2;              next B;
4651              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4652                !!!cp ('t93.2');
4653                !!!parse-error (type => 'after head', text => 'head',
4654                                token => $token);
4655                ## Ignore the token
4656                !!!nack ('t93.3');
4657                !!!next-token;
4658                next B;
4659            } else {            } else {
4660              ## Step 3              !!!cp ('t95');
4661              if (not $formatting_category->{$node->[1]} and              !!!parse-error (type => 'in head:head',
4662                  #not $phrasing_category->{$node->[1]} and                              token => $token); # or in head noscript
4663                  ($special_category->{$node->[1]} or              ## Ignore the token
4664                   $scoping_category->{$node->[1]})) {              !!!nack ('t95.1');
4665                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!next-token;
4666                ## Ignore the token              next B;
               !!!next-token;  
               last S2;  
             }  
4667            }            }
4668                      } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4669            ## Step 4            !!!cp ('t96');
4670            $node_i--;            ## As if <head>
4671            $node = $self->{open_elements}->[$node_i];            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4672                        $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4673            ## Step 5;            push @{$self->{open_elements}},
4674            redo S2;                [$self->{head_element}, $el_category->{head}];
         } # S2  
         return;  
       }  
     }  
   }; # $in_body  
4675    
4676    B: {            $self->{insertion_mode} = IN_HEAD_IM;
4677      if ($self->{insertion_mode} ne 'trailing end') {            ## Reprocess in the "in head" insertion mode...
4678        if ($token->{type} eq 'DOCTYPE') {          } else {
4679          !!!parse-error (type => 'in html:#DOCTYPE');            !!!cp ('t97');
         ## Ignore the token  
         ## Stay in the phase  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'start tag' and  
                $token->{tag_name} eq 'html') {  
 ## ISSUE: "aa<html>" is not a parse error.  
 ## ISSUE: "<html>" in fragment is not a parse error.  
         unless ($token->{first_start_tag}) {  
           !!!parse-error (type => 'not first start tag');  
         }  
         my $top_el = $self->{open_elements}->[0]->[0];  
         for my $attr_name (keys %{$token->{attributes}}) {  
           unless ($top_el->has_attribute_ns (undef, $attr_name)) {  
             $top_el->set_attribute_ns  
               (undef, [undef, $attr_name],  
                $token->{attributes}->{$attr_name}->{value});  
           }  
         }  
         !!!next-token;  
         redo B;  
       } elsif ($token->{type} eq 'end-of-file') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
              tbody => 1, tfoot=> 1, thead => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
4680          }          }
4681    
4682          ## Stop parsing          if ($token->{tag_name} eq 'base') {
4683          last B;            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4684                !!!cp ('t98');
4685                ## As if </noscript>
4686                pop @{$self->{open_elements}};
4687                !!!parse-error (type => 'in noscript', text => 'base',
4688                                token => $token);
4689              
4690                $self->{insertion_mode} = IN_HEAD_IM;
4691                ## Reprocess in the "in head" insertion mode...
4692              } else {
4693                !!!cp ('t99');
4694              }
4695    
4696          ## ISSUE: There is an issue in the spec.            ## NOTE: There is a "as if in head" code clone.
4697        } else {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4698          if ($self->{insertion_mode} eq 'before head') {              !!!cp ('t100');
4699            if ($token->{type} eq 'character') {              !!!parse-error (type => 'after head',
4700              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                              text => $token->{tag_name}, token => $token);
4701                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);              push @{$self->{open_elements}},
4702                unless (length $token->{data}) {                  [$self->{head_element}, $el_category->{head}];
4703                  !!!next-token;              $self->{head_element_inserted} = 1;
                 redo B;  
               }  
             }  
             ## 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;  
           } elsif ($token->{type} eq 'comment') {  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             my $attr = $token->{tag_name} eq 'head' ? $token->{attributes} : {};  
             !!!create-element ($self->{head_element}, 'head', $attr);  
             $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
             push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
             $self->{insertion_mode} = 'in head';  
             if ($token->{tag_name} eq 'head') {  
               !!!next-token;  
             #} elsif ({  
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
             } else {  
               ## reprocess  
             }  
             redo B;  
           } elsif ($token->{type} eq 'end tag') {  
             if ({  
                  head => 1, body => 1, html => 1,  
                  p => 1, br => 1,  
                 }->{$token->{tag_name}}) {  
               ## As if <head>  
               !!!create-element ($self->{head_element}, 'head');  
               $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});  
               push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               $self->{insertion_mode} = 'in head';  
               ## reprocess  
               redo B;  
             } else {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token ## ISSUE: An issue in the spec.  
               !!!next-token;  
               redo B;  
             }  
4704            } else {            } else {
4705              die "$0: $token->{type}: Unknown type";              !!!cp ('t101');
4706            }            }
4707          } elsif ($self->{insertion_mode} eq 'in head' or            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4708                   $self->{insertion_mode} eq 'in head noscript' or            pop @{$self->{open_elements}};
4709                   $self->{insertion_mode} eq 'after head') {            pop @{$self->{open_elements}} # <head>
4710            if ($token->{type} eq 'character') {                if $self->{insertion_mode} == AFTER_HEAD_IM;
4711              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            !!!nack ('t101.1');
4712                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            !!!next-token;
4713                unless (length $token->{data}) {            next B;
4714                  !!!next-token;          } elsif ($token->{tag_name} eq 'link') {
4715                  redo B;            ## NOTE: There is a "as if in head" code clone.
4716                }            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4717              }              !!!cp ('t102');
4718                            !!!parse-error (type => 'after head',
4719              #                              text => $token->{tag_name}, token => $token);
4720            } elsif ($token->{type} eq 'comment') {              push @{$self->{open_elements}},
4721              my $comment = $self->{document}->create_comment ($token->{data});                  [$self->{head_element}, $el_category->{head}];
4722              $self->{open_elements}->[-1]->[0]->append_child ($comment);              $self->{head_element_inserted} = 1;
4723              } else {
4724                !!!cp ('t103');
4725              }
4726              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4727              pop @{$self->{open_elements}};
4728              pop @{$self->{open_elements}} # <head>
4729                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4730              !!!ack ('t103.1');
4731              !!!next-token;
4732              next B;
4733            } elsif ($token->{tag_name} eq 'command' or
4734                     $token->{tag_name} eq 'eventsource') {
4735              if ($self->{insertion_mode} == IN_HEAD_IM) {
4736                ## NOTE: If the insertion mode at the time of the emission
4737                ## of the token was "before head", $self->{insertion_mode}
4738                ## is already changed to |IN_HEAD_IM|.
4739    
4740                ## NOTE: There is a "as if in head" code clone.
4741                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4742                pop @{$self->{open_elements}};
4743                pop @{$self->{open_elements}} # <head>
4744                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4745                !!!ack ('t103.2');
4746              !!!next-token;              !!!next-token;
4747              redo B;              next B;
4748            } elsif ($token->{type} eq 'start tag') {            } else {
4749              if ({base => ($self->{insertion_mode} eq 'in head' or              ## NOTE: "in head noscript" or "after head" insertion mode
4750                            $self->{insertion_mode} eq 'after head'),              ## - in these cases, these tags are treated as same as
4751                   link => 1}->{$token->{tag_name}}) {              ## normal in-body tags.
4752                ## NOTE: There is a "as if in head" code clone.              !!!cp ('t103.3');
4753                if ($self->{insertion_mode} eq 'after head') {              #
4754                  !!!parse-error (type => 'after head:'.$token->{tag_name});            }
4755                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];          } elsif ($token->{tag_name} eq 'meta') {
4756                }            ## NOTE: There is a "as if in head" code clone.
4757                !!!insert-element ($token->{tag_name}, $token->{attributes});            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4758                pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.              !!!cp ('t104');
4759                pop @{$self->{open_elements}}              !!!parse-error (type => 'after head',
4760                    if $self->{insertion_mode} eq 'after head';                              text => $token->{tag_name}, token => $token);
4761                !!!next-token;              push @{$self->{open_elements}},
4762                redo B;                  [$self->{head_element}, $el_category->{head}];
4763              } elsif ($token->{tag_name} eq 'meta') {              $self->{head_element_inserted} = 1;
4764                ## NOTE: There is a "as if in head" code clone.            } else {
4765                if ($self->{insertion_mode} eq 'after head') {              !!!cp ('t105');
4766                  !!!parse-error (type => 'after head:'.$token->{tag_name});            }
4767                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4768                }            my $meta_el = pop @{$self->{open_elements}};
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
4769    
4770                unless ($self->{confident}) {                unless ($self->{confident}) {
4771                  my $charset;                  if ($token->{attributes}->{charset}) {
4772                  if ($token->{attributes}->{charset}) { ## TODO: And if supported                    !!!cp ('t106');
4773                    $charset = $token->{attributes}->{charset}->{value};                    ## NOTE: Whether the encoding is supported or not is handled
4774                      ## in the {change_encoding} callback.
4775                      $self->{change_encoding}
4776                          ->($self, $token->{attributes}->{charset}->{value},
4777                             $token);
4778                      
4779                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4780                          ->set_user_data (manakai_has_reference =>
4781                                               $token->{attributes}->{charset}
4782                                                   ->{has_reference});
4783                    } elsif ($token->{attributes}->{content}) {
4784                      if ($token->{attributes}->{content}->{value}
4785                          =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4786                              [\x09\x0A\x0C\x0D\x20]*=
4787                              [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4788                              ([^"'\x09\x0A\x0C\x0D\x20]
4789                               [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4790                        !!!cp ('t107');
4791                        ## NOTE: Whether the encoding is supported or not is handled
4792                        ## in the {change_encoding} callback.
4793                        $self->{change_encoding}
4794                            ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4795                               $token);
4796                        $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4797                            ->set_user_data (manakai_has_reference =>
4798                                                 $token->{attributes}->{content}
4799                                                       ->{has_reference});
4800                      } else {
4801                        !!!cp ('t108');
4802                      }
4803                    }
4804                  } else {
4805                    if ($token->{attributes}->{charset}) {
4806                      !!!cp ('t109');
4807                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4808                          ->set_user_data (manakai_has_reference =>
4809                                               $token->{attributes}->{charset}
4810                                                   ->{has_reference});
4811                  }                  }
4812                  if ($token->{attributes}->{'http-equiv'}) {                  if ($token->{attributes}->{content}) {
4813                    ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.                    !!!cp ('t110');
4814                    if ($token->{attributes}->{'http-equiv'}->{value}                    $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4815                        =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=                        ->set_user_data (manakai_has_reference =>
4816                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                                             $token->{attributes}->{content}
4817                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                                                 ->{has_reference});
                     $charset = defined $1 ? $1 : defined $2 ? $2 : $3;  
                   } ## TODO: And if supported  
4818                  }                  }
                 ## TODO: Change the encoding  
4819                }                }
4820    
4821                ## TODO: Extracting |charset| from |meta|.                pop @{$self->{open_elements}} # <head>
4822                pop @{$self->{open_elements}}                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4823                    if $self->{insertion_mode} eq 'after head';                !!!ack ('t110.1');
4824                !!!next-token;                !!!next-token;
4825                redo B;                next B;
4826              } elsif ($token->{tag_name} eq 'title' and          } elsif ($token->{tag_name} eq 'title') {
4827                       $self->{insertion_mode} eq 'in head') {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4828                ## NOTE: There is a "as if in head" code clone.              !!!cp ('t111');
4829                if ($self->{insertion_mode} eq 'after head') {              ## As if </noscript>
4830                  !!!parse-error (type => 'after head:'.$token->{tag_name});              pop @{$self->{open_elements}};
4831                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];              !!!parse-error (type => 'in noscript', text => 'title',
4832                }                              token => $token);
4833                my $parent = defined $self->{head_element} ? $self->{head_element}            
4834                    : $self->{open_elements}->[-1]->[0];              $self->{insertion_mode} = IN_HEAD_IM;
4835                $parse_rcdata->('RCDATA', sub { $parent->append_child ($_[0]) });              ## Reprocess in the "in head" insertion mode...
4836                pop @{$self->{open_elements}}            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4837                    if $self->{insertion_mode} eq 'after head';              !!!cp ('t112');
4838                redo B;              !!!parse-error (type => 'after head',
4839              } elsif ($token->{tag_name} eq 'style') {                              text => $token->{tag_name}, token => $token);
4840                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and              push @{$self->{open_elements}},
4841                ## insertion mode 'in head')                  [$self->{head_element}, $el_category->{head}];
4842                ## NOTE: There is a "as if in head" code clone.              $self->{head_element_inserted} = 1;
4843                if ($self->{insertion_mode} eq 'after head') {            } else {
4844                  !!!parse-error (type => 'after head:'.$token->{tag_name});              !!!cp ('t113');
4845                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            }
4846                }  
4847                $parse_rcdata->('CDATA', $insert_to_current);            ## NOTE: There is a "as if in head" code clone.
4848                pop @{$self->{open_elements}}            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4849                    if $self->{insertion_mode} eq 'after head';            ## ISSUE: A spec bug [Bug 6038]
4850                redo B;            splice @{$self->{open_elements}}, -2, 1, () # <head>
4851              } elsif ($token->{tag_name} eq 'noscript') {                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4852                if ($self->{insertion_mode} eq 'in head') {            next B;
4853            } elsif ($token->{tag_name} eq 'style' or
4854                     $token->{tag_name} eq 'noframes') {
4855              ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4856              ## insertion mode IN_HEAD_IM)
4857              ## NOTE: There is a "as if in head" code clone.
4858              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4859                !!!cp ('t114');
4860                !!!parse-error (type => 'after head',
4861                                text => $token->{tag_name}, token => $token);
4862                push @{$self->{open_elements}},
4863                    [$self->{head_element}, $el_category->{head}];
4864                $self->{head_element_inserted} = 1;
4865              } else {
4866                !!!cp ('t115');
4867              }
4868              $parse_rcdata->(CDATA_CONTENT_MODEL);
4869              ## ISSUE: A spec bug [Bug 6038]
4870              splice @{$self->{open_elements}}, -2, 1, () # <head>
4871                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4872              next B;
4873            } elsif ($token->{tag_name} eq 'noscript') {
4874                  if ($self->{insertion_mode} == IN_HEAD_IM) {
4875                    !!!cp ('t116');
4876                  ## NOTE: and scripting is disalbed                  ## NOTE: and scripting is disalbed
4877                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4878                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4879                    !!!nack ('t116.1');
4880                  !!!next-token;                  !!!next-token;
4881                  redo B;                  next B;
4882                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4883                  !!!parse-error (type => 'in noscript:noscript');                  !!!cp ('t117');
4884                    !!!parse-error (type => 'in noscript', text => 'noscript',
4885                                    token => $token);
4886                  ## Ignore the token                  ## Ignore the token
4887                  redo B;                  !!!nack ('t117.1');
4888                    !!!next-token;
4889                    next B;
4890                } else {                } else {
4891                    !!!cp ('t118');
4892                  #                  #
4893                }                }
4894              } elsif ($token->{tag_name} eq 'head' and          } elsif ($token->{tag_name} eq 'script') {
4895                       $self->{insertion_mode} ne 'after head') {            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4896                !!!parse-error (type => 'in head:head'); # or in head noscript              !!!cp ('t119');
4897                ## Ignore the token              ## As if </noscript>
4898                !!!next-token;              pop @{$self->{open_elements}};
4899                redo B;              !!!parse-error (type => 'in noscript', text => 'script',
4900              } elsif ($self->{insertion_mode} ne 'in head noscript' and                              token => $token);
4901                       $token->{tag_name} eq 'script') {            
4902                if ($self->{insertion_mode} eq 'after head') {              $self->{insertion_mode} = IN_HEAD_IM;
4903                  !!!parse-error (type => 'after head:'.$token->{tag_name});              ## Reprocess in the "in head" insertion mode...
4904                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4905                !!!cp ('t120');
4906                !!!parse-error (type => 'after head',
4907                                text => $token->{tag_name}, token => $token);
4908                push @{$self->{open_elements}},
4909                    [$self->{head_element}, $el_category->{head}];
4910                $self->{head_element_inserted} = 1;
4911              } else {
4912                !!!cp ('t121');
4913              }
4914    
4915              ## NOTE: There is a "as if in head" code clone.
4916              $script_start_tag->();
4917              ## ISSUE: A spec bug  [Bug 6038]
4918              splice @{$self->{open_elements}}, -2, 1 # <head>
4919                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4920              next B;
4921            } elsif ($token->{tag_name} eq 'body' or
4922                     $token->{tag_name} eq 'frameset') {
4923                  if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4924                    !!!cp ('t122');
4925                    ## As if </noscript>
4926                    pop @{$self->{open_elements}};
4927                    !!!parse-error (type => 'in noscript',
4928                                    text => $token->{tag_name}, token => $token);
4929                    
4930                    ## Reprocess in the "in head" insertion mode...
4931                    ## As if </head>
4932                    pop @{$self->{open_elements}};
4933                    
4934                    ## Reprocess in the "after head" insertion mode...
4935                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4936                    !!!cp ('t124');
4937                    pop @{$self->{open_elements}};
4938                    
4939                    ## Reprocess in the "after head" insertion mode...
4940                  } else {
4941                    !!!cp ('t125');
4942                }                }
4943                ## NOTE: There is a "as if in head" code clone.  
4944                $script_start_tag->($insert_to_current);                ## "after head" insertion mode
4945                pop @{$self->{open_elements}}                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4946                    if $self->{insertion_mode} eq 'after head';                if ($token->{tag_name} eq 'body') {
4947                redo B;                  !!!cp ('t126');
4948              } elsif ($self->{insertion_mode} eq 'after head' and                  $self->{insertion_mode} = IN_BODY_IM;
4949                       $token->{tag_name} eq 'body') {                } elsif ($token->{tag_name} eq 'frameset') {
4950                !!!insert-element ('body', $token->{attributes});                  !!!cp ('t127');
4951                $self->{insertion_mode} = 'in body';                  $self->{insertion_mode} = IN_FRAMESET_IM;
4952                !!!next-token;                } else {
4953                redo B;                  die "$0: tag name: $self->{tag_name}";
4954              } elsif ($self->{insertion_mode} eq 'after head' and                }
4955                       $token->{tag_name} eq 'frameset') {                !!!nack ('t127.1');
               !!!insert-element ('frameset', $token->{attributes});  
               $self->{insertion_mode} = 'in frameset';  
4956                !!!next-token;                !!!next-token;
4957                redo B;                next B;
4958              } else {              } else {
4959                  !!!cp ('t128');
4960                #                #
4961              }              }
4962            } elsif ($token->{type} eq 'end tag') {  
4963              if ($self->{insertion_mode} eq 'in head' and              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4964                  $token->{tag_name} eq 'head') {                !!!cp ('t129');
4965                  ## As if </noscript>
4966                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
4967                $self->{insertion_mode} = 'after head';                !!!parse-error (type => 'in noscript:/',
4968                !!!next-token;                                text => $token->{tag_name}, token => $token);
4969                redo B;                
4970              } elsif ($self->{insertion_mode} eq 'in head noscript' and                ## Reprocess in the "in head" insertion mode...
4971                  $token->{tag_name} eq 'noscript') {                ## As if </head>
4972                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               $self->{insertion_mode} = 'in head';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'in head' and  
                      {  
                       body => 1, html => 1,  
                       p => 1, br => 1,  
                      }->{$token->{tag_name}}) {  
               #  
             } elsif ($self->{insertion_mode} eq 'in head noscript' and  
                      {  
                       p => 1, br => 1,  
                      }->{$token->{tag_name}}) {  
               #  
             } elsif ($self->{insertion_mode} ne 'after head') {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
             }  
           } else {  
             #  
           }  
4973    
4974            ## As if </head> or </noscript> or <body>                ## Reprocess in the "after head" insertion mode...
4975            if ($self->{insertion_mode} eq 'in head') {              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4976              pop @{$self->{open_elements}};                !!!cp ('t130');
4977              $self->{insertion_mode} = 'after head';                ## As if </head>
4978            } elsif ($self->{insertion_mode} eq 'in head noscript') {                pop @{$self->{open_elements}};
             pop @{$self->{open_elements}};  
             !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));  
             $self->{insertion_mode} = 'in head';  
           } else { # 'after head'  
             !!!insert-element ('body');  
             $self->{insertion_mode} = 'in body';  
           }  
           ## reprocess  
           redo B;  
   
           ## ISSUE: An issue in the spec.  
         } elsif ($self->{insertion_mode} eq 'in body') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: There is a code clone of "character in body".  
             $reconstruct_active_formatting_elements->($insert_to_current);  
               
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
4979    
4980              !!!next-token;                ## Reprocess in the "after head" insertion mode...
4981              redo B;              } else {
4982            } 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;  
               }  
4983              }              }
4984    
4985              !!!parse-error (type => 'in table:#character');              ## "after head" insertion mode
4986                ## As if <body>
4987                !!!insert-element ('body',, $token);
4988                $self->{insertion_mode} = IN_BODY_IM;
4989                ## reprocess
4990                !!!ack-later;
4991                next B;
4992              } elsif ($token->{type} == END_TAG_TOKEN) {
4993                if ($token->{tag_name} eq 'head') {
4994                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4995                    !!!cp ('t132');
4996                    ## As if <head>
4997                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4998                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4999                    push @{$self->{open_elements}},
5000                        [$self->{head_element}, $el_category->{head}];
5001    
5002              ## As if in body, but insert into foster parent element                  ## Reprocess in the "in head" insertion mode...
5003              ## ISSUE: Spec says that "whenever a node would be inserted                  pop @{$self->{open_elements}};
5004              ## into the current node" while characters might not be                  $self->{insertion_mode} = AFTER_HEAD_IM;
5005              ## result in a new Text node.                  !!!next-token;
5006              $reconstruct_active_formatting_elements->($insert_to_foster);                  next B;
5007                              } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5008              if ({                  !!!cp ('t133');
5009                   table => 1, tbody => 1, tfoot => 1,                  ## As if </noscript>
5010                   thead => 1, tr => 1,                  pop @{$self->{open_elements}};
5011                  }->{$self->{open_elements}->[-1]->[1]}) {                  !!!parse-error (type => 'in noscript:/',
5012                # MUST                                  text => 'head', token => $token);
5013                my $foster_parent_element;                  
5014                my $next_sibling;                  ## Reprocess in the "in head" insertion mode...
5015                my $prev_sibling;                  pop @{$self->{open_elements}};
5016                OE: for (reverse 0..$#{$self->{open_elements}}) {                  $self->{insertion_mode} = AFTER_HEAD_IM;
5017                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  !!!next-token;
5018                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  next B;
5019                    if (defined $parent and $parent->node_type == 1) {                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5020                      $foster_parent_element = $parent;                  !!!cp ('t134');
5021                      $next_sibling = $self->{open_elements}->[$_]->[0];                  pop @{$self->{open_elements}};
5022                      $prev_sibling = $next_sibling->previous_sibling;                  $self->{insertion_mode} = AFTER_HEAD_IM;
5023                    } else {                  !!!next-token;
5024                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                  next B;
5025                      $prev_sibling = $foster_parent_element->last_child;                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5026                    }                  !!!cp ('t134.1');
5027                    last OE;                  !!!parse-error (type => 'unmatched end tag', text => 'head',
5028                  }                                  token => $token);
5029                } # OE                  ## Ignore the token
5030                $foster_parent_element = $self->{open_elements}->[0]->[0] and                  !!!next-token;
5031                $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});  
5032                } else {                } else {
5033                  $foster_parent_element->insert_before                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
5034                }                }
5035              } else {              } elsif ($token->{tag_name} eq 'noscript') {
5036                $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5037              }                  !!!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]);  
5038                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5039                    $self->{insertion_mode} = IN_HEAD_IM;
5040                    !!!next-token;
5041                    next B;
5042                  } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5043                           $self->{insertion_mode} == AFTER_HEAD_IM) {
5044                    !!!cp ('t137');
5045                    !!!parse-error (type => 'unmatched end tag',
5046                                    text => 'noscript', token => $token);
5047                    ## Ignore the token ## ISSUE: An issue in the spec.
5048                    !!!next-token;
5049                    next B;
5050                  } else {
5051                    !!!cp ('t138');
5052                    #
5053                }                }
   
               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;  
5054              } elsif ({              } elsif ({
5055                        col => 1,                        body => 1, html => 1,
                       td => 1, th => 1, tr => 1,  
5056                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5057                ## Clear back to table context                ## TODO: This branch is entirely redundant.
5058                while ($self->{open_elements}->[-1]->[1] ne 'table' and                if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5059                       $self->{open_elements}->[-1]->[1] ne 'html') {                    $self->{insertion_mode} == IN_HEAD_IM or
5060                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5061                  pop @{$self->{open_elements}};                  !!!cp ('t140');
5062                    !!!parse-error (type => 'unmatched end tag',
5063                                    text => $token->{tag_name}, token => $token);
5064                    ## Ignore the token
5065                    !!!next-token;
5066                    next B;
5067                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5068                    !!!cp ('t140.1');
5069                    !!!parse-error (type => 'unmatched end tag',
5070                                    text => $token->{tag_name}, token => $token);
5071                    ## Ignore the token
5072                    !!!next-token;
5073                    next B;
5074                  } else {
5075                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5076                }                }
5077                } elsif ($token->{tag_name} eq 'p') {
5078                  !!!cp ('t142');
5079                  !!!parse-error (type => 'unmatched end tag',
5080                                  text => $token->{tag_name}, token => $token);
5081                  ## Ignore the token
5082                  !!!next-token;
5083                  next B;
5084                } elsif ($token->{tag_name} eq 'br') {
5085                  if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5086                    !!!cp ('t142.2');
5087                    ## (before head) as if <head>, (in head) as if </head>
5088                    !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5089                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5090                    $self->{insertion_mode} = AFTER_HEAD_IM;
5091      
5092                    ## Reprocess in the "after head" insertion mode...
5093                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5094                    !!!cp ('t143.2');
5095                    ## As if </head>
5096                    pop @{$self->{open_elements}};
5097                    $self->{insertion_mode} = AFTER_HEAD_IM;
5098      
5099                    ## Reprocess in the "after head" insertion mode...
5100                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5101                    !!!cp ('t143.3');
5102                    ## ISSUE: Two parse errors for <head><noscript></br>
5103                    !!!parse-error (type => 'unmatched end tag',
5104                                    text => 'br', token => $token);
5105                    ## As if </noscript>
5106                    pop @{$self->{open_elements}};
5107                    $self->{insertion_mode} = IN_HEAD_IM;
5108    
5109                !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');                  ## Reprocess in the "in head" insertion mode...
5110                $self->{insertion_mode} = $token->{tag_name} eq 'col'                  ## As if </head>
5111                  ? 'in column group' : 'in table body';                  pop @{$self->{open_elements}};
5112                ## 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]);  
5113    
5114                ## As if </table>                  ## Reprocess in the "after head" insertion mode...
5115                ## have a table element in table scope                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5116                my $i;                  !!!cp ('t143.4');
5117                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  #
5118                  my $node = $self->{open_elements}->[$_];                } else {
5119                  if ($node->[1] eq 'table') {                  die "$0: $self->{insertion_mode}: Unknown insertion mode";
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5120                }                }
5121    
5122                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5123                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                !!!parse-error (type => 'unmatched end tag',
5124                }                                text => 'br', token => $token);
5125                  ## Ignore the token
5126                  !!!next-token;
5127                  next B;
5128                } else {
5129                  !!!cp ('t145');
5130                  !!!parse-error (type => 'unmatched end tag',
5131                                  text => $token->{tag_name}, token => $token);
5132                  ## Ignore the token
5133                  !!!next-token;
5134                  next B;
5135                }
5136    
5137                splice @{$self->{open_elements}}, $i;              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5138                  !!!cp ('t146');
5139                  ## As if </noscript>
5140                  pop @{$self->{open_elements}};
5141                  !!!parse-error (type => 'in noscript:/',
5142                                  text => $token->{tag_name}, token => $token);
5143                  
5144                  ## Reprocess in the "in head" insertion mode...
5145                  ## As if </head>
5146                  pop @{$self->{open_elements}};
5147    
5148                $self->_reset_insertion_mode;                ## Reprocess in the "after head" insertion mode...
5149                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5150                  !!!cp ('t147');
5151                  ## As if </head>
5152                  pop @{$self->{open_elements}};
5153    
5154                ## reprocess                ## Reprocess in the "after head" insertion mode...
5155                redo B;              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5156    ## ISSUE: This case cannot be reached?
5157                  !!!cp ('t148');
5158                  !!!parse-error (type => 'unmatched end tag',
5159                                  text => $token->{tag_name}, token => $token);
5160                  ## Ignore the token ## ISSUE: An issue in the spec.
5161                  !!!next-token;
5162                  next B;
5163              } else {              } else {
5164                #                !!!cp ('t149');
5165              }              }
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'table') {  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
5166    
5167                if ($self->{open_elements}->[-1]->[1] ne 'table') {              ## "after head" insertion mode
5168                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              ## As if <body>
5169                }              !!!insert-element ('body',, $token);
5170                $self->{insertion_mode} = IN_BODY_IM;
5171                ## reprocess
5172                next B;
5173          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5174            if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5175              !!!cp ('t149.1');
5176    
5177              ## NOTE: As if <head>
5178              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5179              $self->{open_elements}->[-1]->[0]->append_child
5180                  ($self->{head_element});
5181              #push @{$self->{open_elements}},
5182              #    [$self->{head_element}, $el_category->{head}];
5183              #$self->{insertion_mode} = IN_HEAD_IM;
5184              ## NOTE: Reprocess.
5185    
5186              ## NOTE: As if </head>
5187              #pop @{$self->{open_elements}};
5188              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5189              ## NOTE: Reprocess.
5190              
5191              #
5192            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5193              !!!cp ('t149.2');
5194    
5195                splice @{$self->{open_elements}}, $i;            ## NOTE: As if </head>
5196              pop @{$self->{open_elements}};
5197              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5198              ## NOTE: Reprocess.
5199    
5200                $self->_reset_insertion_mode;            #
5201            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5202              !!!cp ('t149.3');
5203    
5204                !!!next-token;            !!!parse-error (type => 'in noscript:#eof', token => $token);
5205                redo B;  
5206              } elsif ({            ## As if </noscript>
5207                        body => 1, caption => 1, col => 1, colgroup => 1,            pop @{$self->{open_elements}};
5208                        html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,            #$self->{insertion_mode} = IN_HEAD_IM;
5209                        thead => 1, tr => 1,            ## NOTE: Reprocess.
5210                       }->{$token->{tag_name}}) {  
5211                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            ## NOTE: As if </head>
5212                ## Ignore the token            pop @{$self->{open_elements}};
5213                !!!next-token;            #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5214                redo B;            ## NOTE: Reprocess.
5215              } else {  
5216                #            #
5217              }          } else {
5218            } else {            !!!cp ('t149.4');
5219              #            #
5220            }          }
5221    
5222            !!!parse-error (type => 'in table:'.$token->{tag_name});          ## NOTE: As if <body>
5223            $in_body->($insert_to_foster);          !!!insert-element ('body',, $token);
5224            redo B;          $self->{insertion_mode} = IN_BODY_IM;
5225          } elsif ($self->{insertion_mode} eq 'in caption') {          ## NOTE: Reprocess.
5226            if ($token->{type} eq 'character') {          next B;
5227              ## NOTE: This is a code clone of "character in body".        } else {
5228            die "$0: $token->{type}: Unknown token type";
5229          }
5230        } elsif ($self->{insertion_mode} & BODY_IMS) {
5231              if ($token->{type} == CHARACTER_TOKEN) {
5232                !!!cp ('t150');
5233                ## NOTE: There is a code clone of "character in body".
5234              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5235                            
5236              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5237    
5238              !!!next-token;              !!!next-token;
5239              redo B;              next B;
5240            } 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') {  
5241              if ({              if ({
5242                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5243                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5244                  }->{$token->{tag_name}}) {                  }->{$token->{tag_name}}) {
5245                !!!parse-error (type => 'not closed:caption');                if ($self->{insertion_mode} == IN_CELL_IM) {
5246                    ## have an element in table scope
5247                ## As if </caption>                  for (reverse 0..$#{$self->{open_elements}}) {
5248                ## have a table element in table scope                    my $node = $self->{open_elements}->[$_];
5249                my $i;                    if ($node->[1] == TABLE_CELL_EL) {
5250                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                      !!!cp ('t151');
5251                  my $node = $self->{open_elements}->[$_];  
5252                  if ($node->[1] eq 'caption') {                      ## Close the cell
5253                    $i = $_;                      !!!back-token; # <x>
5254                    last INSCOPE;                      $token = {type => END_TAG_TOKEN,
5255                  } elsif ({                                tag_name => $node->[0]->manakai_local_name,
5256                            table => 1, html => 1,                                line => $token->{line},
5257                           }->{$node->[1]}) {                                column => $token->{column}};
5258                    last INSCOPE;                      next B;
5259                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5260                        !!!cp ('t152');
5261                        ## ISSUE: This case can never be reached, maybe.
5262                        last;
5263                      }
5264                  }                  }
5265                } # INSCOPE  
5266                unless (defined $i) {                  !!!cp ('t153');
5267                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
5268                        text => $token->{tag_name}, token => $token);
5269                  ## Ignore the token                  ## Ignore the token
5270                    !!!nack ('t153.1');
5271                  !!!next-token;                  !!!next-token;
5272                  redo B;                  next B;
5273                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5274                                  !!!parse-error (type => 'not closed', text => 'caption',
5275                ## generate implied end tags                                  token => $token);
5276                if ({                  
5277                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5278                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5279                     tbody => 1, tfoot=> 1, thead => 1,                  my $i;
5280                    }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: {
5281                  !!!back-token; # <?>                    for (reverse 0..$#{$self->{open_elements}}) {
5282                  $token = {type => 'end tag', tag_name => 'caption'};                      my $node = $self->{open_elements}->[$_];
5283                  !!!back-token;                      if ($node->[1] == CAPTION_EL) {
5284                  $token = {type => 'end tag',                        !!!cp ('t155');
5285                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        $i = $_;
5286                  redo B;                        last INSCOPE;
5287                }                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5288                          !!!cp ('t156');
5289                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        last;
5290                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      }
5291                }                    }
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
5292    
5293                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
5294                      !!!parse-error (type => 'start tag not allowed',
5295                                      text => $token->{tag_name}, token => $token);
5296                      ## Ignore the token
5297                      !!!nack ('t157.1');
5298                      !!!next-token;
5299                      next B;
5300                    } # INSCOPE
5301                    
5302                    ## generate implied end tags
5303                    while ($self->{open_elements}->[-1]->[1]
5304                               & END_TAG_OPTIONAL_EL) {
5305                      !!!cp ('t158');
5306                      pop @{$self->{open_elements}};
5307                    }
5308    
5309                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5310                redo B;                    !!!cp ('t159');
5311                      !!!parse-error (type => 'not closed',
5312                                      text => $self->{open_elements}->[-1]->[0]
5313                                          ->manakai_local_name,
5314                                      token => $token);
5315                    } else {
5316                      !!!cp ('t160');
5317                    }
5318                    
5319                    splice @{$self->{open_elements}}, $i;
5320                    
5321                    $clear_up_to_marker->();
5322                    
5323                    $self->{insertion_mode} = IN_TABLE_IM;
5324                    
5325                    ## reprocess
5326                    !!!ack-later;
5327                    next B;
5328                  } else {
5329                    !!!cp ('t161');
5330                    #
5331                  }
5332              } else {              } else {
5333                  !!!cp ('t162');
5334                #                #
5335              }              }
5336            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5337              if ($token->{tag_name} eq 'caption') {              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5338                ## have a table element in table scope                if ($self->{insertion_mode} == IN_CELL_IM) {
5339                my $i;                  ## have an element in table scope
5340                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  my $i;
5341                  my $node = $self->{open_elements}->[$_];                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5342                  if ($node->[1] eq $token->{tag_name}) {                    my $node = $self->{open_elements}->[$_];
5343                    $i = $_;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5344                    last INSCOPE;                      !!!cp ('t163');
5345                  } elsif ({                      $i = $_;
5346                            table => 1, html => 1,                      last INSCOPE;
5347                           }->{$node->[1]}) {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5348                    last INSCOPE;                      !!!cp ('t164');
5349                        last INSCOPE;
5350                      }
5351                    } # INSCOPE
5352                      unless (defined $i) {
5353                        !!!cp ('t165');
5354                        !!!parse-error (type => 'unmatched end tag',
5355                                        text => $token->{tag_name},
5356                                        token => $token);
5357                        ## Ignore the token
5358                        !!!next-token;
5359                        next B;
5360                      }
5361                    
5362                    ## generate implied end tags
5363                    while ($self->{open_elements}->[-1]->[1]
5364                               & END_TAG_OPTIONAL_EL) {
5365                      !!!cp ('t166');
5366                      pop @{$self->{open_elements}};
5367                  }                  }
5368                } # INSCOPE  
5369                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5370                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5371                      !!!cp ('t167');
5372                      !!!parse-error (type => 'not closed',
5373                                      text => $self->{open_elements}->[-1]->[0]
5374                                          ->manakai_local_name,
5375                                      token => $token);
5376                    } else {
5377                      !!!cp ('t168');
5378                    }
5379                    
5380                    splice @{$self->{open_elements}}, $i;
5381                    
5382                    $clear_up_to_marker->();
5383                    
5384                    $self->{insertion_mode} = IN_ROW_IM;
5385                    
5386                    !!!next-token;
5387                    next B;
5388                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5389                    !!!cp ('t169');
5390                    !!!parse-error (type => 'unmatched end tag',
5391                                    text => $token->{tag_name}, token => $token);
5392                  ## Ignore the token                  ## Ignore the token
5393                  !!!next-token;                  !!!next-token;
5394                  redo B;                  next B;
5395                }                } else {
5396                                  !!!cp ('t170');
5397                ## generate implied end tags                  #
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5398                }                }
5399                } elsif ($token->{tag_name} eq 'caption') {
5400                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5401                    ## have a table element in table scope
5402                    my $i;
5403                    INSCOPE: {
5404                      for (reverse 0..$#{$self->{open_elements}}) {
5405                        my $node = $self->{open_elements}->[$_];
5406                        if ($node->[1] == CAPTION_EL) {
5407                          !!!cp ('t171');
5408                          $i = $_;
5409                          last INSCOPE;
5410                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5411                          !!!cp ('t172');
5412                          last;
5413                        }
5414                      }
5415    
5416                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5417                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5418                                      text => $token->{tag_name}, token => $token);
5419                      ## Ignore the token
5420                      !!!next-token;
5421                      next B;
5422                    } # INSCOPE
5423                    
5424                    ## generate implied end tags
5425                    while ($self->{open_elements}->[-1]->[1]
5426                               & END_TAG_OPTIONAL_EL) {
5427                      !!!cp ('t174');
5428                      pop @{$self->{open_elements}};
5429                    }
5430                    
5431                    unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5432                      !!!cp ('t175');
5433                      !!!parse-error (type => 'not closed',
5434                                      text => $self->{open_elements}->[-1]->[0]
5435                                          ->manakai_local_name,
5436                                      token => $token);
5437                    } else {
5438                      !!!cp ('t176');
5439                    }
5440                    
5441                    splice @{$self->{open_elements}}, $i;
5442                    
5443                    $clear_up_to_marker->();
5444                    
5445                    $self->{insertion_mode} = IN_TABLE_IM;
5446                    
5447                    !!!next-token;
5448                    next B;
5449                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5450                    !!!cp ('t177');
5451                    !!!parse-error (type => 'unmatched end tag',
5452                                    text => $token->{tag_name}, token => $token);
5453                    ## Ignore the token
5454                    !!!next-token;
5455                    next B;
5456                  } else {
5457                    !!!cp ('t178');
5458                    #
5459                }                }
5460                } elsif ({
5461                          table => 1, tbody => 1, tfoot => 1,
5462                          thead => 1, tr => 1,
5463                         }->{$token->{tag_name}} and
5464                         $self->{insertion_mode} == IN_CELL_IM) {
5465                  ## have an element in table scope
5466                  my $i;
5467                  my $tn;
5468                  INSCOPE: {
5469                    for (reverse 0..$#{$self->{open_elements}}) {
5470                      my $node = $self->{open_elements}->[$_];
5471                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5472                        !!!cp ('t179');
5473                        $i = $_;
5474    
5475                        ## Close the cell
5476                        !!!back-token; # </x>
5477                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5478                                  line => $token->{line},
5479                                  column => $token->{column}};
5480                        next B;
5481                      } elsif ($node->[1] == TABLE_CELL_EL) {
5482                        !!!cp ('t180');
5483                        $tn = $node->[0]->manakai_local_name;
5484                        ## NOTE: There is exactly one |td| or |th| element
5485                        ## in scope in the stack of open elements by definition.
5486                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5487                        ## ISSUE: Can this be reached?
5488                        !!!cp ('t181');
5489                        last;
5490                      }
5491                    }
5492    
5493                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5494                    !!!parse-error (type => 'unmatched end tag',
5495                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5496                    ## Ignore the token
5497                $self->{insertion_mode} = 'in table';                  !!!next-token;
5498                    next B;
5499                !!!next-token;                } # INSCOPE
5500                redo B;              } elsif ($token->{tag_name} eq 'table' and
5501              } elsif ($token->{tag_name} eq 'table') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5502                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5503                                  token => $token);
5504    
5505                ## As if </caption>                ## As if </caption>
5506                ## have a table element in table scope                ## have a table element in table scope
5507                my $i;                my $i;
5508                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5509                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5510                  if ($node->[1] eq 'caption') {                  if ($node->[1] == CAPTION_EL) {
5511                      !!!cp ('t184');
5512                    $i = $_;                    $i = $_;
5513                    last INSCOPE;                    last INSCOPE;
5514                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5515                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5516                    last INSCOPE;                    last INSCOPE;
5517                  }                  }
5518                } # INSCOPE                } # INSCOPE
5519                unless (defined $i) {                unless (defined $i) {
5520                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5521                    !!!parse-error (type => 'unmatched end tag',
5522                                    text => 'caption', token => $token);
5523                  ## Ignore the token                  ## Ignore the token
5524                  !!!next-token;                  !!!next-token;
5525                  redo B;                  next B;
5526                }                }
5527                                
5528                ## generate implied end tags                ## generate implied end tags
5529                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5530                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5531                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # </table>  
                 $token = {type => 'end tag', tag_name => 'caption'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5532                }                }
5533    
5534                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5535                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5536                    !!!parse-error (type => 'not closed',
5537                                    text => $self->{open_elements}->[-1]->[0]
5538                                        ->manakai_local_name,
5539                                    token => $token);
5540                  } else {
5541                    !!!cp ('t189');
5542                }                }
5543    
5544                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5545    
5546                $clear_up_to_marker->();                $clear_up_to_marker->();
5547    
5548                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5549    
5550                ## reprocess                ## reprocess
5551                redo B;                next B;
5552              } elsif ({              } elsif ({
5553                        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,  
5554                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
5555                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5556                ## Ignore the token                  !!!cp ('t190');
5557                redo B;                  !!!parse-error (type => 'unmatched end tag',
5558              } 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');  
5559                  ## Ignore the token                  ## Ignore the token
5560                  !!!next-token;                  !!!next-token;
5561                  redo B;                  next B;
5562                } else {                } else {
5563                  pop @{$self->{open_elements}}; # colgroup                  !!!cp ('t191');
5564                  $self->{insertion_mode} = 'in table';                  #
                 !!!next-token;  
                 redo B;              
5565                }                }
5566              } elsif ($token->{tag_name} eq 'col') {              } elsif ({
5567                !!!parse-error (type => 'unmatched end tag:col');                        tbody => 1, tfoot => 1,
5568                          thead => 1, tr => 1,
5569                         }->{$token->{tag_name}} and
5570                         $self->{insertion_mode} == IN_CAPTION_IM) {
5571                  !!!cp ('t192');
5572                  !!!parse-error (type => 'unmatched end tag',
5573                                  text => $token->{tag_name}, token => $token);
5574                ## Ignore the token                ## Ignore the token
5575                !!!next-token;                !!!next-token;
5576                redo B;                next B;
5577              } else {              } else {
5578                #                !!!cp ('t193');
5579                  #
5580              }              }
5581            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5582              #          for my $entry (@{$self->{open_elements}}) {
5583              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5584                !!!cp ('t75');
5585                !!!parse-error (type => 'in body:#eof', token => $token);
5586                last;
5587            }            }
5588            }
5589    
5590            ## As if </colgroup>          ## Stop parsing.
5591            if ($self->{open_elements}->[-1]->[1] eq 'html') {          last B;
5592              !!!parse-error (type => 'unmatched end tag:colgroup');        } else {
5593              ## Ignore the token          die "$0: $token->{type}: Unknown token type";
5594          }
5595    
5596          $insert = $insert_to_current;
5597          #
5598        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5599          if ($token->{type} == CHARACTER_TOKEN) {
5600            if (not $open_tables->[-1]->[1] and # tainted
5601                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5602              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5603                  
5604              unless (length $token->{data}) {
5605                !!!cp ('t194');
5606              !!!next-token;              !!!next-token;
5607              redo B;              next B;
5608            } else {            } else {
5609              pop @{$self->{open_elements}}; # colgroup              !!!cp ('t195');
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
5610            }            }
5611          } elsif ($self->{insertion_mode} eq 'in table body') {          }
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
5612    
5613              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table:#text', token => $token);
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5614    
5615              if ({          ## NOTE: As if in body, but insert into the foster parent element.
5616                   table => 1, tbody => 1, tfoot => 1,          $reconstruct_active_formatting_elements->($insert_to_foster);
5617                   thead => 1, tr => 1,              
5618                  }->{$self->{open_elements}->[-1]->[1]}) {          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5619                # MUST            # MUST
5620                my $foster_parent_element;            my $foster_parent_element;
5621                my $next_sibling;            my $next_sibling;
5622                my $prev_sibling;            my $prev_sibling;
5623                OE: for (reverse 0..$#{$self->{open_elements}}) {            OE: for (reverse 0..$#{$self->{open_elements}}) {
5624                  if ($self->{open_elements}->[$_]->[1] eq 'table') {              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
5625                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5626                    if (defined $parent and $parent->node_type == 1) {                if (defined $parent and $parent->node_type == 1) {
5627                      $foster_parent_element = $parent;                  $foster_parent_element = $parent;
5628                      $next_sibling = $self->{open_elements}->[$_]->[0];                  !!!cp ('t196');
5629                      $prev_sibling = $next_sibling->previous_sibling;                  $next_sibling = $self->{open_elements}->[$_]->[0];
5630                    } else {                  $prev_sibling = $next_sibling->previous_sibling;
5631                      $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});  
5632                } else {                } else {
5633                  $foster_parent_element->insert_before                  !!!cp ('t197');
5634                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5635                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5636                    #
5637                }                }
5638              } else {                last OE;
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5639              }              }
5640              } # OE
5641              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5642              $prev_sibling = $foster_parent_element->last_child
5643                  unless defined $foster_parent_element;
5644              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5645              if (defined $prev_sibling and
5646                  $prev_sibling->node_type == 3) {
5647                !!!cp ('t198');
5648                $prev_sibling->manakai_append_text ($token->{data});
5649              } else {
5650                !!!cp ('t199');
5651                $foster_parent_element->insert_before
5652                    ($self->{document}->create_text_node ($token->{data}),
5653                     $next_sibling);
5654              }
5655              $open_tables->[-1]->[1] = 1; # tainted
5656              $open_tables->[-1]->[2] = 1; # ~node inserted
5657            } else {
5658              ## NOTE: Fragment case or in a foster parent'ed element
5659              ## (e.g. |<table><span>a|).  In fragment case, whether the
5660              ## character is appended to existing node or a new node is
5661              ## created is irrelevant, since the foster parent'ed nodes
5662              ## are discarded and fragment parsing does not invoke any
5663              ## script.
5664              !!!cp ('t200');
5665              $self->{open_elements}->[-1]->[0]->manakai_append_text
5666                  ($token->{data});
5667            }
5668                            
5669              !!!next-token;          !!!next-token;
5670              redo B;          next B;
5671            } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == START_TAG_TOKEN) {
5672              ## Copied from 'in table'          if ({
5673              my $comment = $self->{document}->create_comment ($token->{data});               tr => ($self->{insertion_mode} != IN_ROW_IM),
5674              $self->{open_elements}->[-1]->[0]->append_child ($comment);               th => 1, td => 1,
5675              !!!next-token;              }->{$token->{tag_name}}) {
5676              redo B;            if ($self->{insertion_mode} == IN_TABLE_IM) {
5677            } elsif ($token->{type} eq 'start tag') {              ## Clear back to table context
5678              if ({              while (not ($self->{open_elements}->[-1]->[1]
5679                   tr => 1,                              & TABLE_SCOPING_EL)) {
5680                   th => 1, td => 1,                !!!cp ('t201');
5681                  }->{$token->{tag_name}}) {                pop @{$self->{open_elements}};
5682                unless ($token->{tag_name} eq 'tr') {              }
5683                  !!!parse-error (type => 'missing start tag:tr');              
5684                }              !!!insert-element ('tbody',, $token);
5685                $self->{insertion_mode} = IN_TABLE_BODY_IM;
5686                ## reprocess in the "in table body" insertion mode...
5687              }
5688              
5689              if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5690                unless ($token->{tag_name} eq 'tr') {
5691                  !!!cp ('t202');
5692                  !!!parse-error (type => 'missing start tag:tr', token => $token);
5693                }
5694                    
5695                ## Clear back to table body context
5696                while (not ($self->{open_elements}->[-1]->[1]
5697                                & TABLE_ROWS_SCOPING_EL)) {
5698                  !!!cp ('t203');
5699                  ## ISSUE: Can this case be reached?
5700                  pop @{$self->{open_elements}};
5701                }
5702                    
5703                $self->{insertion_mode} = IN_ROW_IM;
5704                if ($token->{tag_name} eq 'tr') {
5705                  !!!cp ('t204');
5706                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5707                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5708                  !!!nack ('t204');
5709                  !!!next-token;
5710                  next B;
5711                } else {
5712                  !!!cp ('t205');
5713                  !!!insert-element ('tr',, $token);
5714                  ## reprocess in the "in row" insertion mode
5715                }
5716              } else {
5717                !!!cp ('t206');
5718              }
5719    
5720                ## Clear back to table body context                ## Clear back to table row context
5721                while (not {                while (not ($self->{open_elements}->[-1]->[1]
5722                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
5723                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t207');
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5724                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5725                }                }
5726                                
5727                $self->{insertion_mode} = 'in row';            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5728                if ($token->{tag_name} eq 'tr') {            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5729                  !!!insert-element ($token->{tag_name}, $token->{attributes});            $self->{insertion_mode} = IN_CELL_IM;
5730                  !!!next-token;  
5731                } else {            push @$active_formatting_elements, ['#marker', ''];
5732                  !!!insert-element ('tr');                
5733                  ## reprocess            !!!nack ('t207.1');
5734                }            !!!next-token;
5735                redo B;            next B;
5736              } elsif ({          } elsif ({
5737                        caption => 1, col => 1, colgroup => 1,                    caption => 1, col => 1, colgroup => 1,
5738                        tbody => 1, tfoot => 1, thead => 1,                    tbody => 1, tfoot => 1, thead => 1,
5739                       }->{$token->{tag_name}}) {                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5740                ## have an element in table scope                   }->{$token->{tag_name}}) {
5741                my $i;            if ($self->{insertion_mode} == IN_ROW_IM) {
5742                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              ## As if </tr>
5743                  my $node = $self->{open_elements}->[$_];              ## have an element in table scope
5744                  if ({              my $i;
5745                       tbody => 1, thead => 1, tfoot => 1,              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5746                      }->{$node->[1]}) {                my $node = $self->{open_elements}->[$_];
5747                    $i = $_;                if ($node->[1] == TABLE_ROW_EL) {
5748                    last INSCOPE;                  !!!cp ('t208');
5749                  } elsif ({                  $i = $_;
5750                            table => 1, html => 1,                  last INSCOPE;
5751                           }->{$node->[1]}) {                } elsif ($node->[1] & TABLE_SCOPING_EL) {
5752                    last INSCOPE;                  !!!cp ('t209');
5753                    last INSCOPE;
5754                  }
5755                } # INSCOPE
5756                unless (defined $i) {
5757                  !!!cp ('t210');
5758                  ## TODO: This type is wrong.
5759                  !!!parse-error (type => 'unmacthed end tag',
5760                                  text => $token->{tag_name}, token => $token);
5761                  ## Ignore the token
5762                  !!!nack ('t210.1');
5763                  !!!next-token;
5764                  next B;
5765                }
5766                    
5767                    ## Clear back to table row context
5768                    while (not ($self->{open_elements}->[-1]->[1]
5769                                    & TABLE_ROW_SCOPING_EL)) {
5770                      !!!cp ('t211');
5771                      ## ISSUE: Can this case be reached?
5772                      pop @{$self->{open_elements}};
5773                    }
5774                    
5775                    pop @{$self->{open_elements}}; # tr
5776                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5777                    if ($token->{tag_name} eq 'tr') {
5778                      !!!cp ('t212');
5779                      ## reprocess
5780                      !!!ack-later;
5781                      next B;
5782                    } else {
5783                      !!!cp ('t213');
5784                      ## reprocess in the "in table body" insertion mode...
5785                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
5786                }                }
5787    
5788                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5789                while (not {                  ## have an element in table scope
5790                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5791                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5792                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5793                      if ($node->[1] == TABLE_ROW_GROUP_EL) {
5794                        !!!cp ('t214');
5795                        $i = $_;
5796                        last INSCOPE;
5797                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5798                        !!!cp ('t215');
5799                        last INSCOPE;
5800                      }
5801                    } # INSCOPE
5802                    unless (defined $i) {
5803                      !!!cp ('t216');
5804    ## TODO: This erorr type is wrong.
5805                      !!!parse-error (type => 'unmatched end tag',
5806                                      text => $token->{tag_name}, token => $token);
5807                      ## Ignore the token
5808                      !!!nack ('t216.1');
5809                      !!!next-token;
5810                      next B;
5811                    }
5812    
5813                    ## Clear back to table body context
5814                    while (not ($self->{open_elements}->[-1]->[1]
5815                                    & TABLE_ROWS_SCOPING_EL)) {
5816                      !!!cp ('t217');
5817                      ## ISSUE: Can this state be reached?
5818                      pop @{$self->{open_elements}};
5819                    }
5820                    
5821                    ## As if <{current node}>
5822                    ## have an element in table scope
5823                    ## true by definition
5824                    
5825                    ## Clear back to table body context
5826                    ## nop by definition
5827                    
5828                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5829                    $self->{insertion_mode} = IN_TABLE_IM;
5830                    ## reprocess in "in table" insertion mode...
5831                  } else {
5832                    !!!cp ('t218');
5833                }                }
5834    
5835                ## As if <{current node}>            if ($token->{tag_name} eq 'col') {
5836                ## have an element in table scope              ## Clear back to table context
5837                ## true by definition              while (not ($self->{open_elements}->[-1]->[1]
5838                                & TABLE_SCOPING_EL)) {
5839                ## Clear back to table body context                !!!cp ('t219');
5840                ## nop by definition                ## ISSUE: Can this state be reached?
   
5841                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5842                $self->{insertion_mode} = 'in table';              }
5843                ## reprocess              
5844                redo B;              !!!insert-element ('colgroup',, $token);
5845                $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5846                ## reprocess
5847                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5848                !!!ack-later;
5849                next B;
5850              } elsif ({
5851                        caption => 1,
5852                        colgroup => 1,
5853                        tbody => 1, tfoot => 1, thead => 1,
5854                       }->{$token->{tag_name}}) {
5855                ## Clear back to table context
5856                    while (not ($self->{open_elements}->[-1]->[1]
5857                                    & TABLE_SCOPING_EL)) {
5858                      !!!cp ('t220');
5859                      ## ISSUE: Can this state be reached?
5860                      pop @{$self->{open_elements}};
5861                    }
5862                    
5863                push @$active_formatting_elements, ['#marker', '']
5864                    if $token->{tag_name} eq 'caption';
5865                    
5866                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5867                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5868                $self->{insertion_mode} = {
5869                                           caption => IN_CAPTION_IM,
5870                                           colgroup => IN_COLUMN_GROUP_IM,
5871                                           tbody => IN_TABLE_BODY_IM,
5872                                           tfoot => IN_TABLE_BODY_IM,
5873                                           thead => IN_TABLE_BODY_IM,
5874                                          }->{$token->{tag_name}};
5875                !!!next-token;
5876                !!!nack ('t220.1');
5877                next B;
5878              } else {
5879                die "$0: in table: <>: $token->{tag_name}";
5880              }
5881              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5882                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5883                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5884                                      ->manakai_local_name,
5885                                  token => $token);
5886    
5887                ## As if </table>                ## As if </table>
5888                ## have a table element in table scope                ## have a table element in table scope
5889                my $i;                my $i;
5890                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5891                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5892                  if ($node->[1] eq 'table') {                  if ($node->[1] == TABLE_EL) {
5893                      !!!cp ('t221');
5894                    $i = $_;                    $i = $_;
5895                    last INSCOPE;                    last INSCOPE;
5896                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5897                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5898                    last INSCOPE;                    last INSCOPE;
5899                  }                  }
5900                } # INSCOPE                } # INSCOPE
5901                unless (defined $i) {                unless (defined $i) {
5902                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5903    ## TODO: The following is wrong, maybe.
5904                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5905                                    token => $token);
5906                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5907                    !!!nack ('t223.1');
5908                  !!!next-token;                  !!!next-token;
5909                  redo B;                  next B;
5910                }                }
5911                                
5912    ## TODO: Followings are removed from the latest spec.
5913                ## generate implied end tags                ## generate implied end tags
5914                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5915                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5916                     td => 1, th => 1, tr => 1,                  pop @{$self->{open_elements}};
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
5917                }                }
5918    
5919                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5920                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5921                    ## NOTE: |<table><tr><table>|
5922                    !!!parse-error (type => 'not closed',
5923                                    text => $self->{open_elements}->[-1]->[0]
5924                                        ->manakai_local_name,
5925                                    token => $token);
5926                  } else {
5927                    !!!cp ('t226');
5928                }                }
5929    
5930                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5931                  pop @{$open_tables};
5932    
5933                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5934    
5935                ## reprocess            ## reprocess
5936                redo B;            !!!ack-later;
5937              } else {            next B;
5938                #          } elsif ($token->{tag_name} eq 'style') {
5939              }            if (not $open_tables->[-1]->[1]) { # tainted
5940            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5941              if ({              ## NOTE: This is a "as if in head" code clone.
5942                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5943                  }->{$token->{tag_name}}) {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5944                ## have an element in table scope              next B;
5945                my $i;            } else {
5946                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!cp ('t227.7');
5947                  my $node = $self->{open_elements}->[$_];              #
5948                  if ($node->[1] eq $token->{tag_name}) {            }
5949                    $i = $_;          } elsif ($token->{tag_name} eq 'script') {
5950                    last INSCOPE;            if (not $open_tables->[-1]->[1]) { # tainted
5951                  } elsif ({              !!!cp ('t227.6');
5952                            table => 1, html => 1,              ## NOTE: This is a "as if in head" code clone.
5953                           }->{$node->[1]}) {              $script_start_tag->();
5954                    last INSCOPE;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5955                  }              next B;
5956                } # INSCOPE            } else {
5957                unless (defined $i) {              !!!cp ('t227.5');
5958                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              #
5959                  ## Ignore the token            }
5960                  !!!next-token;          } elsif ($token->{tag_name} eq 'input') {
5961                  redo B;            if (not $open_tables->[-1]->[1]) { # tainted
5962                }              if ($token->{attributes}->{type}) { ## TODO: case
5963                  my $type = lc $token->{attributes}->{type}->{value};
5964                  if ($type eq 'hidden') {
5965                    !!!cp ('t227.3');
5966                    !!!parse-error (type => 'in table',
5967                                    text => $token->{tag_name}, token => $token);
5968    
5969                ## Clear back to table body context                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5970                while (not {                  $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
                 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}};  
               }  
5971    
5972                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;  
               }  
5973    
               ## 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]);  
5974                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
   
               ## As if <{current node}>  
               ## have an element in table scope  
               ## true by definition  
5975    
5976                ## Clear back to table body context                  !!!next-token;
5977                ## nop by definition                  !!!ack ('t227.2.1');
5978                    next B;
5979                pop @{$self->{open_elements}};                } else {
5980                $self->{insertion_mode} = 'in table';                  !!!cp ('t227.2');
5981                ## reprocess                  #
5982                redo B;                }
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
5983              } else {              } else {
5984                  !!!cp ('t227.1');
5985                #                #
5986              }              }
5987            } else {            } else {
5988                !!!cp ('t227.4');
5989              #              #
5990            }            }
5991                      } else {
5992            ## As if in table            !!!cp ('t227');
5993            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
5994            $in_body->($insert_to_foster);          }
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in row') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
   
             !!!parse-error (type => 'in table:#character');  
5995    
5996              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table', text => $token->{tag_name},
5997              ## ISSUE: Spec says that "whenever a node would be inserted                          token => $token);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'comment') {  
             ## Copied from 'in table'  
             my $comment = $self->{document}->create_comment ($token->{data});  
             $self->{open_elements}->[-1]->[0]->append_child ($comment);  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
5998    
5999                push @$active_formatting_elements, ['#marker', ''];          $insert = $insert_to_foster;
6000                          #
6001                !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
6002                redo B;              if ($token->{tag_name} eq 'tr' and
6003              } elsif ({                  $self->{insertion_mode} == IN_ROW_IM) {
                       caption => 1, col => 1, colgroup => 1,  
                       tbody => 1, tfoot => 1, thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## As if </tr>  
6004                ## have an element in table scope                ## have an element in table scope
6005                my $i;                my $i;
6006                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6007                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6008                  if ($node->[1] eq 'tr') {                  if ($node->[1] == TABLE_ROW_EL) {
6009                      !!!cp ('t228');
6010                    $i = $_;                    $i = $_;
6011                    last INSCOPE;                    last INSCOPE;
6012                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6013                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
6014                    last INSCOPE;                    last INSCOPE;
6015                  }                  }
6016                } # INSCOPE                } # INSCOPE
6017                unless (defined $i) {                unless (defined $i) {
6018                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
6019                    !!!parse-error (type => 'unmatched end tag',
6020                                    text => $token->{tag_name}, token => $token);
6021                  ## Ignore the token                  ## Ignore the token
6022                    !!!nack ('t230.1');
6023                  !!!next-token;                  !!!next-token;
6024                  redo B;                  next B;
6025                  } else {
6026                    !!!cp ('t232');
6027                }                }
6028    
6029                ## Clear back to table row context                ## Clear back to table row context
6030                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6031                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6032                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6033                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6034                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6035                }                }
6036    
6037                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6038                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6039                ## reprocess                !!!next-token;
6040                redo B;                !!!nack ('t231.1');
6041                  next B;
6042              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6043                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
6044                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
6045                    ## have an element in table scope
6046                ## As if </table>                  my $i;
6047                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6048                my $i;                    my $node = $self->{open_elements}->[$_];
6049                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] == TABLE_ROW_EL) {
6050                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
6051                  if ($node->[1] eq 'table') {                      $i = $_;
6052                    $i = $_;                      last INSCOPE;
6053                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6054                  } elsif ({                      !!!cp ('t234');
6055                            table => 1, html => 1,                      last INSCOPE;
6056                           }->{$node->[1]}) {                    }
6057                    last INSCOPE;                  } # INSCOPE
6058                    unless (defined $i) {
6059                      !!!cp ('t235');
6060    ## TODO: The following is wrong.
6061                      !!!parse-error (type => 'unmatched end tag',
6062                                      text => $token->{type}, token => $token);
6063                      ## Ignore the token
6064                      !!!nack ('t236.1');
6065                      !!!next-token;
6066                      next B;
6067                  }                  }
6068                } # INSCOPE                  
6069                unless (defined $i) {                  ## Clear back to table row context
6070                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
6071                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
6072                  !!!next-token;                    !!!cp ('t236');
6073                  redo B;  ## ISSUE: Can this state be reached?
6074                }                    pop @{$self->{open_elements}};
6075                                  }
6076                ## generate implied end tags                  
6077                if ({                  pop @{$self->{open_elements}}; # tr
6078                     dd => 1, dt => 1, li => 1, p => 1,                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6079                     td => 1, th => 1, tr => 1,                  ## reprocess in the "in table body" insertion mode...
6080                     tbody => 1, tfoot=> 1, thead => 1,                }
6081                    }->{$self->{open_elements}->[-1]->[1]}) {  
6082                  !!!back-token; # <table>                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6083                  $token = {type => 'end tag', tag_name => 'table'};                  ## have an element in table scope
6084                  !!!back-token;                  my $i;
6085                  $token = {type => 'end tag',                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6086                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                    my $node = $self->{open_elements}->[$_];
6087                  redo B;                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
6088                        !!!cp ('t237');
6089                        $i = $_;
6090                        last INSCOPE;
6091                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6092                        !!!cp ('t238');
6093                        last INSCOPE;
6094                      }
6095                    } # INSCOPE
6096                    unless (defined $i) {
6097                      !!!cp ('t239');
6098                      !!!parse-error (type => 'unmatched end tag',
6099                                      text => $token->{tag_name}, token => $token);
6100                      ## Ignore the token
6101                      !!!nack ('t239.1');
6102                      !!!next-token;
6103                      next B;
6104                    }
6105                    
6106                    ## Clear back to table body context
6107                    while (not ($self->{open_elements}->[-1]->[1]
6108                                    & TABLE_ROWS_SCOPING_EL)) {
6109                      !!!cp ('t240');
6110                      pop @{$self->{open_elements}};
6111                    }
6112                    
6113                    ## As if <{current node}>
6114                    ## have an element in table scope
6115                    ## true by definition
6116                    
6117                    ## Clear back to table body context
6118                    ## nop by definition
6119                    
6120                    pop @{$self->{open_elements}};
6121                    $self->{insertion_mode} = IN_TABLE_IM;
6122                    ## reprocess in the "in table" insertion mode...
6123                }                }
6124    
6125                if ($self->{open_elements}->[-1]->[1] ne 'table') {                ## NOTE: </table> in the "in table" insertion mode.
6126                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                ## When you edit the code fragment below, please ensure that
6127                }                ## the code for <table> in the "in table" insertion mode
6128                  ## is synced with it.
6129    
6130                splice @{$self->{open_elements}}, $i;                ## have a table element in table scope
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
6131                my $i;                my $i;
6132                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6133                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6134                  if ($node->[1] eq $token->{tag_name}) {                  if ($node->[1] == TABLE_EL) {
6135                      !!!cp ('t241');
6136                    $i = $_;                    $i = $_;
6137                    last INSCOPE;                    last INSCOPE;
6138                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6139                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6140                    last INSCOPE;                    last INSCOPE;
6141                  }                  }
6142                } # INSCOPE                } # INSCOPE
6143                unless (defined $i) {                unless (defined $i) {
6144                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  !!!cp ('t243');
6145                    !!!parse-error (type => 'unmatched end tag',
6146                                    text => $token->{tag_name}, token => $token);
6147                  ## Ignore the token                  ## Ignore the token
6148                    !!!nack ('t243.1');
6149                  !!!next-token;                  !!!next-token;
6150                  redo B;                  next B;
6151                }                }
6152                    
6153                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6154                while (not {                pop @{$open_tables};
6155                  tr => 1, html => 1,                
6156                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6157                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6158                  pop @{$self->{open_elements}};                !!!next-token;
6159                  next B;
6160                } elsif ({
6161                          tbody => 1, tfoot => 1, thead => 1,
6162                         }->{$token->{tag_name}} and
6163                         $self->{insertion_mode} & ROW_IMS) {
6164                  if ($self->{insertion_mode} == IN_ROW_IM) {
6165                    ## have an element in table scope
6166                    my $i;
6167                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6168                      my $node = $self->{open_elements}->[$_];
6169                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6170                        !!!cp ('t247');
6171                        $i = $_;
6172                        last INSCOPE;
6173                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6174                        !!!cp ('t248');
6175                        last INSCOPE;
6176                      }
6177                    } # INSCOPE
6178                      unless (defined $i) {
6179                        !!!cp ('t249');
6180                        !!!parse-error (type => 'unmatched end tag',
6181                                        text => $token->{tag_name}, token => $token);
6182                        ## Ignore the token
6183                        !!!nack ('t249.1');
6184                        !!!next-token;
6185                        next B;
6186                      }
6187                    
6188                    ## As if </tr>
6189                    ## have an element in table scope
6190                    my $i;
6191                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6192                      my $node = $self->{open_elements}->[$_];
6193                      if ($node->[1] == TABLE_ROW_EL) {
6194                        !!!cp ('t250');
6195                        $i = $_;
6196                        last INSCOPE;
6197                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6198                        !!!cp ('t251');
6199                        last INSCOPE;
6200                      }
6201                    } # INSCOPE
6202                      unless (defined $i) {
6203                        !!!cp ('t252');
6204                        !!!parse-error (type => 'unmatched end tag',
6205                                        text => 'tr', token => $token);
6206                        ## Ignore the token
6207                        !!!nack ('t252.1');
6208                        !!!next-token;
6209                        next B;
6210                      }
6211                    
6212                    ## Clear back to table row context
6213                    while (not ($self->{open_elements}->[-1]->[1]
6214                                    & TABLE_ROW_SCOPING_EL)) {
6215                      !!!cp ('t253');
6216    ## ISSUE: Can this case be reached?
6217                      pop @{$self->{open_elements}};
6218                    }
6219                    
6220                    pop @{$self->{open_elements}}; # tr
6221                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
6222                    ## reprocess in the "in table body" insertion mode...
6223                }                }
6224    
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## As if </tr>  
6225                ## have an element in table scope                ## have an element in table scope
6226                my $i;                my $i;
6227                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6228                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6229                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6230                      !!!cp ('t254');
6231                    $i = $_;                    $i = $_;
6232                    last INSCOPE;                    last INSCOPE;
6233                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6234                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6235                    last INSCOPE;                    last INSCOPE;
6236                  }                  }
6237                } # INSCOPE                } # INSCOPE
6238                unless (defined $i) {                unless (defined $i) {
6239                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t256');
6240                    !!!parse-error (type => 'unmatched end tag',
6241                                    text => $token->{tag_name}, token => $token);
6242                  ## Ignore the token                  ## Ignore the token
6243                    !!!nack ('t256.1');
6244                  !!!next-token;                  !!!next-token;
6245                  redo B;                  next B;
6246                }                }
6247    
6248                ## Clear back to table row context                ## Clear back to table body context
6249                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6250                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6251                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6252                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6253                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6254                }                }
6255    
6256                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6257                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6258                ## reprocess                !!!nack ('t257.1');
6259                redo B;                !!!next-token;
6260                  next B;
6261              } elsif ({              } elsif ({
6262                        tbody => 1, tfoot => 1, thead => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6263                          html => 1, td => 1, th => 1,
6264                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6265                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6266                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6267                ## have an element in table scope            !!!cp ('t258');
6268                my $i;            !!!parse-error (type => 'unmatched end tag',
6269                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
6270                  my $node = $self->{open_elements}->[$_];            ## Ignore the token
6271                  if ($node->[1] eq $token->{tag_name}) {            !!!nack ('t258.1');
6272                    $i = $_;             !!!next-token;
6273                    last INSCOPE;            next B;
6274                  } elsif ({          } else {
6275                            table => 1, html => 1,            !!!cp ('t259');
6276                           }->{$node->[1]}) {            !!!parse-error (type => 'in table:/',
6277                    last INSCOPE;                            text => $token->{tag_name}, token => $token);
6278                  }  
6279                } # INSCOPE            $insert = $insert_to_foster;
6280                unless (defined $i) {            #
6281                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          }
6282                  ## Ignore the token        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6283            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6284                    @{$self->{open_elements}} == 1) { # redundant, maybe
6285              !!!parse-error (type => 'in body:#eof', token => $token);
6286              !!!cp ('t259.1');
6287              #
6288            } else {
6289              !!!cp ('t259.2');
6290              #
6291            }
6292    
6293            ## Stop parsing
6294            last B;
6295          } else {
6296            die "$0: $token->{type}: Unknown token type";
6297          }
6298        } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6299              if ($token->{type} == CHARACTER_TOKEN) {
6300                if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6301                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6302                  unless (length $token->{data}) {
6303                    !!!cp ('t260');
6304                  !!!next-token;                  !!!next-token;
6305                  redo B;                  next B;
6306                }                }
6307                }
6308                ## As if </tr>              
6309                ## have an element in table scope              !!!cp ('t261');
6310                my $i;              #
6311                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } elsif ($token->{type} == START_TAG_TOKEN) {
6312                  my $node = $self->{open_elements}->[$_];              if ($token->{tag_name} eq 'col') {
6313                  if ($node->[1] eq 'tr') {                !!!cp ('t262');
6314                    $i = $_;                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6315                    last INSCOPE;                pop @{$self->{open_elements}};
6316                  } elsif ({                !!!ack ('t262.1');
6317                            table => 1, html => 1,                !!!next-token;
6318                           }->{$node->[1]}) {                next B;
6319                    last INSCOPE;              } else {
6320                  }                !!!cp ('t263');
6321                } # INSCOPE                #
6322                unless (defined $i) {              }
6323                  !!!parse-error (type => 'unmatched end tag:tr');            } elsif ($token->{type} == END_TAG_TOKEN) {
6324                if ($token->{tag_name} eq 'colgroup') {
6325                  if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6326                    !!!cp ('t264');
6327                    !!!parse-error (type => 'unmatched end tag',
6328                                    text => 'colgroup', token => $token);
6329                  ## Ignore the token                  ## Ignore the token
6330                  !!!next-token;                  !!!next-token;
6331                  redo B;                  next B;
6332                }                } else {
6333                    !!!cp ('t265');
6334                ## Clear back to table row context                  pop @{$self->{open_elements}}; # colgroup
6335                while (not {                  $self->{insertion_mode} = IN_TABLE_IM;
6336                  tr => 1, html => 1,                  !!!next-token;
6337                }->{$self->{open_elements}->[-1]->[1]}) {                  next B;            
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
6338                }                }
6339                } elsif ($token->{tag_name} eq 'col') {
6340                pop @{$self->{open_elements}}; # tr                !!!cp ('t266');
6341                $self->{insertion_mode} = 'in table body';                !!!parse-error (type => 'unmatched end tag',
6342                ## 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});  
6343                ## Ignore the token                ## Ignore the token
6344                !!!next-token;                !!!next-token;
6345                redo B;                next B;
6346              } else {              } else {
6347                #                !!!cp ('t267');
6348                  #
6349              }              }
6350          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6351            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6352                @{$self->{open_elements}} == 1) { # redundant, maybe
6353              !!!cp ('t270.2');
6354              ## Stop parsing.
6355              last B;
6356            } else {
6357              ## NOTE: As if </colgroup>.
6358              !!!cp ('t270.1');
6359              pop @{$self->{open_elements}}; # colgroup
6360              $self->{insertion_mode} = IN_TABLE_IM;
6361              ## Reprocess.
6362              next B;
6363            }
6364          } else {
6365            die "$0: $token->{type}: Unknown token type";
6366          }
6367    
6368              ## As if </colgroup>
6369              if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6370                !!!cp ('t269');
6371    ## TODO: Wrong error type?
6372                !!!parse-error (type => 'unmatched end tag',
6373                                text => 'colgroup', token => $token);
6374                ## Ignore the token
6375                !!!nack ('t269.1');
6376                !!!next-token;
6377                next B;
6378            } else {            } else {
6379              #              !!!cp ('t270');
6380                pop @{$self->{open_elements}}; # colgroup
6381                $self->{insertion_mode} = IN_TABLE_IM;
6382                !!!ack-later;
6383                ## reprocess
6384                next B;
6385              }
6386        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6387          if ($token->{type} == CHARACTER_TOKEN) {
6388            !!!cp ('t271');
6389            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6390            !!!next-token;
6391            next B;
6392          } elsif ($token->{type} == START_TAG_TOKEN) {
6393            if ($token->{tag_name} eq 'option') {
6394              if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6395                !!!cp ('t272');
6396                ## As if </option>
6397                pop @{$self->{open_elements}};
6398              } else {
6399                !!!cp ('t273');
6400            }            }
6401    
6402            ## As if in table            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6403            !!!parse-error (type => 'in table:'.$token->{tag_name});            !!!nack ('t273.1');
6404            $in_body->($insert_to_foster);            !!!next-token;
6405            redo B;            next B;
6406          } elsif ($self->{insertion_mode} eq 'in cell') {          } elsif ($token->{tag_name} eq 'optgroup') {
6407            if ($token->{type} eq 'character') {            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6408              ## NOTE: This is a code clone of "character in body".              !!!cp ('t274');
6409              $reconstruct_active_formatting_elements->($insert_to_current);              ## As if </option>
6410                            pop @{$self->{open_elements}};
6411              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});            } else {
6412                !!!cp ('t275');
6413              }
6414    
6415              if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6416                !!!cp ('t276');
6417                ## As if </optgroup>
6418                pop @{$self->{open_elements}};
6419              } else {
6420                !!!cp ('t277');
6421              }
6422    
6423              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6424              !!!nack ('t277.1');
6425              !!!next-token;
6426              next B;
6427            } elsif ({
6428                       select => 1, input => 1, textarea => 1,
6429                     }->{$token->{tag_name}} or
6430                     ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6431                      {
6432                       caption => 1, table => 1,
6433                       tbody => 1, tfoot => 1, thead => 1,
6434                       tr => 1, td => 1, th => 1,
6435                      }->{$token->{tag_name}})) {
6436              ## TODO: The type below is not good - <select> is replaced by </select>
6437              !!!parse-error (type => 'not closed', text => 'select',
6438                              token => $token);
6439              ## NOTE: As if the token were </select> (<select> case) or
6440              ## as if there were </select> (otherwise).
6441              ## have an element in table scope
6442              my $i;
6443              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6444                my $node = $self->{open_elements}->[$_];
6445                if ($node->[1] == SELECT_EL) {
6446                  !!!cp ('t278');
6447                  $i = $_;
6448                  last INSCOPE;
6449                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6450                  !!!cp ('t279');
6451                  last INSCOPE;
6452                }
6453              } # INSCOPE
6454              unless (defined $i) {
6455                !!!cp ('t280');
6456                !!!parse-error (type => 'unmatched end tag',
6457                                text => 'select', token => $token);
6458                ## Ignore the token
6459                !!!nack ('t280.1');
6460              !!!next-token;              !!!next-token;
6461              redo B;              next B;
6462            } elsif ($token->{type} eq 'comment') {            }
6463              ## NOTE: This is a code clone of "comment in body".                
6464              my $comment = $self->{document}->create_comment ($token->{data});            !!!cp ('t281');
6465              $self->{open_elements}->[-1]->[0]->append_child ($comment);            splice @{$self->{open_elements}}, $i;
6466    
6467              $self->_reset_insertion_mode;
6468    
6469              if ($token->{tag_name} eq 'select') {
6470                !!!nack ('t281.2');
6471              !!!next-token;              !!!next-token;
6472              redo B;              next B;
6473            } elsif ($token->{type} eq 'start tag') {            } else {
6474              if ({              !!!cp ('t281.1');
6475                   caption => 1, col => 1, colgroup => 1,              !!!ack-later;
6476                   tbody => 1, td => 1, tfoot => 1, th => 1,              ## Reprocess the token.
6477                   thead => 1, tr => 1,              next B;
6478                  }->{$token->{tag_name}}) {            }
6479                ## have an element in table scope          } else {
6480                my $tn;            !!!cp ('t282');
6481                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            !!!parse-error (type => 'in select',
6482                  my $node = $self->{open_elements}->[$_];                            text => $token->{tag_name}, token => $token);
6483                  if ($node->[1] eq 'td' or $node->[1] eq 'th') {            ## Ignore the token
6484                    $tn = $node->[1];            !!!nack ('t282.1');
6485                    last INSCOPE;            !!!next-token;
6486                  } elsif ({            next B;
6487                            table => 1, html => 1,          }
6488                           }->{$node->[1]}) {        } elsif ($token->{type} == END_TAG_TOKEN) {
6489                    last INSCOPE;          if ($token->{tag_name} eq 'optgroup') {
6490                  }            if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6491                } # INSCOPE                $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6492                unless (defined $tn) {              !!!cp ('t283');
6493                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              ## As if </option>
6494                  ## Ignore the token              splice @{$self->{open_elements}}, -2;
6495                  !!!next-token;            } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6496                  redo B;              !!!cp ('t284');
6497                }              pop @{$self->{open_elements}};
6498              } else {
6499                !!!cp ('t285');
6500                !!!parse-error (type => 'unmatched end tag',
6501                                text => $token->{tag_name}, token => $token);
6502                ## Ignore the token
6503              }
6504              !!!nack ('t285.1');
6505              !!!next-token;
6506              next B;
6507            } elsif ($token->{tag_name} eq 'option') {
6508              if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6509                !!!cp ('t286');
6510                pop @{$self->{open_elements}};
6511              } else {
6512                !!!cp ('t287');
6513                !!!parse-error (type => 'unmatched end tag',
6514                                text => $token->{tag_name}, token => $token);
6515                ## Ignore the token
6516              }
6517              !!!nack ('t287.1');
6518              !!!next-token;
6519              next B;
6520            } elsif ($token->{tag_name} eq 'select') {
6521              ## have an element in table scope
6522              my $i;
6523              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6524                my $node = $self->{open_elements}->[$_];
6525                if ($node->[1] == SELECT_EL) {
6526                  !!!cp ('t288');
6527                  $i = $_;
6528                  last INSCOPE;
6529                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6530                  !!!cp ('t289');
6531                  last INSCOPE;
6532                }
6533              } # INSCOPE
6534              unless (defined $i) {
6535                !!!cp ('t290');
6536                !!!parse-error (type => 'unmatched end tag',
6537                                text => $token->{tag_name}, token => $token);
6538                ## Ignore the token
6539                !!!nack ('t290.1');
6540                !!!next-token;
6541                next B;
6542              }
6543                  
6544              !!!cp ('t291');
6545              splice @{$self->{open_elements}}, $i;
6546    
6547                ## Close the cell            $self->_reset_insertion_mode;
6548                !!!back-token; # <?>  
6549                $token = {type => 'end tag', tag_name => $tn};            !!!nack ('t291.1');
6550                redo B;            !!!next-token;
6551              } else {            next B;
6552                #          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6553                     {
6554                      caption => 1, table => 1, tbody => 1,
6555                      tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6556                     }->{$token->{tag_name}}) {
6557    ## TODO: The following is wrong?
6558              !!!parse-error (type => 'unmatched end tag',
6559                              text => $token->{tag_name}, token => $token);
6560                  
6561              ## have an element in table scope
6562              my $i;
6563              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6564                my $node = $self->{open_elements}->[$_];
6565                if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6566                  !!!cp ('t292');
6567                  $i = $_;
6568                  last INSCOPE;
6569                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6570                  !!!cp ('t293');
6571                  last INSCOPE;
6572              }              }
6573            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6574              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {            unless (defined $i) {
6575                ## have an element in table scope              !!!cp ('t294');
6576                my $i;              ## Ignore the token
6577                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!nack ('t294.1');
6578                  my $node = $self->{open_elements}->[$_];              !!!next-token;
6579                  if ($node->[1] eq $token->{tag_name}) {              next B;
6580                    $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;  
               }  
6581                                
6582                ## generate implied end tags            ## As if </select>
6583                if ({            ## have an element in table scope
6584                     dd => 1, dt => 1, li => 1, p => 1,            undef $i;
6585                     td => ($token->{tag_name} eq 'th'),            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6586                     th => ($token->{tag_name} eq 'td'),              my $node = $self->{open_elements}->[$_];
6587                     tr => 1,              if ($node->[1] == SELECT_EL) {
6588                     tbody => 1, tfoot=> 1, thead => 1,                !!!cp ('t295');
6589                    }->{$self->{open_elements}->[-1]->[1]}) {                $i = $_;
6590                  !!!back-token;                last INSCOPE;
6591                  $token = {type => 'end tag',              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6592                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  ## ISSUE: Can this state be reached?
6593                  redo B;                !!!cp ('t296');
6594                }                last INSCOPE;
6595                }
6596              } # INSCOPE
6597              unless (defined $i) {
6598                !!!cp ('t297');
6599    ## TODO: The following error type is correct?
6600                !!!parse-error (type => 'unmatched end tag',
6601                                text => 'select', token => $token);
6602                ## Ignore the </select> token
6603                !!!nack ('t297.1');
6604                !!!next-token; ## TODO: ok?
6605                next B;
6606              }
6607                  
6608              !!!cp ('t298');
6609              splice @{$self->{open_elements}}, $i;
6610    
6611                if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            $self->_reset_insertion_mode;
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
6612    
6613                splice @{$self->{open_elements}}, $i;            !!!ack-later;
6614              ## reprocess
6615              next B;
6616            } else {
6617              !!!cp ('t299');
6618              !!!parse-error (type => 'in select:/',
6619                              text => $token->{tag_name}, token => $token);
6620              ## Ignore the token
6621              !!!nack ('t299.3');
6622              !!!next-token;
6623              next B;
6624            }
6625          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6626            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6627                    @{$self->{open_elements}} == 1) { # redundant, maybe
6628              !!!cp ('t299.1');
6629              !!!parse-error (type => 'in body:#eof', token => $token);
6630            } else {
6631              !!!cp ('t299.2');
6632            }
6633    
6634                $clear_up_to_marker->();          ## Stop parsing.
6635            last B;
6636          } else {
6637            die "$0: $token->{type}: Unknown token type";
6638          }
6639        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6640          if ($token->{type} == CHARACTER_TOKEN) {
6641            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6642              my $data = $1;
6643              ## As if in body
6644              $reconstruct_active_formatting_elements->($insert_to_current);
6645                  
6646              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6647              
6648              unless (length $token->{data}) {
6649                !!!cp ('t300');
6650                !!!next-token;
6651                next B;
6652              }
6653            }
6654            
6655            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6656              !!!cp ('t301');
6657              !!!parse-error (type => 'after html:#text', token => $token);
6658              #
6659            } else {
6660              !!!cp ('t302');
6661              ## "after body" insertion mode
6662              !!!parse-error (type => 'after body:#text', token => $token);
6663              #
6664            }
6665    
6666                $self->{insertion_mode} = 'in row';          $self->{insertion_mode} = IN_BODY_IM;
6667            ## reprocess
6668            next B;
6669          } elsif ($token->{type} == START_TAG_TOKEN) {
6670            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6671              !!!cp ('t303');
6672              !!!parse-error (type => 'after html',
6673                              text => $token->{tag_name}, token => $token);
6674              #
6675            } else {
6676              !!!cp ('t304');
6677              ## "after body" insertion mode
6678              !!!parse-error (type => 'after body',
6679                              text => $token->{tag_name}, token => $token);
6680              #
6681            }
6682    
6683                !!!next-token;          $self->{insertion_mode} = IN_BODY_IM;
6684                redo B;          !!!ack-later;
6685              } elsif ({          ## reprocess
6686                        body => 1, caption => 1, col => 1,          next B;
6687                        colgroup => 1, html => 1,        } elsif ($token->{type} == END_TAG_TOKEN) {
6688                       }->{$token->{tag_name}}) {          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6689                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t305');
6690                ## Ignore the token            !!!parse-error (type => 'after html:/',
6691                !!!next-token;                            text => $token->{tag_name}, token => $token);
6692                redo B;            
6693              } elsif ({            $self->{insertion_mode} = IN_BODY_IM;
6694                        table => 1, tbody => 1, tfoot => 1,            ## Reprocess.
6695                        thead => 1, tr => 1,            next B;
6696                       }->{$token->{tag_name}}) {          } else {
6697                ## have an element in table scope            !!!cp ('t306');
6698                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;  
               }  
6699    
6700                ## Close the cell          ## "after body" insertion mode
6701                !!!back-token; # </?>          if ($token->{tag_name} eq 'html') {
6702                $token = {type => 'end tag', tag_name => $tn};            if (defined $self->{inner_html_node}) {
6703                redo B;              !!!cp ('t307');
6704              } else {              !!!parse-error (type => 'unmatched end tag',
6705                #                              text => 'html', token => $token);
6706              }              ## Ignore the token
6707                !!!next-token;
6708                next B;
6709            } else {            } else {
6710              #              !!!cp ('t308');
6711                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6712                !!!next-token;
6713                next B;
6714            }            }
6715            } else {
6716              !!!cp ('t309');
6717              !!!parse-error (type => 'after body:/',
6718                              text => $token->{tag_name}, token => $token);
6719    
6720              $self->{insertion_mode} = IN_BODY_IM;
6721              ## reprocess
6722              next B;
6723            }
6724          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6725            !!!cp ('t309.2');
6726            ## Stop parsing
6727            last B;
6728          } else {
6729            die "$0: $token->{type}: Unknown token type";
6730          }
6731        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6732          if ($token->{type} == CHARACTER_TOKEN) {
6733            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6734              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6735                        
6736            $in_body->($insert_to_current);            unless (length $token->{data}) {
6737            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});  
6738              !!!next-token;              !!!next-token;
6739              redo B;              next B;
6740            } elsif ($token->{type} eq 'comment') {            }
6741              my $comment = $self->{document}->create_comment ($token->{data});          }
6742              $self->{open_elements}->[-1]->[0]->append_child ($comment);          
6743            if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6744              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6745                !!!cp ('t311');
6746                !!!parse-error (type => 'in frameset:#text', token => $token);
6747              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6748                !!!cp ('t312');
6749                !!!parse-error (type => 'after frameset:#text', token => $token);
6750              } else { # "after after frameset"
6751                !!!cp ('t313');
6752                !!!parse-error (type => 'after html:#text', token => $token);
6753              }
6754              
6755              ## Ignore the token.
6756              if (length $token->{data}) {
6757                !!!cp ('t314');
6758                ## reprocess the rest of characters
6759              } else {
6760                !!!cp ('t315');
6761              !!!next-token;              !!!next-token;
6762              redo B;            }
6763            } elsif ($token->{type} eq 'start tag') {            next B;
6764              if ($token->{tag_name} eq 'option') {          }
6765                if ($self->{open_elements}->[-1]->[1] eq 'option') {          
6766                  ## As if </option>          die qq[$0: Character "$token->{data}"];
6767                  pop @{$self->{open_elements}};        } elsif ($token->{type} == START_TAG_TOKEN) {
6768                }          if ($token->{tag_name} eq 'frameset' and
6769                $self->{insertion_mode} == IN_FRAMESET_IM) {
6770                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!cp ('t318');
6771                !!!next-token;            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6772                redo B;            !!!nack ('t318.1');
6773              } elsif ($token->{tag_name} eq 'optgroup') {            !!!next-token;
6774                if ($self->{open_elements}->[-1]->[1] eq 'option') {            next B;
6775                  ## As if </option>          } elsif ($token->{tag_name} eq 'frame' and
6776                  pop @{$self->{open_elements}};                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6777                }            !!!cp ('t319');
6778              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6779              pop @{$self->{open_elements}};
6780              !!!ack ('t319.1');
6781              !!!next-token;
6782              next B;
6783            } elsif ($token->{tag_name} eq 'noframes') {
6784              !!!cp ('t320');
6785              ## NOTE: As if in head.
6786              $parse_rcdata->(CDATA_CONTENT_MODEL);
6787              next B;
6788    
6789                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6790                  ## As if </optgroup>            ## has no parse error.
6791                  pop @{$self->{open_elements}};          } else {
6792                }            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6793                !!!cp ('t321');
6794                !!!parse-error (type => 'in frameset',
6795                                text => $token->{tag_name}, token => $token);
6796              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6797                !!!cp ('t322');
6798                !!!parse-error (type => 'after frameset',
6799                                text => $token->{tag_name}, token => $token);
6800              } else { # "after after frameset"
6801                !!!cp ('t322.2');
6802                !!!parse-error (type => 'after after frameset',
6803                                text => $token->{tag_name}, token => $token);
6804              }
6805              ## Ignore the token
6806              !!!nack ('t322.1');
6807              !!!next-token;
6808              next B;
6809            }
6810          } elsif ($token->{type} == END_TAG_TOKEN) {
6811            if ($token->{tag_name} eq 'frameset' and
6812                $self->{insertion_mode} == IN_FRAMESET_IM) {
6813              if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6814                  @{$self->{open_elements}} == 1) {
6815                !!!cp ('t325');
6816                !!!parse-error (type => 'unmatched end tag',
6817                                text => $token->{tag_name}, token => $token);
6818                ## Ignore the token
6819                !!!next-token;
6820              } else {
6821                !!!cp ('t326');
6822                pop @{$self->{open_elements}};
6823                !!!next-token;
6824              }
6825    
6826                !!!insert-element ($token->{tag_name}, $token->{attributes});            if (not defined $self->{inner_html_node} and
6827                !!!next-token;                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
6828                redo B;              !!!cp ('t327');
6829              } elsif ($token->{tag_name} eq 'select') {              $self->{insertion_mode} = AFTER_FRAMESET_IM;
6830                !!!parse-error (type => 'not closed:select');            } else {
6831                ## As if </select> instead              !!!cp ('t328');
6832                ## have an element in table scope            }
6833                my $i;            next B;
6834                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {          } elsif ($token->{tag_name} eq 'html' and
6835                  my $node = $self->{open_elements}->[$_];                   $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6836                  if ($node->[1] eq $token->{tag_name}) {            !!!cp ('t329');
6837                    $i = $_;            $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6838                    last INSCOPE;            !!!next-token;
6839                  } elsif ({            next B;
6840                            table => 1, html => 1,          } else {
6841                           }->{$node->[1]}) {            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6842                    last INSCOPE;              !!!cp ('t330');
6843                  }              !!!parse-error (type => 'in frameset:/',
6844                } # INSCOPE                              text => $token->{tag_name}, token => $token);
6845                unless (defined $i) {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6846                  !!!parse-error (type => 'unmatched end tag:select');              !!!cp ('t330.1');
6847                  ## Ignore the token              !!!parse-error (type => 'after frameset:/',
6848                  !!!next-token;                              text => $token->{tag_name}, token => $token);
6849                  redo B;            } else { # "after after html"
6850                }              !!!cp ('t331');
6851                              !!!parse-error (type => 'after after frameset:/',
6852                splice @{$self->{open_elements}}, $i;                              text => $token->{tag_name}, token => $token);
6853              }
6854              ## Ignore the token
6855              !!!next-token;
6856              next B;
6857            }
6858          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6859            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6860                    @{$self->{open_elements}} == 1) { # redundant, maybe
6861              !!!cp ('t331.1');
6862              !!!parse-error (type => 'in body:#eof', token => $token);
6863            } else {
6864              !!!cp ('t331.2');
6865            }
6866            
6867            ## Stop parsing
6868            last B;
6869          } else {
6870            die "$0: $token->{type}: Unknown token type";
6871          }
6872        } else {
6873          die "$0: $self->{insertion_mode}: Unknown insertion mode";
6874        }
6875    
6876                $self->_reset_insertion_mode;      ## "in body" insertion mode
6877        if ($token->{type} == START_TAG_TOKEN) {
6878          if ($token->{tag_name} eq 'script') {
6879            !!!cp ('t332');
6880            ## NOTE: This is an "as if in head" code clone
6881            $script_start_tag->();
6882            next B;
6883          } elsif ($token->{tag_name} eq 'style') {
6884            !!!cp ('t333');
6885            ## NOTE: This is an "as if in head" code clone
6886            $parse_rcdata->(CDATA_CONTENT_MODEL);
6887            next B;
6888          } elsif ({
6889                    base => 1, command => 1, eventsource => 1, link => 1,
6890                   }->{$token->{tag_name}}) {
6891            !!!cp ('t334');
6892            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6893            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6894            pop @{$self->{open_elements}};
6895            !!!ack ('t334.1');
6896            !!!next-token;
6897            next B;
6898          } elsif ($token->{tag_name} eq 'meta') {
6899            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6900            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6901            my $meta_el = pop @{$self->{open_elements}};
6902    
6903                !!!next-token;          unless ($self->{confident}) {
6904                redo B;            if ($token->{attributes}->{charset}) {
6905              } else {              !!!cp ('t335');
6906                #              ## NOTE: Whether the encoding is supported or not is handled
6907                ## in the {change_encoding} callback.
6908                $self->{change_encoding}
6909                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6910                
6911                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6912                    ->set_user_data (manakai_has_reference =>
6913                                         $token->{attributes}->{charset}
6914                                             ->{has_reference});
6915              } elsif ($token->{attributes}->{content}) {
6916                if ($token->{attributes}->{content}->{value}
6917                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6918                        [\x09\x0A\x0C\x0D\x20]*=
6919                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6920                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6921                       /x) {
6922                  !!!cp ('t336');
6923                  ## NOTE: Whether the encoding is supported or not is handled
6924                  ## in the {change_encoding} callback.
6925                  $self->{change_encoding}
6926                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6927                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6928                      ->set_user_data (manakai_has_reference =>
6929                                           $token->{attributes}->{content}
6930                                                 ->{has_reference});
6931              }              }
6932            } elsif ($token->{type} eq 'end tag') {            }
6933              if ($token->{tag_name} eq 'optgroup') {          } else {
6934                if ($self->{open_elements}->[-1]->[1] eq 'option' and            if ($token->{attributes}->{charset}) {
6935                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!cp ('t337');
6936                  ## As if </option>              $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6937                  splice @{$self->{open_elements}}, -2;                  ->set_user_data (manakai_has_reference =>
6938                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {                                       $token->{attributes}->{charset}
6939                  pop @{$self->{open_elements}};                                           ->{has_reference});
6940                } else {            }
6941                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            if ($token->{attributes}->{content}) {
6942                  ## Ignore the token              !!!cp ('t338');
6943                }              $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6944                !!!next-token;                  ->set_user_data (manakai_has_reference =>
6945                redo B;                                       $token->{attributes}->{content}
6946              } elsif ($token->{tag_name} eq 'option') {                                           ->{has_reference});
6947                if ($self->{open_elements}->[-1]->[1] eq 'option') {            }
6948                  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;  
6949    
6950                !!!next-token;          !!!ack ('t338.1');
6951                redo B;          !!!next-token;
6952              } elsif ({          next B;
6953                        caption => 1, table => 1, tbody => 1,        } elsif ($token->{tag_name} eq 'title') {
6954                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,          !!!cp ('t341');
6955                       }->{$token->{tag_name}}) {          ## NOTE: This is an "as if in head" code clone
6956                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          $parse_rcdata->(RCDATA_CONTENT_MODEL);
6957                          next B;
6958                ## have an element in table scope        } elsif ($token->{tag_name} eq 'body') {
6959                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;  
               }  
6960                                
6961                splice @{$self->{open_elements}}, $i;          if (@{$self->{open_elements}} == 1 or
6962                not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
6963              !!!cp ('t342');
6964              ## Ignore the token
6965            } else {
6966              my $body_el = $self->{open_elements}->[1]->[0];
6967              for my $attr_name (keys %{$token->{attributes}}) {
6968                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6969                  !!!cp ('t343');
6970                  $body_el->set_attribute_ns
6971                    (undef, [undef, $attr_name],
6972                     $token->{attributes}->{$attr_name}->{value});
6973                }
6974              }
6975            }
6976            !!!nack ('t343.1');
6977            !!!next-token;
6978            next B;
6979          } elsif ({
6980                    ## NOTE: Start tags for non-phrasing flow content elements
6981    
6982                $self->_reset_insertion_mode;                  ## NOTE: The normal one
6983                    address => 1, article => 1, aside => 1, blockquote => 1,
6984                    center => 1, datagrid => 1, details => 1, dialog => 1,
6985                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6986                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6987                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6988                    section => 1, ul => 1,
6989                    ## NOTE: As normal, but drops leading newline
6990                    pre => 1, listing => 1,
6991                    ## NOTE: As normal, but interacts with the form element pointer
6992                    form => 1,
6993                    
6994                    table => 1,
6995                    hr => 1,
6996                   }->{$token->{tag_name}}) {
6997            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
6998              !!!cp ('t350');
6999              !!!parse-error (type => 'in form:form', token => $token);
7000              ## Ignore the token
7001              !!!nack ('t350.1');
7002              !!!next-token;
7003              next B;
7004            }
7005    
7006                ## reprocess          ## has a p element in scope
7007                redo B;          INSCOPE: for (reverse @{$self->{open_elements}}) {
7008              if ($_->[1] == P_EL) {
7009                !!!cp ('t344');
7010                !!!back-token; # <form>
7011                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7012                          line => $token->{line}, column => $token->{column}};
7013                next B;
7014              } elsif ($_->[1] & SCOPING_EL) {
7015                !!!cp ('t345');
7016                last INSCOPE;
7017              }
7018            } # INSCOPE
7019              
7020            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7021            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7022              !!!nack ('t346.1');
7023              !!!next-token;
7024              if ($token->{type} == CHARACTER_TOKEN) {
7025                $token->{data} =~ s/^\x0A//;
7026                unless (length $token->{data}) {
7027                  !!!cp ('t346');
7028                  !!!next-token;
7029              } else {              } else {
7030                #                !!!cp ('t349');
7031              }              }
7032            } else {            } else {
7033              #              !!!cp ('t348');
7034            }            }
7035            } elsif ($token->{tag_name} eq 'form') {
7036              !!!cp ('t347.1');
7037              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7038    
7039            !!!parse-error (type => 'in select:'.$token->{tag_name});            !!!nack ('t347.2');
           ## Ignore the token  
7040            !!!next-token;            !!!next-token;
7041            redo B;          } elsif ($token->{tag_name} eq 'table') {
7042          } elsif ($self->{insertion_mode} eq 'after body') {            !!!cp ('t382');
7043            if ($token->{type} eq 'character') {            push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7044              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            
7045                my $data = $1;            $self->{insertion_mode} = IN_TABLE_IM;
               ## As if in body  
               $reconstruct_active_formatting_elements->($insert_to_current);  
                 
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
7046    
7047                unless (length $token->{data}) {            !!!nack ('t382.1');
7048                  !!!next-token;            !!!next-token;
7049                  redo B;          } elsif ($token->{tag_name} eq 'hr') {
7050                }            !!!cp ('t386');
7051              }            pop @{$self->{open_elements}};
7052                        
7053              #            !!!nack ('t386.1');
7054              !!!parse-error (type => 'after body:#'.$token->{type});            !!!next-token;
7055            } elsif ($token->{type} eq 'comment') {          } else {
7056              my $comment = $self->{document}->create_comment ($token->{data});            !!!nack ('t347.1');
7057              $self->{open_elements}->[0]->[0]->append_child ($comment);            !!!next-token;
7058              !!!next-token;          }
7059              redo B;          next B;
7060            } elsif ($token->{type} eq 'start tag') {        } elsif ($token->{tag_name} eq 'li') {
7061              !!!parse-error (type => 'after body:'.$token->{tag_name});          ## NOTE: As normal, but imply </li> when there's another <li> ...
7062              #  
7063            } elsif ($token->{type} eq 'end tag') {          ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7064              if ($token->{tag_name} eq 'html') {            ## Interpreted as <li><foo/></li><li/> (non-conforming)
7065                if (defined $self->{inner_html_node}) {            ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7066                  !!!parse-error (type => 'unmatched end tag:html');            ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7067                  ## Ignore the token            ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7068                  !!!next-token;            ## object (Fx)
7069                  redo B;            ## Generate non-tree (non-conforming)
7070              ## basefont (IE7 (where basefont is non-void)), center (IE),
7071              ## form (IE), hn (IE)
7072            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7073              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7074              ## div (Fx, S)
7075    
7076            my $non_optional;
7077            my $i = -1;
7078    
7079            ## 1.
7080            for my $node (reverse @{$self->{open_elements}}) {
7081              if ($node->[1] == LI_EL) {
7082                ## 2. (a) As if </li>
7083                {
7084                  ## If no </li> - not applied
7085                  #
7086    
7087                  ## Otherwise
7088    
7089                  ## 1. generate implied end tags, except for </li>
7090                  #
7091    
7092                  ## 2. If current node != "li", parse error
7093                  if ($non_optional) {
7094                    !!!parse-error (type => 'not closed',
7095                                    text => $non_optional->[0]->manakai_local_name,
7096                                    token => $token);
7097                    !!!cp ('t355');
7098                } else {                } else {
7099                  $previous_insertion_mode = $self->{insertion_mode};                  !!!cp ('t356');
                 $self->{insertion_mode} = 'trailing end';  
                 !!!next-token;  
                 redo B;  
7100                }                }
7101              } else {  
7102                !!!parse-error (type => 'after body:/'.$token->{tag_name});                ## 3. Pop
7103                  splice @{$self->{open_elements}}, $i;
7104              }              }
7105    
7106                last; ## 2. (b) goto 5.
7107              } elsif (
7108                       ## NOTE: not "formatting" and not "phrasing"
7109                       ($node->[1] & SPECIAL_EL or
7110                        $node->[1] & SCOPING_EL) and
7111                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7112                       (not $node->[1] & ADDRESS_DIV_P_EL)
7113                      ) {
7114                ## 3.
7115                !!!cp ('t357');
7116                last; ## goto 5.
7117              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7118                !!!cp ('t358');
7119                #
7120            } else {            } else {
7121              !!!parse-error (type => 'after body:#'.$token->{type});              !!!cp ('t359');
7122                $non_optional ||= $node;
7123                #
7124            }            }
7125              ## 4.
7126              ## goto 2.
7127              $i--;
7128            }
7129    
7130            $self->{insertion_mode} = 'in body';          ## 5. (a) has a |p| element in scope
7131            ## reprocess          INSCOPE: for (reverse @{$self->{open_elements}}) {
7132            redo B;            if ($_->[1] == P_EL) {
7133          } 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 ($1);  
7134    
7135                unless (length $token->{data}) {              ## NOTE: |<p><li>|, for example.
                 !!!next-token;  
                 redo B;  
               }  
             }  
7136    
7137              #              !!!back-token; # <x>
7138            } elsif ($token->{type} eq 'comment') {              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7139              my $comment = $self->{document}->create_comment ($token->{data});                        line => $token->{line}, column => $token->{column}};
7140              $self->{open_elements}->[-1]->[0]->append_child ($comment);              next B;
7141              !!!next-token;            } elsif ($_->[1] & SCOPING_EL) {
7142              redo B;              !!!cp ('t354');
7143            } elsif ($token->{type} eq 'start tag') {              last INSCOPE;
7144              if ($token->{tag_name} eq 'frameset') {            }
7145                !!!insert-element ($token->{tag_name}, $token->{attributes});          } # INSCOPE
7146                !!!next-token;  
7147                redo B;          ## 5. (b) insert
7148              } elsif ($token->{tag_name} eq 'frame') {          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7149                !!!insert-element ($token->{tag_name}, $token->{attributes});          !!!nack ('t359.1');
7150                pop @{$self->{open_elements}};          !!!next-token;
7151                !!!next-token;          next B;
7152                redo B;        } elsif ($token->{tag_name} eq 'dt' or
7153              } elsif ($token->{tag_name} eq 'noframes') {                 $token->{tag_name} eq 'dd') {
7154                $in_body->($insert_to_current);          ## NOTE: As normal, but imply </dt> or </dd> when ...
7155                redo B;  
7156              } else {          my $non_optional;
7157            my $i = -1;
7158    
7159            ## 1.
7160            for my $node (reverse @{$self->{open_elements}}) {
7161              if ($node->[1] == DTDD_EL) {
7162                ## 2. (a) As if </li>
7163                {
7164                  ## If no </li> - not applied
7165                #                #
7166              }  
7167            } elsif ($token->{type} eq 'end tag') {                ## Otherwise
7168              if ($token->{tag_name} eq 'frameset') {  
7169                if ($self->{open_elements}->[-1]->[1] eq 'html' and                ## 1. generate implied end tags, except for </dt> or </dd>
7170                    @{$self->{open_elements}} == 1) {                #
7171                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
7172                  ## Ignore the token                ## 2. If current node != "dt"|"dd", parse error
7173                  !!!next-token;                if ($non_optional) {
7174                    !!!parse-error (type => 'not closed',
7175                                    text => $non_optional->[0]->manakai_local_name,
7176                                    token => $token);
7177                    !!!cp ('t355.1');
7178                } else {                } else {
7179                  pop @{$self->{open_elements}};                  !!!cp ('t356.1');
                 !!!next-token;  
7180                }                }
7181                  
7182                ## if not inner_html and                ## 3. Pop
7183                if ($self->{open_elements}->[-1]->[1] ne 'frameset') {                splice @{$self->{open_elements}}, $i;
                 $self->{insertion_mode} = 'after frameset';  
               }  
               redo B;  
             } else {  
               #  
7184              }              }
7185    
7186                last; ## 2. (b) goto 5.
7187              } elsif (
7188                       ## NOTE: not "formatting" and not "phrasing"
7189                       ($node->[1] & SPECIAL_EL or
7190                        $node->[1] & SCOPING_EL) and
7191                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7192    
7193                       (not $node->[1] & ADDRESS_DIV_P_EL)
7194                      ) {
7195                ## 3.
7196                !!!cp ('t357.1');
7197                last; ## goto 5.
7198              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7199                !!!cp ('t358.1');
7200                #
7201            } else {            } else {
7202                !!!cp ('t359.1');
7203                $non_optional ||= $node;
7204              #              #
7205            }            }
7206              ## 4.
7207              ## goto 2.
7208              $i--;
7209            }
7210    
7211            ## 5. (a) has a |p| element in scope
7212            INSCOPE: for (reverse @{$self->{open_elements}}) {
7213              if ($_->[1] == P_EL) {
7214                !!!cp ('t353.1');
7215                !!!back-token; # <x>
7216                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7217                          line => $token->{line}, column => $token->{column}};
7218                next B;
7219              } elsif ($_->[1] & SCOPING_EL) {
7220                !!!cp ('t354.1');
7221                last INSCOPE;
7222              }
7223            } # INSCOPE
7224    
7225            ## 5. (b) insert
7226            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7227            !!!nack ('t359.2');
7228            !!!next-token;
7229            next B;
7230          } elsif ($token->{tag_name} eq 'plaintext') {
7231            ## NOTE: As normal, but effectively ends parsing
7232    
7233            ## has a p element in scope
7234            INSCOPE: for (reverse @{$self->{open_elements}}) {
7235              if ($_->[1] == P_EL) {
7236                !!!cp ('t367');
7237                !!!back-token; # <plaintext>
7238                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7239                          line => $token->{line}, column => $token->{column}};
7240                next B;
7241              } elsif ($_->[1] & SCOPING_EL) {
7242                !!!cp ('t368');
7243                last INSCOPE;
7244              }
7245            } # INSCOPE
7246                        
7247            if (defined $token->{tag_name}) {          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7248              !!!parse-error (type => 'in frameset:'.($token->{type} eq 'end tag' ? '/' : '').$token->{tag_name});            
7249            } else {          $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7250              !!!parse-error (type => 'in frameset:#'.$token->{type});            
7251            !!!nack ('t368.1');
7252            !!!next-token;
7253            next B;
7254          } elsif ($token->{tag_name} eq 'a') {
7255            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7256              my $node = $active_formatting_elements->[$i];
7257              if ($node->[1] == A_EL) {
7258                !!!cp ('t371');
7259                !!!parse-error (type => 'in a:a', token => $token);
7260                
7261                !!!back-token; # <a>
7262                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7263                          line => $token->{line}, column => $token->{column}};
7264                $formatting_end_tag->($token);
7265                
7266                AFE2: for (reverse 0..$#$active_formatting_elements) {
7267                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7268                    !!!cp ('t372');
7269                    splice @$active_formatting_elements, $_, 1;
7270                    last AFE2;
7271                  }
7272                } # AFE2
7273                OE: for (reverse 0..$#{$self->{open_elements}}) {
7274                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7275                    !!!cp ('t373');
7276                    splice @{$self->{open_elements}}, $_, 1;
7277                    last OE;
7278                  }
7279                } # OE
7280                last AFE;
7281              } elsif ($node->[0] eq '#marker') {
7282                !!!cp ('t374');
7283                last AFE;
7284            }            }
7285            } # AFE
7286              
7287            $reconstruct_active_formatting_elements->($insert_to_current);
7288    
7289            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7290            push @$active_formatting_elements, $self->{open_elements}->[-1];
7291    
7292            !!!nack ('t374.1');
7293            !!!next-token;
7294            next B;
7295          } elsif ($token->{tag_name} eq 'nobr') {
7296            $reconstruct_active_formatting_elements->($insert_to_current);
7297    
7298            ## has a |nobr| element in scope
7299            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7300              my $node = $self->{open_elements}->[$_];
7301              if ($node->[1] == NOBR_EL) {
7302                !!!cp ('t376');
7303                !!!parse-error (type => 'in nobr:nobr', token => $token);
7304                !!!back-token; # <nobr>
7305                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7306                          line => $token->{line}, column => $token->{column}};
7307                next B;
7308              } elsif ($node->[1] & SCOPING_EL) {
7309                !!!cp ('t377');
7310                last INSCOPE;
7311              }
7312            } # INSCOPE
7313            
7314            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7315            push @$active_formatting_elements, $self->{open_elements}->[-1];
7316            
7317            !!!nack ('t377.1');
7318            !!!next-token;
7319            next B;
7320          } elsif ($token->{tag_name} eq 'button') {
7321            ## has a button element in scope
7322            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7323              my $node = $self->{open_elements}->[$_];
7324              if ($node->[1] == BUTTON_EL) {
7325                !!!cp ('t378');
7326                !!!parse-error (type => 'in button:button', token => $token);
7327                !!!back-token; # <button>
7328                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7329                          line => $token->{line}, column => $token->{column}};
7330                next B;
7331              } elsif ($node->[1] & SCOPING_EL) {
7332                !!!cp ('t379');
7333                last INSCOPE;
7334              }
7335            } # INSCOPE
7336              
7337            $reconstruct_active_formatting_elements->($insert_to_current);
7338              
7339            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7340    
7341            ## TODO: associate with $self->{form_element} if defined
7342    
7343            push @$active_formatting_elements, ['#marker', ''];
7344    
7345            !!!nack ('t379.1');
7346            !!!next-token;
7347            next B;
7348          } elsif ({
7349                    xmp => 1,
7350                    iframe => 1,
7351                    noembed => 1,
7352                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7353                    noscript => 0, ## TODO: 1 if scripting is enabled
7354                   }->{$token->{tag_name}}) {
7355            if ($token->{tag_name} eq 'xmp') {
7356              !!!cp ('t381');
7357              $reconstruct_active_formatting_elements->($insert_to_current);
7358            } else {
7359              !!!cp ('t399');
7360            }
7361            ## NOTE: There is an "as if in body" code clone.
7362            $parse_rcdata->(CDATA_CONTENT_MODEL);
7363            next B;
7364          } elsif ($token->{tag_name} eq 'isindex') {
7365            !!!parse-error (type => 'isindex', token => $token);
7366            
7367            if (defined $self->{form_element}) {
7368              !!!cp ('t389');
7369            ## Ignore the token            ## Ignore the token
7370              !!!nack ('t389'); ## NOTE: Not acknowledged.
7371            !!!next-token;            !!!next-token;
7372            redo B;            next B;
7373          } elsif ($self->{insertion_mode} eq 'after frameset') {          } else {
7374            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 ($1);  
7375    
7376                unless (length $token->{data}) {            my $at = $token->{attributes};
7377                  !!!next-token;            my $form_attrs;
7378                  redo B;            $form_attrs->{action} = $at->{action} if $at->{action};
7379                }            my $prompt_attr = $at->{prompt};
7380              }            $at->{name} = {name => 'name', value => 'isindex'};
7381              delete $at->{action};
7382              delete $at->{prompt};
7383              my @tokens = (
7384                            {type => START_TAG_TOKEN, tag_name => 'form',
7385                             attributes => $form_attrs,
7386                             line => $token->{line}, column => $token->{column}},
7387                            {type => START_TAG_TOKEN, tag_name => 'hr',
7388                             line => $token->{line}, column => $token->{column}},
7389                            {type => START_TAG_TOKEN, tag_name => 'p',
7390                             line => $token->{line}, column => $token->{column}},
7391                            {type => START_TAG_TOKEN, tag_name => 'label',
7392                             line => $token->{line}, column => $token->{column}},
7393                           );
7394              if ($prompt_attr) {
7395                !!!cp ('t390');
7396                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7397                               #line => $token->{line}, column => $token->{column},
7398                              };
7399              } else {
7400                !!!cp ('t391');
7401                push @tokens, {type => CHARACTER_TOKEN,
7402                               data => 'This is a searchable index. Insert your search keywords here: ',
7403                               #line => $token->{line}, column => $token->{column},
7404                              }; # SHOULD
7405                ## TODO: make this configurable
7406              }
7407              push @tokens,
7408                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7409                             line => $token->{line}, column => $token->{column}},
7410                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7411                            {type => END_TAG_TOKEN, tag_name => 'label',
7412                             line => $token->{line}, column => $token->{column}},
7413                            {type => END_TAG_TOKEN, tag_name => 'p',
7414                             line => $token->{line}, column => $token->{column}},
7415                            {type => START_TAG_TOKEN, tag_name => 'hr',
7416                             line => $token->{line}, column => $token->{column}},
7417                            {type => END_TAG_TOKEN, tag_name => 'form',
7418                             line => $token->{line}, column => $token->{column}};
7419              !!!back-token (@tokens);
7420              !!!next-token;
7421              next B;
7422            }
7423          } elsif ($token->{tag_name} eq 'textarea') {
7424            ## Step 1
7425            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7426            
7427            ## Step 2
7428            ## TODO: $self->{form_element} if defined
7429    
7430              if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {          ## Step 3
7431                !!!parse-error (type => 'after frameset:#character');          $self->{ignore_newline} = 1;
7432    
7433                ## Ignore the token.          ## Step 4
7434                if (length $token->{data}) {          ## ISSUE: This step is wrong. (r2302 enbugged)
7435                  ## reprocess the rest of characters  
7436                } else {          ## Step 5
7437                  !!!next-token;          $self->{content_model} = RCDATA_CONTENT_MODEL;
7438                }          delete $self->{escape}; # MUST
7439                redo B;  
7440            ## Step 6-7
7441            $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7442    
7443            !!!nack ('t392.1');
7444            !!!next-token;
7445            next B;
7446          } elsif ($token->{tag_name} eq 'optgroup' or
7447                   $token->{tag_name} eq 'option') {
7448            ## has an |option| element in scope
7449            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7450              my $node = $self->{open_elements}->[$_];
7451              if ($node->[1] == OPTION_EL) {
7452                !!!cp ('t397.1');
7453                ## NOTE: As if </option>
7454                !!!back-token; # <option> or <optgroup>
7455                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7456                          line => $token->{line}, column => $token->{column}};
7457                next B;
7458              } elsif ($node->[1] & SCOPING_EL) {
7459                !!!cp ('t397.2');
7460                last INSCOPE;
7461              }
7462            } # INSCOPE
7463    
7464            $reconstruct_active_formatting_elements->($insert_to_current);
7465    
7466            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7467    
7468            !!!nack ('t397.3');
7469            !!!next-token;
7470            redo B;
7471          } elsif ($token->{tag_name} eq 'rt' or
7472                   $token->{tag_name} eq 'rp') {
7473            ## has a |ruby| element in scope
7474            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7475              my $node = $self->{open_elements}->[$_];
7476              if ($node->[1] == RUBY_EL) {
7477                !!!cp ('t398.1');
7478                ## generate implied end tags
7479                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7480                  !!!cp ('t398.2');
7481                  pop @{$self->{open_elements}};
7482              }              }
7483            } elsif ($token->{type} eq 'comment') {              unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
7484              my $comment = $self->{document}->create_comment ($token->{data});                !!!cp ('t398.3');
7485              $self->{open_elements}->[-1]->[0]->append_child ($comment);                !!!parse-error (type => 'not closed',
7486              !!!next-token;                                text => $self->{open_elements}->[-1]->[0]
7487              redo B;                                    ->manakai_local_name,
7488            } elsif ($token->{type} eq 'start tag') {                                token => $token);
7489              if ($token->{tag_name} eq 'noframes') {                pop @{$self->{open_elements}}
7490                $in_body->($insert_to_current);                    while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
               redo B;  
             } else {  
               #  
7491              }              }
7492            } elsif ($token->{type} eq 'end tag') {              last INSCOPE;
7493              if ($token->{tag_name} eq 'html') {            } elsif ($node->[1] & SCOPING_EL) {
7494                $previous_insertion_mode = $self->{insertion_mode};              !!!cp ('t398.4');
7495                $self->{insertion_mode} = 'trailing end';              last INSCOPE;
7496                !!!next-token;            }
7497                redo B;          } # INSCOPE
7498              } else {  
7499                #          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7500    
7501            !!!nack ('t398.5');
7502            !!!next-token;
7503            redo B;
7504          } elsif ($token->{tag_name} eq 'math' or
7505                   $token->{tag_name} eq 'svg') {
7506            $reconstruct_active_formatting_elements->($insert_to_current);
7507    
7508            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7509    
7510            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7511    
7512            ## "adjust foreign attributes" - done in insert-element-f
7513            
7514            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7515            
7516            if ($self->{self_closing}) {
7517              pop @{$self->{open_elements}};
7518              !!!ack ('t398.6');
7519            } else {
7520              !!!cp ('t398.7');
7521              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7522              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7523              ## mode, "in body" (not "in foreign content") secondary insertion
7524              ## mode, maybe.
7525            }
7526    
7527            !!!next-token;
7528            next B;
7529          } elsif ({
7530                    caption => 1, col => 1, colgroup => 1, frame => 1,
7531                    frameset => 1, head => 1,
7532                    tbody => 1, td => 1, tfoot => 1, th => 1,
7533                    thead => 1, tr => 1,
7534                   }->{$token->{tag_name}}) {
7535            !!!cp ('t401');
7536            !!!parse-error (type => 'in body',
7537                            text => $token->{tag_name}, token => $token);
7538            ## Ignore the token
7539            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7540            !!!next-token;
7541            next B;
7542          } elsif ($token->{tag_name} eq 'param' or
7543                   $token->{tag_name} eq 'source') {
7544            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7545            pop @{$self->{open_elements}};
7546    
7547            !!!ack ('t398.5');
7548            !!!next-token;
7549            redo B;
7550          } else {
7551            if ($token->{tag_name} eq 'image') {
7552              !!!cp ('t384');
7553              !!!parse-error (type => 'image', token => $token);
7554              $token->{tag_name} = 'img';
7555            } else {
7556              !!!cp ('t385');
7557            }
7558    
7559            ## NOTE: There is an "as if <br>" code clone.
7560            $reconstruct_active_formatting_elements->($insert_to_current);
7561            
7562            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7563    
7564            if ({
7565                 applet => 1, marquee => 1, object => 1,
7566                }->{$token->{tag_name}}) {
7567              !!!cp ('t380');
7568              push @$active_formatting_elements, ['#marker', ''];
7569              !!!nack ('t380.1');
7570            } elsif ({
7571                      b => 1, big => 1, em => 1, font => 1, i => 1,
7572                      s => 1, small => 1, strike => 1,
7573                      strong => 1, tt => 1, u => 1,
7574                     }->{$token->{tag_name}}) {
7575              !!!cp ('t375');
7576              push @$active_formatting_elements, $self->{open_elements}->[-1];
7577              !!!nack ('t375.1');
7578            } elsif ($token->{tag_name} eq 'input') {
7579              !!!cp ('t388');
7580              ## TODO: associate with $self->{form_element} if defined
7581              pop @{$self->{open_elements}};
7582              !!!ack ('t388.2');
7583            } elsif ({
7584                      area => 1, basefont => 1, bgsound => 1, br => 1,
7585                      embed => 1, img => 1, spacer => 1, wbr => 1,
7586                     }->{$token->{tag_name}}) {
7587              !!!cp ('t388.1');
7588              pop @{$self->{open_elements}};
7589              !!!ack ('t388.3');
7590            } elsif ($token->{tag_name} eq 'select') {
7591              ## TODO: associate with $self->{form_element} if defined
7592            
7593              if ($self->{insertion_mode} & TABLE_IMS or
7594                  $self->{insertion_mode} & BODY_TABLE_IMS or
7595                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7596                !!!cp ('t400.1');
7597                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7598              } else {
7599                !!!cp ('t400.2');
7600                $self->{insertion_mode} = IN_SELECT_IM;
7601              }
7602              !!!nack ('t400.3');
7603            } else {
7604              !!!nack ('t402');
7605            }
7606            
7607            !!!next-token;
7608            next B;
7609          }
7610        } elsif ($token->{type} == END_TAG_TOKEN) {
7611          if ($token->{tag_name} eq 'body') {
7612            ## has a |body| element in scope
7613            my $i;
7614            INSCOPE: {
7615              for (reverse @{$self->{open_elements}}) {
7616                if ($_->[1] == BODY_EL) {
7617                  !!!cp ('t405');
7618                  $i = $_;
7619                  last INSCOPE;
7620                } elsif ($_->[1] & SCOPING_EL) {
7621                  !!!cp ('t405.1');
7622                  last;
7623              }              }
7624              }
7625    
7626              ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7627    
7628              !!!parse-error (type => 'unmatched end tag',
7629                              text => $token->{tag_name}, token => $token);
7630              ## NOTE: Ignore the token.
7631              !!!next-token;
7632              next B;
7633            } # INSCOPE
7634    
7635            for (@{$self->{open_elements}}) {
7636              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7637                !!!cp ('t403');
7638                !!!parse-error (type => 'not closed',
7639                                text => $_->[0]->manakai_local_name,
7640                                token => $token);
7641                last;
7642            } else {            } else {
7643              die "$0: $token->{type}: Unknown token type";              !!!cp ('t404');
7644            }            }
7645                      }
7646            !!!parse-error (type => 'after frameset:'.($token->{tag_name} eq 'end tag' ? '/' : '').$token->{tag_name});  
7647            $self->{insertion_mode} = AFTER_BODY_IM;
7648            !!!next-token;
7649            next B;
7650          } elsif ($token->{tag_name} eq 'html') {
7651            ## TODO: Update this code.  It seems that the code below is not
7652            ## up-to-date, though it has same effect as speced.
7653            if (@{$self->{open_elements}} > 1 and
7654                $self->{open_elements}->[1]->[1] == BODY_EL) {
7655              unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
7656                !!!cp ('t406');
7657                !!!parse-error (type => 'not closed',
7658                                text => $self->{open_elements}->[1]->[0]
7659                                    ->manakai_local_name,
7660                                token => $token);
7661              } else {
7662                !!!cp ('t407');
7663              }
7664              $self->{insertion_mode} = AFTER_BODY_IM;
7665              ## reprocess
7666              next B;
7667            } else {
7668              !!!cp ('t408');
7669              !!!parse-error (type => 'unmatched end tag',
7670                              text => $token->{tag_name}, token => $token);
7671            ## Ignore the token            ## Ignore the token
7672            !!!next-token;            !!!next-token;
7673            redo B;            next B;
7674            }
7675          } elsif ({
7676                    ## NOTE: End tags for non-phrasing flow content elements
7677    
7678                    ## NOTE: The normal ones
7679                    address => 1, article => 1, aside => 1, blockquote => 1,
7680                    center => 1, datagrid => 1, details => 1, dialog => 1,
7681                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7682                    footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7683                    ol => 1, pre => 1, section => 1, ul => 1,
7684    
7685                    ## NOTE: As normal, but ... optional tags
7686                    dd => 1, dt => 1, li => 1,
7687    
7688                    applet => 1, button => 1, marquee => 1, object => 1,
7689                   }->{$token->{tag_name}}) {
7690            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7691            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7692            ## </dd>" code.
7693    
7694            ## ISSUE: An issue in spec there          ## has an element in scope
7695            my $i;
7696            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7697              my $node = $self->{open_elements}->[$_];
7698              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7699                !!!cp ('t410');
7700                $i = $_;
7701                last INSCOPE;
7702              } elsif ($node->[1] & SCOPING_EL) {
7703                !!!cp ('t411');
7704                last INSCOPE;
7705              }
7706            } # INSCOPE
7707    
7708            unless (defined $i) { # has an element in scope
7709              !!!cp ('t413');
7710              !!!parse-error (type => 'unmatched end tag',
7711                              text => $token->{tag_name}, token => $token);
7712              ## NOTE: Ignore the token.
7713          } else {          } else {
7714            die "$0: $self->{insertion_mode}: Unknown insertion mode";            ## Step 1. generate implied end tags
7715              while ({
7716                      ## END_TAG_OPTIONAL_EL
7717                      dd => ($token->{tag_name} ne 'dd'),
7718                      dt => ($token->{tag_name} ne 'dt'),
7719                      li => ($token->{tag_name} ne 'li'),
7720                      option => 1,
7721                      optgroup => 1,
7722                      p => 1,
7723                      rt => 1,
7724                      rp => 1,
7725                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7726                !!!cp ('t409');
7727                pop @{$self->{open_elements}};
7728              }
7729    
7730              ## Step 2.
7731              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7732                      ne $token->{tag_name}) {
7733                !!!cp ('t412');
7734                !!!parse-error (type => 'not closed',
7735                                text => $self->{open_elements}->[-1]->[0]
7736                                    ->manakai_local_name,
7737                                token => $token);
7738              } else {
7739                !!!cp ('t414');
7740              }
7741    
7742              ## Step 3.
7743              splice @{$self->{open_elements}}, $i;
7744    
7745              ## Step 4.
7746              $clear_up_to_marker->()
7747                  if {
7748                    applet => 1, button => 1, marquee => 1, object => 1,
7749                  }->{$token->{tag_name}};
7750          }          }
       }  
     } elsif ($self->{insertion_mode} 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  
7751          !!!next-token;          !!!next-token;
7752          redo B;          next B;
7753        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{tag_name} eq 'form') {
7754          my $comment = $self->{document}->create_comment ($token->{data});          ## NOTE: As normal, but interacts with the form element pointer
7755          $self->{document}->append_child ($comment);  
7756          !!!next-token;          undef $self->{form_element};
7757          redo B;  
7758        } elsif ($token->{type} eq 'character') {          ## has an element in scope
7759          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          my $i;
7760            my $data = $1;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7761            ## As if in the main phase.            my $node = $self->{open_elements}->[$_];
7762            ## NOTE: The insertion mode in the main phase            if ($node->[1] == FORM_EL) {
7763            ## just before the phase has been changed to the trailing              !!!cp ('t418');
7764            ## end phase is either "after body" or "after frameset".              $i = $_;
7765            $reconstruct_active_formatting_elements->($insert_to_current);              last INSCOPE;
7766              } elsif ($node->[1] & SCOPING_EL) {
7767                !!!cp ('t419');
7768                last INSCOPE;
7769              }
7770            } # INSCOPE
7771    
7772            unless (defined $i) { # has an element in scope
7773              !!!cp ('t421');
7774              !!!parse-error (type => 'unmatched end tag',
7775                              text => $token->{tag_name}, token => $token);
7776              ## NOTE: Ignore the token.
7777            } else {
7778              ## Step 1. generate implied end tags
7779              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7780                !!!cp ('t417');
7781                pop @{$self->{open_elements}};
7782              }
7783                        
7784            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 2.
7785              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7786                      ne $token->{tag_name}) {
7787                !!!cp ('t417.1');
7788                !!!parse-error (type => 'not closed',
7789                                text => $self->{open_elements}->[-1]->[0]
7790                                    ->manakai_local_name,
7791                                token => $token);
7792              } else {
7793                !!!cp ('t420');
7794              }  
7795                        
7796            unless (length $token->{data}) {            ## Step 3.
7797              !!!next-token;            splice @{$self->{open_elements}}, $i;
7798              redo B;          }
7799    
7800            !!!next-token;
7801            next B;
7802          } elsif ({
7803                    ## NOTE: As normal, except acts as a closer for any ...
7804                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7805                   }->{$token->{tag_name}}) {
7806            ## has an element in scope
7807            my $i;
7808            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7809              my $node = $self->{open_elements}->[$_];
7810              if ($node->[1] == HEADING_EL) {
7811                !!!cp ('t423');
7812                $i = $_;
7813                last INSCOPE;
7814              } elsif ($node->[1] & SCOPING_EL) {
7815                !!!cp ('t424');
7816                last INSCOPE;
7817              }
7818            } # INSCOPE
7819    
7820            unless (defined $i) { # has an element in scope
7821              !!!cp ('t425.1');
7822              !!!parse-error (type => 'unmatched end tag',
7823                              text => $token->{tag_name}, token => $token);
7824              ## NOTE: Ignore the token.
7825            } else {
7826              ## Step 1. generate implied end tags
7827              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7828                !!!cp ('t422');
7829                pop @{$self->{open_elements}};
7830            }            }
7831              
7832              ## Step 2.
7833              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7834                      ne $token->{tag_name}) {
7835                !!!cp ('t425');
7836                !!!parse-error (type => 'unmatched end tag',
7837                                text => $token->{tag_name}, token => $token);
7838              } else {
7839                !!!cp ('t426');
7840              }
7841    
7842              ## Step 3.
7843              splice @{$self->{open_elements}}, $i;
7844          }          }
7845            
7846            !!!next-token;
7847            next B;
7848          } elsif ($token->{tag_name} eq 'p') {
7849            ## NOTE: As normal, except </p> implies <p> and ...
7850    
7851          !!!parse-error (type => 'after html:#character');          ## has an element in scope
7852          $self->{insertion_mode} = $previous_insertion_mode;          my $non_optional;
7853          ## reprocess          my $i;
7854          redo B;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7855        } elsif ($token->{type} eq 'start tag' or            my $node = $self->{open_elements}->[$_];
7856                 $token->{type} eq 'end tag') {            if ($node->[1] == P_EL) {
7857          !!!parse-error (type => 'after html:'.($token->{type} eq 'end tag' ? '/' : '').$token->{tag_name});              !!!cp ('t410.1');
7858          $self->{insertion_mode} = $previous_insertion_mode;              $i = $_;
7859          ## reprocess              last INSCOPE;
7860          redo B;            } elsif ($node->[1] & SCOPING_EL) {
7861        } elsif ($token->{type} eq 'end-of-file') {              !!!cp ('t411.1');
7862          ## Stop parsing              last INSCOPE;
7863          last B;            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7864                ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7865                !!!cp ('t411.2');
7866                #
7867              } else {
7868                !!!cp ('t411.3');
7869                $non_optional ||= $node;
7870                #
7871              }
7872            } # INSCOPE
7873    
7874            if (defined $i) {
7875              ## 1. Generate implied end tags
7876              #
7877    
7878              ## 2. If current node != "p", parse error
7879              if ($non_optional) {
7880                !!!cp ('t412.1');
7881                !!!parse-error (type => 'not closed',
7882                                text => $non_optional->[0]->manakai_local_name,
7883                                token => $token);
7884              } else {
7885                !!!cp ('t414.1');
7886              }
7887    
7888              ## 3. Pop
7889              splice @{$self->{open_elements}}, $i;
7890            } else {
7891              !!!cp ('t413.1');
7892              !!!parse-error (type => 'unmatched end tag',
7893                              text => $token->{tag_name}, token => $token);
7894    
7895              !!!cp ('t415.1');
7896              ## As if <p>, then reprocess the current token
7897              my $el;
7898              !!!create-element ($el, $HTML_NS, 'p',, $token);
7899              $insert->($el);
7900              ## NOTE: Not inserted into |$self->{open_elements}|.
7901            }
7902    
7903            !!!next-token;
7904            next B;
7905          } elsif ({
7906                    a => 1,
7907                    b => 1, big => 1, em => 1, font => 1, i => 1,
7908                    nobr => 1, s => 1, small => 1, strike => 1,
7909                    strong => 1, tt => 1, u => 1,
7910                   }->{$token->{tag_name}}) {
7911            !!!cp ('t427');
7912            $formatting_end_tag->($token);
7913            next B;
7914          } elsif ($token->{tag_name} eq 'br') {
7915            !!!cp ('t428');
7916            !!!parse-error (type => 'unmatched end tag',
7917                            text => 'br', token => $token);
7918    
7919            ## As if <br>
7920            $reconstruct_active_formatting_elements->($insert_to_current);
7921            
7922            my $el;
7923            !!!create-element ($el, $HTML_NS, 'br',, $token);
7924            $insert->($el);
7925            
7926            ## Ignore the token.
7927            !!!next-token;
7928            next B;
7929        } else {        } else {
7930          die "$0: $token->{type}: Unknown token";          if ($token->{tag_name} eq 'sarcasm') {
7931              sleep 0.001; # take a deep breath
7932            }
7933    
7934            ## Step 1
7935            my $node_i = -1;
7936            my $node = $self->{open_elements}->[$node_i];
7937    
7938            ## Step 2
7939            S2: {
7940              my $node_tag_name = $node->[0]->manakai_local_name;
7941              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7942              if ($node_tag_name eq $token->{tag_name}) {
7943                ## Step 1
7944                ## generate implied end tags
7945                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7946                  !!!cp ('t430');
7947                  ## NOTE: |<ruby><rt></ruby>|.
7948                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7949                  ## which seems wrong.
7950                  pop @{$self->{open_elements}};
7951                  $node_i++;
7952                }
7953            
7954                ## Step 2
7955                my $current_tag_name
7956                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7957                $current_tag_name =~ tr/A-Z/a-z/;
7958                if ($current_tag_name ne $token->{tag_name}) {
7959                  !!!cp ('t431');
7960                  ## NOTE: <x><y></x>
7961                  !!!parse-error (type => 'not closed',
7962                                  text => $self->{open_elements}->[-1]->[0]
7963                                      ->manakai_local_name,
7964                                  token => $token);
7965                } else {
7966                  !!!cp ('t432');
7967                }
7968                
7969                ## Step 3
7970                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7971    
7972                !!!next-token;
7973                last S2;
7974              } else {
7975                ## Step 3
7976                if (not ($node->[1] & FORMATTING_EL) and
7977                    #not $phrasing_category->{$node->[1]} and
7978                    ($node->[1] & SPECIAL_EL or
7979                     $node->[1] & SCOPING_EL)) {
7980                  !!!cp ('t433');
7981                  !!!parse-error (type => 'unmatched end tag',
7982                                  text => $token->{tag_name}, token => $token);
7983                  ## Ignore the token
7984                  !!!next-token;
7985                  last S2;
7986    
7987                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7988                  ## 9.27, "a" is a child of <dd> (conforming).  In
7989                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7990                  ## "a" is a child of both <body> and <dd>.
7991                }
7992                
7993                !!!cp ('t434');
7994              }
7995              
7996              ## Step 4
7997              $node_i--;
7998              $node = $self->{open_elements}->[$node_i];
7999              
8000              ## Step 5;
8001              redo S2;
8002            } # S2
8003            next B;
8004        }        }
8005      }      }
8006        next B;
8007      } continue { # B
8008        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8009          ## NOTE: The code below is executed in cases where it does not have
8010          ## to be, but it it is harmless even in those cases.
8011          ## has an element in scope
8012          INSCOPE: {
8013            for (reverse 0..$#{$self->{open_elements}}) {
8014              my $node = $self->{open_elements}->[$_];
8015              if ($node->[1] & FOREIGN_EL) {
8016                last INSCOPE;
8017              } elsif ($node->[1] & SCOPING_EL) {
8018                last;
8019              }
8020            }
8021            
8022            ## NOTE: No foreign element in scope.
8023            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8024          } # INSCOPE
8025        }
8026    } # B    } # B
8027    
8028    ## Stop parsing # MUST    ## Stop parsing # MUST
# Line 5262  sub _tree_construction_main ($) { Line 8030  sub _tree_construction_main ($) {
8030    ## TODO: script stuffs    ## TODO: script stuffs
8031  } # _tree_construct_main  } # _tree_construct_main
8032    
8033  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8034    my $class = shift;    my $class = shift;
8035    my $node = shift;    my $node = shift;
8036    my $s = \$_[0];    #my $s = \$_[0];
8037    my $onerror = $_[1];    my $onerror = $_[1];
8038      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8039    
8040      ## ISSUE: Should {confident} be true?
8041    
8042    my $nt = $node->node_type;    my $nt = $node->node_type;
8043    if ($nt == 9) {    if ($nt == 9) {
# Line 5283  sub set_inner_html ($$$) { Line 8054  sub set_inner_html ($$$) {
8054      }      }
8055    
8056      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8057      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8058    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8059      ## TODO: If non-html element      ## TODO: If non-html element
8060    
8061      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8062    
8063    ## TODO: Support for $get_wrapper
8064    
8065      ## Step 1 # MUST      ## Step 1 # MUST
8066      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8067      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5296  sub set_inner_html ($$$) { Line 8069  sub set_inner_html ($$$) {
8069      my $p = $class->new;      my $p = $class->new;
8070      $p->{document} = $doc;      $p->{document} = $doc;
8071    
8072      ## Step 9 # MUST      ## Step 8 # MUST
8073      my $i = 0;      my $i = 0;
8074      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8075      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8076      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8077        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8078        $input = $get_wrapper->($input);
8079        $p->{set_nc} = sub {
8080        my $self = shift;        my $self = shift;
8081    
8082        pop @{$self->{prev_input_character}};        my $char = '';
8083        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8084            $char = $self->{next_nc};
8085            delete $self->{next_nc};
8086            $self->{nc} = ord $char;
8087          } else {
8088            $self->{char_buffer} = '';
8089            $self->{char_buffer_pos} = 0;
8090            
8091            my $count = $input->manakai_read_until
8092                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8093                 $self->{char_buffer_pos});
8094            if ($count) {
8095              $self->{line_prev} = $self->{line};
8096              $self->{column_prev} = $self->{column};
8097              $self->{column}++;
8098              $self->{nc}
8099                  = ord substr ($self->{char_buffer},
8100                                $self->{char_buffer_pos}++, 1);
8101              return;
8102            }
8103            
8104            if ($input->read ($char, 1)) {
8105              $self->{nc} = ord $char;
8106            } else {
8107              $self->{nc} = -1;
8108              return;
8109            }
8110          }
8111    
8112          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8113          $p->{column}++;
8114    
8115        $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
8116        $self->{next_input_character} = ord substr $$s, $i++, 1;          $p->{line}++;
8117        $column++;          $p->{column} = 0;
8118            !!!cp ('i1');
8119        if ($self->{next_input_character} == 0x000A) { # LF        } elsif ($self->{nc} == 0x000D) { # CR
8120          $line++;  ## TODO: support for abort/streaming
8121          $column = 0;          my $next = '';
8122        } elsif ($self->{next_input_character} == 0x000D) { # CR          if ($input->read ($next, 1) and $next ne "\x0A") {
8123          $i++ if substr ($$s, $i, 1) eq "\x0A";            $self->{next_nc} = $next;
8124          $self->{next_input_character} = 0x000A; # LF # MUST          }
8125          $line++;          $self->{nc} = 0x000A; # LF # MUST
8126          $column = 0;          $p->{line}++;
8127        } elsif ($self->{next_input_character} > 0x10FFFF) {          $p->{column} = 0;
8128          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          !!!cp ('i2');
8129        } elsif ($self->{next_input_character} == 0x0000) { # NULL        } elsif ($self->{nc} == 0x0000) { # NULL
8130            !!!cp ('i4');
8131          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8132          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8133        }        }
8134      };      };
8135      $p->{prev_input_character} = [-1, -1, -1];  
8136      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8137              #my ($scalar, $specials_range, $offset) = @_;
8138          return 0 if defined $p->{next_nc};
8139    
8140          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8141          my $offset = $_[2] || 0;
8142          
8143          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8144            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8145            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8146              substr ($_[0], $offset)
8147                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8148              my $count = $+[0] - $-[0];
8149              if ($count) {
8150                $p->{column} += $count;
8151                $p->{char_buffer_pos} += $count;
8152                $p->{line_prev} = $p->{line};
8153                $p->{column_prev} = $p->{column} - 1;
8154                $p->{nc} = -1;
8155              }
8156              return $count;
8157            } else {
8158              return 0;
8159            }
8160          } else {
8161            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8162            if ($count) {
8163              $p->{column} += $count;
8164              $p->{column_prev} += $count;
8165              $p->{nc} = -1;
8166            }
8167            return $count;
8168          }
8169        }; # $p->{read_until}
8170    
8171      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8172        my (%opt) = @_;        my (%opt) = @_;
8173        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8174          my $column = $opt{column};
8175          if (defined $opt{token} and defined $opt{token}->{line}) {
8176            $line = $opt{token}->{line};
8177            $column = $opt{token}->{column};
8178          }
8179          warn "Parse error ($opt{type}) at line $line column $column\n";
8180      };      };
8181      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8182        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8183      };      };
8184            
8185        my $char_onerror = sub {
8186          my (undef, $type, %opt) = @_;
8187          $ponerror->(layer => 'encode',
8188                      line => $p->{line}, column => $p->{column} + 1,
8189                      %opt, type => $type);
8190        }; # $char_onerror
8191        $input->onerror ($char_onerror);
8192    
8193      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8194      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8195    
8196      ## Step 2      ## Step 2
8197      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8198      $p->{content_model_flag} = {      $p->{content_model} = {
8199        title => 'RCDATA',        title => RCDATA_CONTENT_MODEL,
8200        textarea => 'RCDATA',        textarea => RCDATA_CONTENT_MODEL,
8201        style => 'CDATA',        style => CDATA_CONTENT_MODEL,
8202        script => 'CDATA',        script => CDATA_CONTENT_MODEL,
8203        xmp => 'CDATA',        xmp => CDATA_CONTENT_MODEL,
8204        iframe => 'CDATA',        iframe => CDATA_CONTENT_MODEL,
8205        noembed => 'CDATA',        noembed => CDATA_CONTENT_MODEL,
8206        noframes => 'CDATA',        noframes => CDATA_CONTENT_MODEL,
8207        noscript => 'CDATA',        noscript => CDATA_CONTENT_MODEL,
8208        plaintext => 'PLAINTEXT',        plaintext => PLAINTEXT_CONTENT_MODEL,
8209      }->{$node_ln} || 'PCDATA';      }->{$node_ln};
8210         ## ISSUE: What is "the name of the element"? local name?      $p->{content_model} = PCDATA_CONTENT_MODEL
8211            unless defined $p->{content_model};
8212            ## ISSUE: What is "the name of the element"? local name?
8213    
8214      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8215          ## TODO: Foreign element OK?
8216    
8217      ## Step 4      ## Step 3
8218      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8219        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8220    
8221      ## Step 5 # MUST      ## Step 4 # MUST
8222      $doc->append_child ($root);      $doc->append_child ($root);
8223    
8224      ## Step 6 # MUST      ## Step 5 # MUST
8225      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8226    
8227      undef $p->{head_element};      undef $p->{head_element};
8228        undef $p->{head_element_inserted};
8229    
8230      ## Step 7 # MUST      ## Step 6 # MUST
8231      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8232    
8233      ## Step 8 # MUST      ## Step 7 # MUST
8234      my $anode = $node;      my $anode = $node;
8235      AN: while (defined $anode) {      AN: while (defined $anode) {
8236        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8237          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8238          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8239            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8240                !!!cp ('i5');
8241              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8242              last AN;              last AN;
8243            }            }
# Line 5387  sub set_inner_html ($$$) { Line 8246  sub set_inner_html ($$$) {
8246        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8247      } # AN      } # AN
8248            
8249      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8250      {      {
8251        my $self = $p;        my $self = $p;
8252        !!!next-token;        !!!next-token;
8253      }      }
8254      $p->_tree_construction_main;      $p->_tree_construction_main;
8255    
8256      ## Step 11 # MUST      ## Step 10 # MUST
8257      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8258      for (@cn) {      for (@cn) {
8259        $node->remove_child ($_);        $node->remove_child ($_);
8260      }      }
8261      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8262    
8263      ## Step 12 # MUST      ## Step 11 # MUST
8264      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8265      for (@cn) {      for (@cn) {
8266        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5411  sub set_inner_html ($$$) { Line 8269  sub set_inner_html ($$$) {
8269      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8270    
8271      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8272    
8273        delete $p->{parse_error}; # delete loop
8274    } else {    } else {
8275      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";
8276    }    }
# Line 5418  sub set_inner_html ($$$) { Line 8278  sub set_inner_html ($$$) {
8278    
8279  } # tree construction stage  } # tree construction stage
8280    
8281  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8282    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  
8283    
8284  1;  1;
8285  # $Date$  # $Date$

Legend:
Removed from v.1.35  
changed lines
  Added in v.1.207

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24