/[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.42 by wakaba, Sat Jul 21 06:59:16 2007 UTC revision 1.206 by wakaba, Mon Oct 13 08:22:30 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 DD_EL () {
74      SPECIAL_EL |
75      END_TAG_OPTIONAL_EL |
76      ALL_END_TAG_OPTIONAL_EL |
77      0b001
78    }
79    sub DT_EL () {
80      SPECIAL_EL |
81      END_TAG_OPTIONAL_EL |
82      ALL_END_TAG_OPTIONAL_EL |
83      0b010
84    }
85    sub LI_EL () {
86      SPECIAL_EL |
87      END_TAG_OPTIONAL_EL |
88      ALL_END_TAG_OPTIONAL_EL |
89      0b100
90    }
91    sub P_EL () {
92      SPECIAL_EL |
93      ADDRESS_DIV_P_EL |
94      END_TAG_OPTIONAL_EL |
95      ALL_END_TAG_OPTIONAL_EL |
96      0b001
97    }
98    
99    sub TABLE_ROW_EL () {
100      SPECIAL_EL |
101      TABLE_ROWS_EL |
102      TABLE_ROW_SCOPING_EL |
103      ALL_END_TAG_OPTIONAL_EL |
104      0b001
105    }
106    sub TABLE_ROW_GROUP_EL () {
107      SPECIAL_EL |
108      TABLE_ROWS_EL |
109      TABLE_ROWS_SCOPING_EL |
110      ALL_END_TAG_OPTIONAL_EL |
111      0b001
112    }
113    
114    sub MISC_SCOPING_EL () { SCOPING_EL | 0b000 }
115    sub BUTTON_EL () { SCOPING_EL | 0b001 }
116    sub CAPTION_EL () { SCOPING_EL | 0b010 }
117    sub HTML_EL () {
118      SCOPING_EL |
119      TABLE_SCOPING_EL |
120      TABLE_ROWS_SCOPING_EL |
121      TABLE_ROW_SCOPING_EL |
122      ALL_END_TAG_OPTIONAL_EL |
123      0b001
124    }
125    sub TABLE_EL () {
126      SCOPING_EL |
127      TABLE_ROWS_EL |
128      TABLE_SCOPING_EL |
129      0b001
130    }
131    sub TABLE_CELL_EL () {
132      SCOPING_EL |
133      TABLE_ROW_SCOPING_EL |
134      ALL_END_TAG_OPTIONAL_EL |
135      0b001
136    }
137    
138    sub MISC_FORMATTING_EL () { FORMATTING_EL | 0b000 }
139    sub A_EL () { FORMATTING_EL | 0b001 }
140    sub NOBR_EL () { FORMATTING_EL | 0b010 }
141    
142    sub RUBY_EL () { PHRASING_EL | 0b001 }
143    
144    ## ISSUE: ALL_END_TAG_OPTIONAL_EL?
145    sub OPTGROUP_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b001 }
146    sub OPTION_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b010 }
147    sub RUBY_COMPONENT_EL () { PHRASING_EL | END_TAG_OPTIONAL_EL | 0b100 }
148    
149    sub MML_AXML_EL () { PHRASING_EL | FOREIGN_EL | 0b001 }
150    
151    my $el_category = {
152      a => A_EL,
153      address => ADDRESS_DIV_EL,
154      applet => MISC_SCOPING_EL,
155      area => MISC_SPECIAL_EL,
156      article => MISC_SPECIAL_EL,
157      aside => MISC_SPECIAL_EL,
158      b => FORMATTING_EL,
159      base => MISC_SPECIAL_EL,
160      basefont => MISC_SPECIAL_EL,
161      bgsound => MISC_SPECIAL_EL,
162      big => FORMATTING_EL,
163      blockquote => MISC_SPECIAL_EL,
164      body => BODY_EL,
165      br => MISC_SPECIAL_EL,
166      button => BUTTON_EL,
167      caption => CAPTION_EL,
168      center => MISC_SPECIAL_EL,
169      col => MISC_SPECIAL_EL,
170      colgroup => MISC_SPECIAL_EL,
171      command => MISC_SPECIAL_EL,
172      datagrid => MISC_SPECIAL_EL,
173      dd => DD_EL,
174      details => MISC_SPECIAL_EL,
175      dialog => MISC_SPECIAL_EL,
176      dir => MISC_SPECIAL_EL,
177      div => ADDRESS_DIV_EL,
178      dl => MISC_SPECIAL_EL,
179      dt => DT_EL,
180      em => FORMATTING_EL,
181      embed => MISC_SPECIAL_EL,
182      eventsource => MISC_SPECIAL_EL,
183      fieldset => MISC_SPECIAL_EL,
184      figure => MISC_SPECIAL_EL,
185      font => FORMATTING_EL,
186      footer => MISC_SPECIAL_EL,
187      form => FORM_EL,
188      frame => MISC_SPECIAL_EL,
189      frameset => FRAMESET_EL,
190      h1 => HEADING_EL,
191      h2 => HEADING_EL,
192      h3 => HEADING_EL,
193      h4 => HEADING_EL,
194      h5 => HEADING_EL,
195      h6 => HEADING_EL,
196      head => MISC_SPECIAL_EL,
197      header => MISC_SPECIAL_EL,
198      hr => MISC_SPECIAL_EL,
199      html => HTML_EL,
200      i => FORMATTING_EL,
201      iframe => MISC_SPECIAL_EL,
202      img => MISC_SPECIAL_EL,
203      #image => MISC_SPECIAL_EL, ## NOTE: Commented out in the spec.
204      input => MISC_SPECIAL_EL,
205      isindex => MISC_SPECIAL_EL,
206      li => LI_EL,
207      link => MISC_SPECIAL_EL,
208      listing => MISC_SPECIAL_EL,
209      marquee => MISC_SCOPING_EL,
210      menu => MISC_SPECIAL_EL,
211      meta => MISC_SPECIAL_EL,
212      nav => MISC_SPECIAL_EL,
213      nobr => NOBR_EL,
214      noembed => MISC_SPECIAL_EL,
215      noframes => MISC_SPECIAL_EL,
216      noscript => MISC_SPECIAL_EL,
217      object => MISC_SCOPING_EL,
218      ol => MISC_SPECIAL_EL,
219      optgroup => OPTGROUP_EL,
220      option => OPTION_EL,
221      p => P_EL,
222      param => MISC_SPECIAL_EL,
223      plaintext => MISC_SPECIAL_EL,
224      pre => MISC_SPECIAL_EL,
225      rp => RUBY_COMPONENT_EL,
226      rt => RUBY_COMPONENT_EL,
227      ruby => RUBY_EL,
228      s => FORMATTING_EL,
229      script => MISC_SPECIAL_EL,
230      select => SELECT_EL,
231      section => MISC_SPECIAL_EL,
232      small => FORMATTING_EL,
233      spacer => MISC_SPECIAL_EL,
234      strike => FORMATTING_EL,
235      strong => FORMATTING_EL,
236      style => MISC_SPECIAL_EL,
237      table => TABLE_EL,
238      tbody => TABLE_ROW_GROUP_EL,
239      td => TABLE_CELL_EL,
240      textarea => MISC_SPECIAL_EL,
241      tfoot => TABLE_ROW_GROUP_EL,
242      th => TABLE_CELL_EL,
243      thead => TABLE_ROW_GROUP_EL,
244      title => MISC_SPECIAL_EL,
245      tr => TABLE_ROW_EL,
246      tt => FORMATTING_EL,
247      u => FORMATTING_EL,
248      ul => MISC_SPECIAL_EL,
249      wbr => MISC_SPECIAL_EL,
250    };
251    
252    my $el_category_f = {
253      $MML_NS => {
254        'annotation-xml' => MML_AXML_EL,
255        mi => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
256        mo => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
257        mn => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
258        ms => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
259        mtext => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
260      },
261      $SVG_NS => {
262        foreignObject => SCOPING_EL | FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
263        desc => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
264        title => FOREIGN_EL | FOREIGN_FLOW_CONTENT_EL,
265      },
266      ## NOTE: In addition, FOREIGN_EL is set to non-HTML elements.
267    };
268    
269    my $svg_attr_name = {
270      attributename => 'attributeName',
271      attributetype => 'attributeType',
272      basefrequency => 'baseFrequency',
273      baseprofile => 'baseProfile',
274      calcmode => 'calcMode',
275      clippathunits => 'clipPathUnits',
276      contentscripttype => 'contentScriptType',
277      contentstyletype => 'contentStyleType',
278      diffuseconstant => 'diffuseConstant',
279      edgemode => 'edgeMode',
280      externalresourcesrequired => 'externalResourcesRequired',
281      filterres => 'filterRes',
282      filterunits => 'filterUnits',
283      glyphref => 'glyphRef',
284      gradienttransform => 'gradientTransform',
285      gradientunits => 'gradientUnits',
286      kernelmatrix => 'kernelMatrix',
287      kernelunitlength => 'kernelUnitLength',
288      keypoints => 'keyPoints',
289      keysplines => 'keySplines',
290      keytimes => 'keyTimes',
291      lengthadjust => 'lengthAdjust',
292      limitingconeangle => 'limitingConeAngle',
293      markerheight => 'markerHeight',
294      markerunits => 'markerUnits',
295      markerwidth => 'markerWidth',
296      maskcontentunits => 'maskContentUnits',
297      maskunits => 'maskUnits',
298      numoctaves => 'numOctaves',
299      pathlength => 'pathLength',
300      patterncontentunits => 'patternContentUnits',
301      patterntransform => 'patternTransform',
302      patternunits => 'patternUnits',
303      pointsatx => 'pointsAtX',
304      pointsaty => 'pointsAtY',
305      pointsatz => 'pointsAtZ',
306      preservealpha => 'preserveAlpha',
307      preserveaspectratio => 'preserveAspectRatio',
308      primitiveunits => 'primitiveUnits',
309      refx => 'refX',
310      refy => 'refY',
311      repeatcount => 'repeatCount',
312      repeatdur => 'repeatDur',
313      requiredextensions => 'requiredExtensions',
314      requiredfeatures => 'requiredFeatures',
315      specularconstant => 'specularConstant',
316      specularexponent => 'specularExponent',
317      spreadmethod => 'spreadMethod',
318      startoffset => 'startOffset',
319      stddeviation => 'stdDeviation',
320      stitchtiles => 'stitchTiles',
321      surfacescale => 'surfaceScale',
322      systemlanguage => 'systemLanguage',
323      tablevalues => 'tableValues',
324      targetx => 'targetX',
325      targety => 'targetY',
326      textlength => 'textLength',
327      viewbox => 'viewBox',
328      viewtarget => 'viewTarget',
329      xchannelselector => 'xChannelSelector',
330      ychannelselector => 'yChannelSelector',
331      zoomandpan => 'zoomAndPan',
332  };  };
333    
334  my $c1_entity_char = {  my $foreign_attr_xname = {
335      'xlink:actuate' => [$XLINK_NS, ['xlink', 'actuate']],
336      'xlink:arcrole' => [$XLINK_NS, ['xlink', 'arcrole']],
337      'xlink:href' => [$XLINK_NS, ['xlink', 'href']],
338      'xlink:role' => [$XLINK_NS, ['xlink', 'role']],
339      'xlink:show' => [$XLINK_NS, ['xlink', 'show']],
340      'xlink:title' => [$XLINK_NS, ['xlink', 'title']],
341      'xlink:type' => [$XLINK_NS, ['xlink', 'type']],
342      'xml:base' => [$XML_NS, ['xml', 'base']],
343      'xml:lang' => [$XML_NS, ['xml', 'lang']],
344      'xml:space' => [$XML_NS, ['xml', 'space']],
345      'xmlns' => [$XMLNS_NS, [undef, 'xmlns']],
346      'xmlns:xlink' => [$XMLNS_NS, ['xmlns', 'xlink']],
347    };
348    
349    ## ISSUE: xmlns:xlink="non-xlink-ns" is not an error.
350    
351    my $charref_map = {
352      0x0D => 0x000A,
353    0x80 => 0x20AC,    0x80 => 0x20AC,
354    0x81 => 0xFFFD,    0x81 => 0xFFFD,
355    0x82 => 0x201A,    0x82 => 0x201A,
# Line 60  my $c1_entity_char = { Line 382  my $c1_entity_char = {
382    0x9D => 0xFFFD,    0x9D => 0xFFFD,
383    0x9E => 0x017E,    0x9E => 0x017E,
384    0x9F => 0x0178,    0x9F => 0x0178,
385  }; # $c1_entity_char  }; # $charref_map
386    $charref_map->{$_} = 0xFFFD
387        for 0x0000..0x0008, 0x000B, 0x000E..0x001F, 0x007F,
388            0xD800..0xDFFF, 0xFDD0..0xFDDF, ## ISSUE: 0xFDEF
389            0xFFFE, 0xFFFF, 0x1FFFE, 0x1FFFF, 0x2FFFE, 0x2FFFF, 0x3FFFE, 0x3FFFF,
390            0x4FFFE, 0x4FFFF, 0x5FFFE, 0x5FFFF, 0x6FFFE, 0x6FFFF, 0x7FFFE,
391            0x7FFFF, 0x8FFFE, 0x8FFFF, 0x9FFFE, 0x9FFFF, 0xAFFFE, 0xAFFFF,
392            0xBFFFE, 0xBFFFF, 0xCFFFE, 0xCFFFF, 0xDFFFE, 0xDFFFF, 0xEFFFE,
393            0xEFFFF, 0xFFFFE, 0xFFFFF, 0x10FFFE, 0x10FFFF;
394    
395  my $special_category = {  ## TODO: Invoke the reset algorithm when a resettable element is
396    address => 1, area => 1, base => 1, basefont => 1, bgsound => 1,  ## created (cf. HTML5 revision 2259).
397    blockquote => 1, body => 1, br => 1, center => 1, col => 1, colgroup => 1,  
398    dd => 1, dir => 1, div => 1, dl => 1, dt => 1, embed => 1, fieldset => 1,  sub parse_byte_string ($$$$;$) {
399    form => 1, frame => 1, frameset => 1, h1 => 1, h2 => 1, h3 => 1,    my $self = shift;
400    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, iframe => 1, image => 1,    my $charset_name = shift;
401    img => 1, input => 1, isindex => 1, li => 1, link => 1, listing => 1,    open my $input, '<', ref $_[0] ? $_[0] : \($_[0]);
402    menu => 1, meta => 1, noembed => 1, noframes => 1, noscript => 1,    return $self->parse_byte_stream ($charset_name, $input, @_[1..$#_]);
403    ol => 1, optgroup => 1, option => 1, p => 1, param => 1, plaintext => 1,  } # parse_byte_string
404    pre => 1, script => 1, select => 1, spacer => 1, style => 1, tbody => 1,  
405    textarea => 1, tfoot => 1, thead => 1, title => 1, tr => 1, ul => 1, wbr => 1,  sub parse_byte_stream ($$$$;$$) {
406  };    # my ($self, $charset_name, $byte_stream, $doc, $onerror, $get_wrapper) = @_;
407  my $scoping_category = {    my $self = ref $_[0] ? shift : shift->new;
408    button => 1, caption => 1, html => 1, marquee => 1, object => 1,    my $charset_name = shift;
409    table => 1, td => 1, th => 1,    my $byte_stream = $_[0];
410  };  
411  my $formatting_category = {    my $onerror = $_[2] || sub {
412    a => 1, b => 1, big => 1, em => 1, font => 1, i => 1, nobr => 1,      my (%opt) = @_;
413    s => 1, small => 1, strile => 1, strong => 1, tt => 1, u => 1,      warn "Parse error ($opt{type})\n";
414  };    };
415  # $phrasing_category: all other elements    $self->{parse_error} = $onerror; # updated later by parse_char_string
416    
417      my $get_wrapper = $_[3] || sub ($) {
418        return $_[0]; # $_[0] = byte stream handle, returned = arg to char handle
419      };
420    
421      ## HTML5 encoding sniffing algorithm
422      require Message::Charset::Info;
423      my $charset;
424      my $buffer;
425      my ($char_stream, $e_status);
426    
427      SNIFFING: {
428        ## NOTE: By setting |allow_fallback| option true when the
429        ## |get_decode_handle| method is invoked, we ignore what the HTML5
430        ## spec requires, i.e. unsupported encoding should be ignored.
431          ## TODO: We should not do this unless the parser is invoked
432          ## in the conformance checking mode, in which this behavior
433          ## would be useful.
434    
435        ## Step 1
436        if (defined $charset_name) {
437          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
438              ## TODO: Is this ok?  Transfer protocol's parameter should be
439              ## interpreted in its semantics?
440    
441          ($char_stream, $e_status) = $charset->get_decode_handle
442              ($byte_stream, allow_error_reporting => 1,
443               allow_fallback => 1);
444          if ($char_stream) {
445            $self->{confident} = 1;
446            last SNIFFING;
447          } else {
448            !!!parse-error (type => 'charset:not supported',
449                            layer => 'encode',
450                            line => 1, column => 1,
451                            value => $charset_name,
452                            level => $self->{level}->{uncertain});
453          }
454        }
455    
456        ## Step 2
457        my $byte_buffer = '';
458        for (1..1024) {
459          my $char = $byte_stream->getc;
460          last unless defined $char;
461          $byte_buffer .= $char;
462        } ## TODO: timeout
463    
464        ## Step 3
465        if ($byte_buffer =~ /^\xFE\xFF/) {
466          $charset = Message::Charset::Info->get_by_html_name ('utf-16be');
467          ($char_stream, $e_status) = $charset->get_decode_handle
468              ($byte_stream, allow_error_reporting => 1,
469               allow_fallback => 1, byte_buffer => \$byte_buffer);
470          $self->{confident} = 1;
471          last SNIFFING;
472        } elsif ($byte_buffer =~ /^\xFF\xFE/) {
473          $charset = Message::Charset::Info->get_by_html_name ('utf-16le');
474          ($char_stream, $e_status) = $charset->get_decode_handle
475              ($byte_stream, allow_error_reporting => 1,
476               allow_fallback => 1, byte_buffer => \$byte_buffer);
477          $self->{confident} = 1;
478          last SNIFFING;
479        } elsif ($byte_buffer =~ /^\xEF\xBB\xBF/) {
480          $charset = Message::Charset::Info->get_by_html_name ('utf-8');
481          ($char_stream, $e_status) = $charset->get_decode_handle
482              ($byte_stream, allow_error_reporting => 1,
483               allow_fallback => 1, byte_buffer => \$byte_buffer);
484          $self->{confident} = 1;
485          last SNIFFING;
486        }
487    
488        ## Step 4
489        ## TODO: <meta charset>
490    
491        ## Step 5
492        ## TODO: from history
493    
494        ## Step 6
495        require Whatpm::Charset::UniversalCharDet;
496        $charset_name = Whatpm::Charset::UniversalCharDet->detect_byte_string
497            ($byte_buffer);
498        if (defined $charset_name) {
499          $charset = Message::Charset::Info->get_by_html_name ($charset_name);
500    
501          require Whatpm::Charset::DecodeHandle;
502          $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
503              ($byte_stream);
504          ($char_stream, $e_status) = $charset->get_decode_handle
505              ($buffer, allow_error_reporting => 1,
506               allow_fallback => 1, byte_buffer => \$byte_buffer);
507          if ($char_stream) {
508            $buffer->{buffer} = $byte_buffer;
509            !!!parse-error (type => 'sniffing:chardet',
510                            text => $charset_name,
511                            level => $self->{level}->{info},
512                            layer => 'encode',
513                            line => 1, column => 1);
514            $self->{confident} = 0;
515            last SNIFFING;
516          }
517        }
518    
519        ## Step 7: default
520        ## TODO: Make this configurable.
521        $charset = Message::Charset::Info->get_by_html_name ('windows-1252');
522            ## NOTE: We choose |windows-1252| here, since |utf-8| should be
523            ## detectable in the step 6.
524        require Whatpm::Charset::DecodeHandle;
525        $buffer = Whatpm::Charset::DecodeHandle::ByteBuffer->new
526            ($byte_stream);
527        ($char_stream, $e_status)
528            = $charset->get_decode_handle ($buffer,
529                                           allow_error_reporting => 1,
530                                           allow_fallback => 1,
531                                           byte_buffer => \$byte_buffer);
532        $buffer->{buffer} = $byte_buffer;
533        !!!parse-error (type => 'sniffing:default',
534                        text => 'windows-1252',
535                        level => $self->{level}->{info},
536                        line => 1, column => 1,
537                        layer => 'encode');
538        $self->{confident} = 0;
539      } # SNIFFING
540    
541      if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
542        $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
543        !!!parse-error (type => 'chardecode:fallback',
544                        #text => $self->{input_encoding},
545                        level => $self->{level}->{uncertain},
546                        line => 1, column => 1,
547                        layer => 'encode');
548      } elsif (not ($e_status &
549                    Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
550        $self->{input_encoding} = $charset->get_iana_name;
551        !!!parse-error (type => 'chardecode:no error',
552                        text => $self->{input_encoding},
553                        level => $self->{level}->{uncertain},
554                        line => 1, column => 1,
555                        layer => 'encode');
556      } else {
557        $self->{input_encoding} = $charset->get_iana_name;
558      }
559    
560      $self->{change_encoding} = sub {
561        my $self = shift;
562        $charset_name = shift;
563        my $token = shift;
564    
565  sub parse_string ($$$;$) {      $charset = Message::Charset::Info->get_by_html_name ($charset_name);
566    my $self = shift->new;      ($char_stream, $e_status) = $charset->get_decode_handle
567    my $s = \$_[0];          ($byte_stream, allow_error_reporting => 1, allow_fallback => 1,
568             byte_buffer => \ $buffer->{buffer});
569        
570        if ($char_stream) { # if supported
571          ## "Change the encoding" algorithm:
572    
573          ## Step 1    
574          if ($charset->{category} &
575              Message::Charset::Info::CHARSET_CATEGORY_UTF16 ()) {
576            $charset = Message::Charset::Info->get_by_html_name ('utf-8');
577            ($char_stream, $e_status) = $charset->get_decode_handle
578                ($byte_stream,
579                 byte_buffer => \ $buffer->{buffer});
580          }
581          $charset_name = $charset->get_iana_name;
582          
583          ## Step 2
584          if (defined $self->{input_encoding} and
585              $self->{input_encoding} eq $charset_name) {
586            !!!parse-error (type => 'charset label:matching',
587                            text => $charset_name,
588                            level => $self->{level}->{info});
589            $self->{confident} = 1;
590            return;
591          }
592    
593          !!!parse-error (type => 'charset label detected',
594                          text => $self->{input_encoding},
595                          value => $charset_name,
596                          level => $self->{level}->{warn},
597                          token => $token);
598          
599          ## Step 3
600          # if (can) {
601            ## change the encoding on the fly.
602            #$self->{confident} = 1;
603            #return;
604          # }
605          
606          ## Step 4
607          throw Whatpm::HTML::RestartParser ();
608        }
609      }; # $self->{change_encoding}
610    
611      my $char_onerror = sub {
612        my (undef, $type, %opt) = @_;
613        !!!parse-error (layer => 'encode',
614                        line => $self->{line}, column => $self->{column} + 1,
615                        %opt, type => $type);
616        if ($opt{octets}) {
617          ${$opt{octets}} = "\x{FFFD}"; # relacement character
618        }
619      };
620    
621      my $wrapped_char_stream = $get_wrapper->($char_stream);
622      $wrapped_char_stream->onerror ($char_onerror);
623    
624      my @args = ($_[1], $_[2]); # $doc, $onerror - $get_wrapper = undef;
625      my $return;
626      try {
627        $return = $self->parse_char_stream ($wrapped_char_stream, @args);  
628      } catch Whatpm::HTML::RestartParser with {
629        ## NOTE: Invoked after {change_encoding}.
630    
631        if ($e_status & Message::Charset::Info::FALLBACK_ENCODING_IMPL ()) {
632          $self->{input_encoding} = $charset->get_iana_name; ## TODO: Should we set actual charset decoder's encoding name?
633          !!!parse-error (type => 'chardecode:fallback',
634                          level => $self->{level}->{uncertain},
635                          #text => $self->{input_encoding},
636                          line => 1, column => 1,
637                          layer => 'encode');
638        } elsif (not ($e_status &
639                      Message::Charset::Info::ERROR_REPORTING_ENCODING_IMPL ())) {
640          $self->{input_encoding} = $charset->get_iana_name;
641          !!!parse-error (type => 'chardecode:no error',
642                          text => $self->{input_encoding},
643                          level => $self->{level}->{uncertain},
644                          line => 1, column => 1,
645                          layer => 'encode');
646        } else {
647          $self->{input_encoding} = $charset->get_iana_name;
648        }
649        $self->{confident} = 1;
650    
651        $wrapped_char_stream = $get_wrapper->($char_stream);
652        $wrapped_char_stream->onerror ($char_onerror);
653    
654        $return = $self->parse_char_stream ($wrapped_char_stream, @args);
655      };
656      return $return;
657    } # parse_byte_stream
658    
659    ## NOTE: HTML5 spec says that the encoding layer MUST NOT strip BOM
660    ## and the HTML layer MUST ignore it.  However, we does strip BOM in
661    ## the encoding layer and the HTML layer does not ignore any U+FEFF,
662    ## because the core part of our HTML parser expects a string of character,
663    ## not a string of bytes or code units or anything which might contain a BOM.
664    ## Therefore, any parser interface that accepts a string of bytes,
665    ## such as |parse_byte_string| in this module, must ensure that it does
666    ## strip the BOM and never strip any ZWNBSP.
667    
668    sub parse_char_string ($$$;$$) {
669      #my ($self, $s, $doc, $onerror, $get_wrapper) = @_;
670      my $self = shift;
671      my $s = ref $_[0] ? $_[0] : \($_[0]);
672      require Whatpm::Charset::DecodeHandle;
673      my $input = Whatpm::Charset::DecodeHandle::CharString->new ($s);
674      return $self->parse_char_stream ($input, @_[1..$#_]);
675    } # parse_char_string
676    *parse_string = \&parse_char_string; ## NOTE: Alias for backward compatibility.
677    
678    sub parse_char_stream ($$$;$$) {
679      my $self = ref $_[0] ? shift : shift->new;
680      my $input = $_[0];
681    $self->{document} = $_[1];    $self->{document} = $_[1];
682      @{$self->{document}->child_nodes} = ();
683    
684    ## NOTE: |set_inner_html| copies most of this method's code    ## NOTE: |set_inner_html| copies most of this method's code
685    
686    my $i = 0;    $self->{confident} = 1 unless exists $self->{confident};
687    my $line = 1;    $self->{document}->input_encoding ($self->{input_encoding})
688    my $column = 0;        if defined $self->{input_encoding};
689    $self->{set_next_input_character} = sub {  ## TODO: |{input_encoding}| is needless?
690    
691      $self->{line_prev} = $self->{line} = 1;
692      $self->{column_prev} = -1;
693      $self->{column} = 0;
694      $self->{set_nc} = sub {
695      my $self = shift;      my $self = shift;
696    
697      pop @{$self->{prev_input_character}};      my $char = '';
698      unshift @{$self->{prev_input_character}}, $self->{next_input_character};      if (defined $self->{next_nc}) {
699          $char = $self->{next_nc};
700          delete $self->{next_nc};
701          $self->{nc} = ord $char;
702        } else {
703          $self->{char_buffer} = '';
704          $self->{char_buffer_pos} = 0;
705    
706      $self->{next_input_character} = -1 and return if $i >= length $$s;        my $count = $input->manakai_read_until
707      $self->{next_input_character} = ord substr $$s, $i++, 1;           ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/, $self->{char_buffer_pos});
708      $column++;        if ($count) {
709            $self->{line_prev} = $self->{line};
710            $self->{column_prev} = $self->{column};
711            $self->{column}++;
712            $self->{nc}
713                = ord substr ($self->{char_buffer}, $self->{char_buffer_pos}++, 1);
714            return;
715          }
716    
717          if ($input->read ($char, 1)) {
718            $self->{nc} = ord $char;
719          } else {
720            $self->{nc} = -1;
721            return;
722          }
723        }
724    
725        ($self->{line_prev}, $self->{column_prev})
726            = ($self->{line}, $self->{column});
727        $self->{column}++;
728            
729      if ($self->{next_input_character} == 0x000A) { # LF      if ($self->{nc} == 0x000A) { # LF
730        $line++;        !!!cp ('j1');
731        $column = 0;        $self->{line}++;
732      } elsif ($self->{next_input_character} == 0x000D) { # CR        $self->{column} = 0;
733        $i++ if substr ($$s, $i, 1) eq "\x0A";      } elsif ($self->{nc} == 0x000D) { # CR
734        $self->{next_input_character} = 0x000A; # LF # MUST        !!!cp ('j2');
735        $line++;  ## TODO: support for abort/streaming
736        $column = 0;        my $next = '';
737      } elsif ($self->{next_input_character} > 0x10FFFF) {        if ($input->read ($next, 1) and $next ne "\x0A") {
738        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{next_nc} = $next;
739      } elsif ($self->{next_input_character} == 0x0000) { # NULL        }
740          $self->{nc} = 0x000A; # LF # MUST
741          $self->{line}++;
742          $self->{column} = 0;
743        } elsif ($self->{nc} == 0x0000) { # NULL
744          !!!cp ('j4');
745        !!!parse-error (type => 'NULL');        !!!parse-error (type => 'NULL');
746        $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST        $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
747      }      }
748    };    };
749    $self->{prev_input_character} = [-1, -1, -1];  
750    $self->{next_input_character} = -1;    $self->{read_until} = sub {
751        #my ($scalar, $specials_range, $offset) = @_;
752        return 0 if defined $self->{next_nc};
753    
754        my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
755        my $offset = $_[2] || 0;
756    
757        if ($self->{char_buffer_pos} < length $self->{char_buffer}) {
758          pos ($self->{char_buffer}) = $self->{char_buffer_pos};
759          if ($self->{char_buffer} =~ /\G(?>$pattern)+/) {
760            substr ($_[0], $offset)
761                = substr ($self->{char_buffer}, $-[0], $+[0] - $-[0]);
762            my $count = $+[0] - $-[0];
763            if ($count) {
764              $self->{column} += $count;
765              $self->{char_buffer_pos} += $count;
766              $self->{line_prev} = $self->{line};
767              $self->{column_prev} = $self->{column} - 1;
768              $self->{nc} = -1;
769            }
770            return $count;
771          } else {
772            return 0;
773          }
774        } else {
775          my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
776          if ($count) {
777            $self->{column} += $count;
778            $self->{line_prev} = $self->{line};
779            $self->{column_prev} = $self->{column} - 1;
780            $self->{nc} = -1;
781          }
782          return $count;
783        }
784      }; # $self->{read_until}
785    
786    my $onerror = $_[2] || sub {    my $onerror = $_[2] || sub {
787      my (%opt) = @_;      my (%opt) = @_;
788      warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";      my $line = $opt{token} ? $opt{token}->{line} : $opt{line};
789        my $column = $opt{token} ? $opt{token}->{column} : $opt{column};
790        warn "Parse error ($opt{type}) at line $line column $column\n";
791    };    };
792    $self->{parse_error} = sub {    $self->{parse_error} = sub {
793      $onerror->(@_, line => $line, column => $column);      $onerror->(line => $self->{line}, column => $self->{column}, @_);
794    };    };
795    
796      my $char_onerror = sub {
797        my (undef, $type, %opt) = @_;
798        !!!parse-error (layer => 'encode',
799                        line => $self->{line}, column => $self->{column} + 1,
800                        %opt, type => $type);
801      }; # $char_onerror
802    
803      if ($_[3]) {
804        $input = $_[3]->($input);
805        $input->onerror ($char_onerror);
806      } else {
807        $input->onerror ($char_onerror) unless defined $input->onerror;
808      }
809    
810    $self->_initialize_tokenizer;    $self->_initialize_tokenizer;
811    $self->_initialize_tree_constructor;    $self->_initialize_tree_constructor;
812    $self->_construct_tree;    $self->_construct_tree;
813    $self->_terminate_tree_constructor;    $self->_terminate_tree_constructor;
814    
815      delete $self->{parse_error}; # remove loop
816    
817    return $self->{document};    return $self->{document};
818  } # parse_string  } # parse_char_stream
819    
820  sub new ($) {  sub new ($) {
821    my $class = shift;    my $class = shift;
822    my $self = bless {}, $class;    my $self = bless {
823    $self->{set_next_input_character} = sub {      level => {must => 'm',
824      $self->{next_input_character} = -1;                should => 's',
825                  warn => 'w',
826                  info => 'i',
827                  uncertain => 'u'},
828      }, $class;
829      $self->{set_nc} = sub {
830        $self->{nc} = -1;
831    };    };
832    $self->{parse_error} = sub {    $self->{parse_error} = sub {
833      #      #
834    };    };
835      $self->{change_encoding} = sub {
836        # if ($_[0] is a supported encoding) {
837        #   run "change the encoding" algorithm;
838        #   throw Whatpm::HTML::RestartParser (charset => $new_encoding);
839        # }
840      };
841      $self->{application_cache_selection} = sub {
842        #
843      };
844    return $self;    return $self;
845  } # new  } # new
846    
# Line 159  sub CDATA_CONTENT_MODEL () { CM_LIMITED_ Line 853  sub CDATA_CONTENT_MODEL () { CM_LIMITED_
853  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }  sub RCDATA_CONTENT_MODEL () { CM_ENTITY | CM_LIMITED_MARKUP }
854  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }  sub PCDATA_CONTENT_MODEL () { CM_ENTITY | CM_FULL_MARKUP }
855    
856    sub DATA_STATE () { 0 }
857    #sub ENTITY_DATA_STATE () { 1 }
858    sub TAG_OPEN_STATE () { 2 }
859    sub CLOSE_TAG_OPEN_STATE () { 3 }
860    sub TAG_NAME_STATE () { 4 }
861    sub BEFORE_ATTRIBUTE_NAME_STATE () { 5 }
862    sub ATTRIBUTE_NAME_STATE () { 6 }
863    sub AFTER_ATTRIBUTE_NAME_STATE () { 7 }
864    sub BEFORE_ATTRIBUTE_VALUE_STATE () { 8 }
865    sub ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE () { 9 }
866    sub ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE () { 10 }
867    sub ATTRIBUTE_VALUE_UNQUOTED_STATE () { 11 }
868    #sub ENTITY_IN_ATTRIBUTE_VALUE_STATE () { 12 }
869    sub MARKUP_DECLARATION_OPEN_STATE () { 13 }
870    sub COMMENT_START_STATE () { 14 }
871    sub COMMENT_START_DASH_STATE () { 15 }
872    sub COMMENT_STATE () { 16 }
873    sub COMMENT_END_STATE () { 17 }
874    sub COMMENT_END_DASH_STATE () { 18 }
875    sub BOGUS_COMMENT_STATE () { 19 }
876    sub DOCTYPE_STATE () { 20 }
877    sub BEFORE_DOCTYPE_NAME_STATE () { 21 }
878    sub DOCTYPE_NAME_STATE () { 22 }
879    sub AFTER_DOCTYPE_NAME_STATE () { 23 }
880    sub BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 24 }
881    sub DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE () { 25 }
882    sub DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE () { 26 }
883    sub AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE () { 27 }
884    sub BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 28 }
885    sub DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE () { 29 }
886    sub DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE () { 30 }
887    sub AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE () { 31 }
888    sub BOGUS_DOCTYPE_STATE () { 32 }
889    sub AFTER_ATTRIBUTE_VALUE_QUOTED_STATE () { 33 }
890    sub SELF_CLOSING_START_TAG_STATE () { 34 }
891    sub CDATA_SECTION_STATE () { 35 }
892    sub MD_HYPHEN_STATE () { 36 } # "markup declaration open state" in the spec
893    sub MD_DOCTYPE_STATE () { 37 } # "markup declaration open state" in the spec
894    sub MD_CDATA_STATE () { 38 } # "markup declaration open state" in the spec
895    sub CDATA_RCDATA_CLOSE_TAG_STATE () { 39 } # "close tag open state" in the spec
896    sub CDATA_SECTION_MSE1_STATE () { 40 } # "CDATA section state" in the spec
897    sub CDATA_SECTION_MSE2_STATE () { 41 } # "CDATA section state" in the spec
898    sub PUBLIC_STATE () { 42 } # "after DOCTYPE name state" in the spec
899    sub SYSTEM_STATE () { 43 } # "after DOCTYPE name state" in the spec
900    ## NOTE: "Entity data state", "entity in attribute value state", and
901    ## "consume a character reference" algorithm are jointly implemented
902    ## using the following six states:
903    sub ENTITY_STATE () { 44 }
904    sub ENTITY_HASH_STATE () { 45 }
905    sub NCR_NUM_STATE () { 46 }
906    sub HEXREF_X_STATE () { 47 }
907    sub HEXREF_HEX_STATE () { 48 }
908    sub ENTITY_NAME_STATE () { 49 }
909    sub PCDATA_STATE () { 50 } # "data state" in the spec
910    
911    sub DOCTYPE_TOKEN () { 1 }
912    sub COMMENT_TOKEN () { 2 }
913    sub START_TAG_TOKEN () { 3 }
914    sub END_TAG_TOKEN () { 4 }
915    sub END_OF_FILE_TOKEN () { 5 }
916    sub CHARACTER_TOKEN () { 6 }
917    
918    sub AFTER_HTML_IMS () { 0b100 }
919    sub HEAD_IMS ()       { 0b1000 }
920    sub BODY_IMS ()       { 0b10000 }
921    sub BODY_TABLE_IMS () { 0b100000 }
922    sub TABLE_IMS ()      { 0b1000000 }
923    sub ROW_IMS ()        { 0b10000000 }
924    sub BODY_AFTER_IMS () { 0b100000000 }
925    sub FRAME_IMS ()      { 0b1000000000 }
926    sub SELECT_IMS ()     { 0b10000000000 }
927    sub IN_FOREIGN_CONTENT_IM () { 0b100000000000 }
928        ## NOTE: "in foreign content" insertion mode is special; it is combined
929        ## with the secondary insertion mode.  In this parser, they are stored
930        ## together in the bit-or'ed form.
931    sub IN_CDATA_RCDATA_IM () { 0b1000000000000 }
932        ## NOTE: "in CDATA/RCDATA" insertion mode is also special; it is
933        ## combined with the original insertion mode.  In thie parser,
934        ## they are stored together in the bit-or'ed form.
935    
936    ## NOTE: "initial" and "before html" insertion modes have no constants.
937    
938    ## NOTE: "after after body" insertion mode.
939    sub AFTER_HTML_BODY_IM () { AFTER_HTML_IMS | BODY_AFTER_IMS }
940    
941    ## NOTE: "after after frameset" insertion mode.
942    sub AFTER_HTML_FRAMESET_IM () { AFTER_HTML_IMS | FRAME_IMS }
943    
944    sub IN_HEAD_IM () { HEAD_IMS | 0b00 }
945    sub IN_HEAD_NOSCRIPT_IM () { HEAD_IMS | 0b01 }
946    sub AFTER_HEAD_IM () { HEAD_IMS | 0b10 }
947    sub BEFORE_HEAD_IM () { HEAD_IMS | 0b11 }
948    sub IN_BODY_IM () { BODY_IMS }
949    sub IN_CELL_IM () { BODY_IMS | BODY_TABLE_IMS | 0b01 }
950    sub IN_CAPTION_IM () { BODY_IMS | BODY_TABLE_IMS | 0b10 }
951    sub IN_ROW_IM () { TABLE_IMS | ROW_IMS | 0b01 }
952    sub IN_TABLE_BODY_IM () { TABLE_IMS | ROW_IMS | 0b10 }
953    sub IN_TABLE_IM () { TABLE_IMS }
954    sub AFTER_BODY_IM () { BODY_AFTER_IMS }
955    sub IN_FRAMESET_IM () { FRAME_IMS | 0b01 }
956    sub AFTER_FRAMESET_IM () { FRAME_IMS | 0b10 }
957    sub IN_SELECT_IM () { SELECT_IMS | 0b01 }
958    sub IN_SELECT_IN_TABLE_IM () { SELECT_IMS | 0b10 }
959    sub IN_COLUMN_GROUP_IM () { 0b10 }
960    
961  ## Implementations MUST act as if state machine in the spec  ## Implementations MUST act as if state machine in the spec
962    
963  sub _initialize_tokenizer ($) {  sub _initialize_tokenizer ($) {
964    my $self = shift;    my $self = shift;
965    $self->{state} = 'data'; # MUST    $self->{state} = DATA_STATE; # MUST
966      #$self->{s_kwd}; # state keyword - initialized when used
967      #$self->{entity__value}; # initialized when used
968      #$self->{entity__match}; # initialized when used
969    $self->{content_model} = PCDATA_CONTENT_MODEL; # be    $self->{content_model} = PCDATA_CONTENT_MODEL; # be
970    undef $self->{current_token}; # start tag, end tag, comment, or DOCTYPE    undef $self->{ct}; # current token
971    undef $self->{current_attribute};    undef $self->{ca}; # current attribute
972    undef $self->{last_emitted_start_tag_name};    undef $self->{last_stag_name}; # last emitted start tag name
973    undef $self->{last_attribute_value_state};    #$self->{prev_state}; # initialized when used
974    $self->{char} = [];    delete $self->{self_closing};
975    # $self->{next_input_character}    $self->{char_buffer} = '';
976      $self->{char_buffer_pos} = 0;
977      $self->{nc} = -1; # next input character
978      #$self->{next_nc}
979    !!!next-input-character;    !!!next-input-character;
980    $self->{token} = [];    $self->{token} = [];
981    # $self->{escape}    # $self->{escape}
982  } # _initialize_tokenizer  } # _initialize_tokenizer
983    
984  ## A token has:  ## A token has:
985  ##   ->{type} eq 'DOCTYPE', 'start tag', 'end tag', 'comment',  ##   ->{type} == DOCTYPE_TOKEN, START_TAG_TOKEN, END_TAG_TOKEN, COMMENT_TOKEN,
986  ##       'character', or 'end-of-file'  ##       CHARACTER_TOKEN, or END_OF_FILE_TOKEN
987  ##   ->{name} (DOCTYPE, start tag (tag name), end tag (tag name))  ##   ->{name} (DOCTYPE_TOKEN)
988  ##   ->{public_identifier} (DOCTYPE)  ##   ->{tag_name} (START_TAG_TOKEN, END_TAG_TOKEN)
989  ##   ->{system_identifier} (DOCTYPE)  ##   ->{pubid} (DOCTYPE_TOKEN)
990  ##   ->{correct} == 1 or 0 (DOCTYPE)  ##   ->{sysid} (DOCTYPE_TOKEN)
991  ##   ->{attributes} isa HASH (start tag, end tag)  ##   ->{quirks} == 1 or 0 (DOCTYPE_TOKEN): "force-quirks" flag
992  ##   ->{data} (comment, character)  ##   ->{attributes} isa HASH (START_TAG_TOKEN, END_TAG_TOKEN)
993    ##        ->{name}
994    ##        ->{value}
995    ##        ->{has_reference} == 1 or 0
996    ##   ->{data} (COMMENT_TOKEN, CHARACTER_TOKEN)
997    ## NOTE: The "self-closing flag" is hold as |$self->{self_closing}|.
998    ##     |->{self_closing}| is used to save the value of |$self->{self_closing}|
999    ##     while the token is pushed back to the stack.
1000    
1001  ## Emitted token MUST immediately be handled by the tree construction state.  ## Emitted token MUST immediately be handled by the tree construction state.
1002    
# Line 194  sub _initialize_tokenizer ($) { Line 1006  sub _initialize_tokenizer ($) {
1006  ## has completed loading.  If one has, then it MUST be executed  ## has completed loading.  If one has, then it MUST be executed
1007  ## and removed from the list.  ## and removed from the list.
1008    
1009    ## TODO: Polytheistic slash SHOULD NOT be used. (Applied only to atheists.)
1010    ## (This requirement was dropped from HTML5 spec, unfortunately.)
1011    
1012    my $is_space = {
1013      0x0009 => 1, # CHARACTER TABULATION (HT)
1014      0x000A => 1, # LINE FEED (LF)
1015      #0x000B => 0, # LINE TABULATION (VT)
1016      0x000C => 1, # FORM FEED (FF)
1017      #0x000D => 1, # CARRIAGE RETURN (CR)
1018      0x0020 => 1, # SPACE (SP)
1019    };
1020    
1021  sub _get_next_token ($) {  sub _get_next_token ($) {
1022    my $self = shift;    my $self = shift;
1023    
1024      if ($self->{self_closing}) {
1025        !!!parse-error (type => 'nestc', token => $self->{ct});
1026        ## NOTE: The |self_closing| flag is only set by start tag token.
1027        ## In addition, when a start tag token is emitted, it is always set to
1028        ## |ct|.
1029        delete $self->{self_closing};
1030      }
1031    
1032    if (@{$self->{token}}) {    if (@{$self->{token}}) {
1033        $self->{self_closing} = $self->{token}->[0]->{self_closing};
1034      return shift @{$self->{token}};      return shift @{$self->{token}};
1035    }    }
1036    
1037    A: {    A: {
1038      if ($self->{state} eq 'data') {      if ($self->{state} == PCDATA_STATE) {
1039        if ($self->{next_input_character} == 0x0026) { # &        ## NOTE: Same as |DATA_STATE|, but only for |PCDATA| content model.
1040          if ($self->{content_model} & CM_ENTITY) { # PCDATA | RCDATA  
1041            $self->{state} = 'entity data';        if ($self->{nc} == 0x0026) { # &
1042            !!!cp (0.1);
1043            ## NOTE: In the spec, the tokenizer is switched to the
1044            ## "entity data state".  In this implementation, the tokenizer
1045            ## is switched to the |ENTITY_STATE|, which is an implementation
1046            ## of the "consume a character reference" algorithm.
1047            $self->{entity_add} = -1;
1048            $self->{prev_state} = DATA_STATE;
1049            $self->{state} = ENTITY_STATE;
1050            !!!next-input-character;
1051            redo A;
1052          } elsif ($self->{nc} == 0x003C) { # <
1053            !!!cp (0.2);
1054            $self->{state} = TAG_OPEN_STATE;
1055            !!!next-input-character;
1056            redo A;
1057          } elsif ($self->{nc} == -1) {
1058            !!!cp (0.3);
1059            !!!emit ({type => END_OF_FILE_TOKEN,
1060                      line => $self->{line}, column => $self->{column}});
1061            last A; ## TODO: ok?
1062          } else {
1063            !!!cp (0.4);
1064            #
1065          }
1066    
1067          # Anything else
1068          my $token = {type => CHARACTER_TOKEN,
1069                       data => chr $self->{nc},
1070                       line => $self->{line}, column => $self->{column},
1071                      };
1072          $self->{read_until}->($token->{data}, q[<&], length $token->{data});
1073    
1074          ## Stay in the state.
1075          !!!next-input-character;
1076          !!!emit ($token);
1077          redo A;
1078        } elsif ($self->{state} == DATA_STATE) {
1079          $self->{s_kwd} = '' unless defined $self->{s_kwd};
1080          if ($self->{nc} == 0x0026) { # &
1081            $self->{s_kwd} = '';
1082            if ($self->{content_model} & CM_ENTITY and # PCDATA | RCDATA
1083                not $self->{escape}) {
1084              !!!cp (1);
1085              ## NOTE: In the spec, the tokenizer is switched to the
1086              ## "entity data state".  In this implementation, the tokenizer
1087              ## is switched to the |ENTITY_STATE|, which is an implementation
1088              ## of the "consume a character reference" algorithm.
1089              $self->{entity_add} = -1;
1090              $self->{prev_state} = DATA_STATE;
1091              $self->{state} = ENTITY_STATE;
1092            !!!next-input-character;            !!!next-input-character;
1093            redo A;            redo A;
1094          } else {          } else {
1095              !!!cp (2);
1096            #            #
1097          }          }
1098        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
1099          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA          if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1100            unless ($self->{escape}) {            $self->{s_kwd} .= '-';
1101              if ($self->{prev_input_character}->[0] == 0x002D and # -            
1102                  $self->{prev_input_character}->[1] == 0x0021 and # !            if ($self->{s_kwd} eq '<!--') {
1103                  $self->{prev_input_character}->[2] == 0x003C) { # <              !!!cp (3);
1104                $self->{escape} = 1;              $self->{escape} = 1; # unless $self->{escape};
1105              }              $self->{s_kwd} = '--';
1106                #
1107              } elsif ($self->{s_kwd} eq '---') {
1108                !!!cp (4);
1109                $self->{s_kwd} = '--';
1110                #
1111              } else {
1112                !!!cp (5);
1113                #
1114            }            }
1115          }          }
1116                    
1117          #          #
1118        } elsif ($self->{next_input_character} == 0x003C) { # <        } elsif ($self->{nc} == 0x0021) { # !
1119            if (length $self->{s_kwd}) {
1120              !!!cp (5.1);
1121              $self->{s_kwd} .= '!';
1122              #
1123            } else {
1124              !!!cp (5.2);
1125              #$self->{s_kwd} = '';
1126              #
1127            }
1128            #
1129          } elsif ($self->{nc} == 0x003C) { # <
1130          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA          if ($self->{content_model} & CM_FULL_MARKUP or # PCDATA
1131              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA              (($self->{content_model} & CM_LIMITED_MARKUP) and # CDATA | RCDATA
1132               not $self->{escape})) {               not $self->{escape})) {
1133            $self->{state} = 'tag open';            !!!cp (6);
1134              $self->{state} = TAG_OPEN_STATE;
1135            !!!next-input-character;            !!!next-input-character;
1136            redo A;            redo A;
1137          } else {          } else {
1138              !!!cp (7);
1139              $self->{s_kwd} = '';
1140            #            #
1141          }          }
1142        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1143          if ($self->{escape} and          if ($self->{escape} and
1144              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA              ($self->{content_model} & CM_LIMITED_MARKUP)) { # RCDATA | CDATA
1145            if ($self->{prev_input_character}->[0] == 0x002D and # -            if ($self->{s_kwd} eq '--') {
1146                $self->{prev_input_character}->[1] == 0x002D) { # -              !!!cp (8);
1147              delete $self->{escape};              delete $self->{escape};
1148              } else {
1149                !!!cp (9);
1150            }            }
1151            } else {
1152              !!!cp (10);
1153          }          }
1154                    
1155            $self->{s_kwd} = '';
1156          #          #
1157        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1158          !!!emit ({type => 'end-of-file'});          !!!cp (11);
1159            $self->{s_kwd} = '';
1160            !!!emit ({type => END_OF_FILE_TOKEN,
1161                      line => $self->{line}, column => $self->{column}});
1162          last A; ## TODO: ok?          last A; ## TODO: ok?
1163          } else {
1164            !!!cp (12);
1165            $self->{s_kwd} = '';
1166            #
1167        }        }
       # Anything else  
       my $token = {type => 'character',  
                    data => chr $self->{next_input_character}};  
       ## Stay in the data state  
       !!!next-input-character;  
   
       !!!emit ($token);  
   
       redo A;  
     } elsif ($self->{state} eq 'entity data') {  
       ## (cannot happen in CDATA state)  
         
       my $token = $self->_tokenize_attempt_to_consume_an_entity (0);  
   
       $self->{state} = 'data';  
       # next-input-character is already done  
1168    
1169        unless (defined $token) {        # Anything else
1170          !!!emit ({type => 'character', data => '&'});        my $token = {type => CHARACTER_TOKEN,
1171                       data => chr $self->{nc},
1172                       line => $self->{line}, column => $self->{column},
1173                      };
1174          if ($self->{read_until}->($token->{data}, q[-!<>&],
1175                                    length $token->{data})) {
1176            $self->{s_kwd} = '';
1177          }
1178    
1179          ## Stay in the data state.
1180          if ($self->{content_model} == PCDATA_CONTENT_MODEL) {
1181            !!!cp (13);
1182            $self->{state} = PCDATA_STATE;
1183        } else {        } else {
1184          !!!emit ($token);          !!!cp (14);
1185            ## Stay in the state.
1186        }        }
1187          !!!next-input-character;
1188          !!!emit ($token);
1189        redo A;        redo A;
1190      } elsif ($self->{state} eq 'tag open') {      } elsif ($self->{state} == TAG_OPEN_STATE) {
1191        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1192          if ($self->{next_input_character} == 0x002F) { # /          if ($self->{nc} == 0x002F) { # /
1193              !!!cp (15);
1194            !!!next-input-character;            !!!next-input-character;
1195            $self->{state} = 'close tag open';            $self->{state} = CLOSE_TAG_OPEN_STATE;
1196            redo A;            redo A;
1197            } elsif ($self->{nc} == 0x0021) { # !
1198              !!!cp (15.1);
1199              $self->{s_kwd} = '<' unless $self->{escape};
1200              #
1201          } else {          } else {
1202            ## reconsume            !!!cp (16);
1203            $self->{state} = 'data';            #
   
           !!!emit ({type => 'character', data => '<'});  
   
           redo A;  
1204          }          }
1205    
1206            ## reconsume
1207            $self->{state} = DATA_STATE;
1208            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1209                      line => $self->{line_prev},
1210                      column => $self->{column_prev},
1211                     });
1212            redo A;
1213        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA        } elsif ($self->{content_model} & CM_FULL_MARKUP) { # PCDATA
1214          if ($self->{next_input_character} == 0x0021) { # !          if ($self->{nc} == 0x0021) { # !
1215            $self->{state} = 'markup declaration open';            !!!cp (17);
1216              $self->{state} = MARKUP_DECLARATION_OPEN_STATE;
1217            !!!next-input-character;            !!!next-input-character;
1218            redo A;            redo A;
1219          } elsif ($self->{next_input_character} == 0x002F) { # /          } elsif ($self->{nc} == 0x002F) { # /
1220            $self->{state} = 'close tag open';            !!!cp (18);
1221              $self->{state} = CLOSE_TAG_OPEN_STATE;
1222            !!!next-input-character;            !!!next-input-character;
1223            redo A;            redo A;
1224          } elsif (0x0041 <= $self->{next_input_character} and          } elsif (0x0041 <= $self->{nc} and
1225                   $self->{next_input_character} <= 0x005A) { # A..Z                   $self->{nc} <= 0x005A) { # A..Z
1226            $self->{current_token}            !!!cp (19);
1227              = {type => 'start tag',            $self->{ct}
1228                 tag_name => chr ($self->{next_input_character} + 0x0020)};              = {type => START_TAG_TOKEN,
1229            $self->{state} = 'tag name';                 tag_name => chr ($self->{nc} + 0x0020),
1230                   line => $self->{line_prev},
1231                   column => $self->{column_prev}};
1232              $self->{state} = TAG_NAME_STATE;
1233            !!!next-input-character;            !!!next-input-character;
1234            redo A;            redo A;
1235          } elsif (0x0061 <= $self->{next_input_character} and          } elsif (0x0061 <= $self->{nc} and
1236                   $self->{next_input_character} <= 0x007A) { # a..z                   $self->{nc} <= 0x007A) { # a..z
1237            $self->{current_token} = {type => 'start tag',            !!!cp (20);
1238                              tag_name => chr ($self->{next_input_character})};            $self->{ct} = {type => START_TAG_TOKEN,
1239            $self->{state} = 'tag name';                                      tag_name => chr ($self->{nc}),
1240                                        line => $self->{line_prev},
1241                                        column => $self->{column_prev}};
1242              $self->{state} = TAG_NAME_STATE;
1243            !!!next-input-character;            !!!next-input-character;
1244            redo A;            redo A;
1245          } elsif ($self->{next_input_character} == 0x003E) { # >          } elsif ($self->{nc} == 0x003E) { # >
1246            !!!parse-error (type => 'empty start tag');            !!!cp (21);
1247            $self->{state} = 'data';            !!!parse-error (type => 'empty start tag',
1248                              line => $self->{line_prev},
1249                              column => $self->{column_prev});
1250              $self->{state} = DATA_STATE;
1251            !!!next-input-character;            !!!next-input-character;
1252    
1253            !!!emit ({type => 'character', data => '<>'});            !!!emit ({type => CHARACTER_TOKEN, data => '<>',
1254                        line => $self->{line_prev},
1255                        column => $self->{column_prev},
1256                       });
1257    
1258            redo A;            redo A;
1259          } elsif ($self->{next_input_character} == 0x003F) { # ?          } elsif ($self->{nc} == 0x003F) { # ?
1260            !!!parse-error (type => 'pio');            !!!cp (22);
1261            $self->{state} = 'bogus comment';            !!!parse-error (type => 'pio',
1262            ## $self->{next_input_character} is intentionally left as is                            line => $self->{line_prev},
1263                              column => $self->{column_prev});
1264              $self->{state} = BOGUS_COMMENT_STATE;
1265              $self->{ct} = {type => COMMENT_TOKEN, data => '',
1266                                        line => $self->{line_prev},
1267                                        column => $self->{column_prev},
1268                                       };
1269              ## $self->{nc} is intentionally left as is
1270            redo A;            redo A;
1271          } else {          } else {
1272            !!!parse-error (type => 'bare stago');            !!!cp (23);
1273            $self->{state} = 'data';            !!!parse-error (type => 'bare stago',
1274                              line => $self->{line_prev},
1275                              column => $self->{column_prev});
1276              $self->{state} = DATA_STATE;
1277            ## reconsume            ## reconsume
1278    
1279            !!!emit ({type => 'character', data => '<'});            !!!emit ({type => CHARACTER_TOKEN, data => '<',
1280                        line => $self->{line_prev},
1281                        column => $self->{column_prev},
1282                       });
1283    
1284            redo A;            redo A;
1285          }          }
1286        } else {        } else {
1287          die "$0: $self->{content_model} in tag open";          die "$0: $self->{content_model} in tag open";
1288        }        }
1289      } elsif ($self->{state} eq 'close tag open') {      } elsif ($self->{state} == CLOSE_TAG_OPEN_STATE) {
1290        if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA        ## NOTE: The "close tag open state" in the spec is implemented as
1291          if (defined $self->{last_emitted_start_tag_name}) {        ## |CLOSE_TAG_OPEN_STATE| and |CDATA_RCDATA_CLOSE_TAG_STATE|.
           ## NOTE: <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>  
           my @next_char;  
           TAGNAME: for (my $i = 0; $i < length $self->{last_emitted_start_tag_name}; $i++) {  
             push @next_char, $self->{next_input_character};  
             my $c = ord substr ($self->{last_emitted_start_tag_name}, $i, 1);  
             my $C = 0x0061 <= $c && $c <= 0x007A ? $c - 0x0020 : $c;  
             if ($self->{next_input_character} == $c or $self->{next_input_character} == $C) {  
               !!!next-input-character;  
               next TAGNAME;  
             } else {  
               $self->{next_input_character} = shift @next_char; # reconsume  
               !!!back-next-input-character (@next_char);  
               $self->{state} = 'data';  
1292    
1293                !!!emit ({type => 'character', data => '</'});        my ($l, $c) = ($self->{line_prev}, $self->{column_prev} - 1); # "<"of"</"
1294            if ($self->{content_model} & CM_LIMITED_MARKUP) { # RCDATA | CDATA
1295                redo A;          if (defined $self->{last_stag_name}) {
1296              }            $self->{state} = CDATA_RCDATA_CLOSE_TAG_STATE;
1297            }            $self->{s_kwd} = '';
1298            push @next_char, $self->{next_input_character};            ## Reconsume.
1299                    redo A;
           unless ($self->{next_input_character} == 0x0009 or # HT  
                   $self->{next_input_character} == 0x000A or # LF  
                   $self->{next_input_character} == 0x000B or # VT  
                   $self->{next_input_character} == 0x000C or # FF  
                   $self->{next_input_character} == 0x0020 or # SP  
                   $self->{next_input_character} == 0x003E or # >  
                   $self->{next_input_character} == 0x002F or # /  
                   $self->{next_input_character} == -1) {  
             $self->{next_input_character} = shift @next_char; # reconsume  
             !!!back-next-input-character (@next_char);  
             $self->{state} = 'data';  
             !!!emit ({type => 'character', data => '</'});  
             redo A;  
           } else {  
             $self->{next_input_character} = shift @next_char;  
             !!!back-next-input-character (@next_char);  
             # and consume...  
           }  
1300          } else {          } else {
1301            ## No start tag token has ever been emitted            ## No start tag token has ever been emitted
1302            # next-input-character is already done            ## NOTE: See <http://krijnhoetmer.nl/irc-logs/whatwg/20070626#l-564>.
1303            $self->{state} = 'data';            !!!cp (28);
1304            !!!emit ({type => 'character', data => '</'});            $self->{state} = DATA_STATE;
1305              ## Reconsume.
1306              !!!emit ({type => CHARACTER_TOKEN, data => '</',
1307                        line => $l, column => $c,
1308                       });
1309            redo A;            redo A;
1310          }          }
1311        }        }
1312          
1313        if (0x0041 <= $self->{next_input_character} and        if (0x0041 <= $self->{nc} and
1314            $self->{next_input_character} <= 0x005A) { # A..Z            $self->{nc} <= 0x005A) { # A..Z
1315          $self->{current_token} = {type => 'end tag',          !!!cp (29);
1316                            tag_name => chr ($self->{next_input_character} + 0x0020)};          $self->{ct}
1317          $self->{state} = 'tag name';              = {type => END_TAG_TOKEN,
1318          !!!next-input-character;                 tag_name => chr ($self->{nc} + 0x0020),
1319          redo A;                 line => $l, column => $c};
1320        } elsif (0x0061 <= $self->{next_input_character} and          $self->{state} = TAG_NAME_STATE;
1321                 $self->{next_input_character} <= 0x007A) { # a..z          !!!next-input-character;
1322          $self->{current_token} = {type => 'end tag',          redo A;
1323                            tag_name => chr ($self->{next_input_character})};        } elsif (0x0061 <= $self->{nc} and
1324          $self->{state} = 'tag name';                 $self->{nc} <= 0x007A) { # a..z
1325          !!!next-input-character;          !!!cp (30);
1326          redo A;          $self->{ct} = {type => END_TAG_TOKEN,
1327        } elsif ($self->{next_input_character} == 0x003E) { # >                                    tag_name => chr ($self->{nc}),
1328          !!!parse-error (type => 'empty end tag');                                    line => $l, column => $c};
1329          $self->{state} = 'data';          $self->{state} = TAG_NAME_STATE;
1330            !!!next-input-character;
1331            redo A;
1332          } elsif ($self->{nc} == 0x003E) { # >
1333            !!!cp (31);
1334            !!!parse-error (type => 'empty end tag',
1335                            line => $self->{line_prev}, ## "<" in "</>"
1336                            column => $self->{column_prev} - 1);
1337            $self->{state} = DATA_STATE;
1338          !!!next-input-character;          !!!next-input-character;
1339          redo A;          redo A;
1340        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1341            !!!cp (32);
1342          !!!parse-error (type => 'bare etago');          !!!parse-error (type => 'bare etago');
1343          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1344          # reconsume          # reconsume
1345    
1346          !!!emit ({type => 'character', data => '</'});          !!!emit ({type => CHARACTER_TOKEN, data => '</',
1347                      line => $l, column => $c,
1348                     });
1349    
1350          redo A;          redo A;
1351        } else {        } else {
1352            !!!cp (33);
1353          !!!parse-error (type => 'bogus end tag');          !!!parse-error (type => 'bogus end tag');
1354          $self->{state} = 'bogus comment';          $self->{state} = BOGUS_COMMENT_STATE;
1355          ## $self->{next_input_character} is intentionally left as is          $self->{ct} = {type => COMMENT_TOKEN, data => '',
1356          redo A;                                    line => $self->{line_prev}, # "<" of "</"
1357                                      column => $self->{column_prev} - 1,
1358                                     };
1359            ## NOTE: $self->{nc} is intentionally left as is.
1360            ## Although the "anything else" case of the spec not explicitly
1361            ## states that the next input character is to be reconsumed,
1362            ## it will be included to the |data| of the comment token
1363            ## generated from the bogus end tag, as defined in the
1364            ## "bogus comment state" entry.
1365            redo A;
1366          }
1367        } elsif ($self->{state} == CDATA_RCDATA_CLOSE_TAG_STATE) {
1368          my $ch = substr $self->{last_stag_name}, length $self->{s_kwd}, 1;
1369          if (length $ch) {
1370            my $CH = $ch;
1371            $ch =~ tr/a-z/A-Z/;
1372            my $nch = chr $self->{nc};
1373            if ($nch eq $ch or $nch eq $CH) {
1374              !!!cp (24);
1375              ## Stay in the state.
1376              $self->{s_kwd} .= $nch;
1377              !!!next-input-character;
1378              redo A;
1379            } else {
1380              !!!cp (25);
1381              $self->{state} = DATA_STATE;
1382              ## Reconsume.
1383              !!!emit ({type => CHARACTER_TOKEN,
1384                        data => '</' . $self->{s_kwd},
1385                        line => $self->{line_prev},
1386                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1387                       });
1388              redo A;
1389            }
1390          } else { # after "<{tag-name}"
1391            unless ($is_space->{$self->{nc}} or
1392                    {
1393                     0x003E => 1, # >
1394                     0x002F => 1, # /
1395                     -1 => 1, # EOF
1396                    }->{$self->{nc}}) {
1397              !!!cp (26);
1398              ## Reconsume.
1399              $self->{state} = DATA_STATE;
1400              !!!emit ({type => CHARACTER_TOKEN,
1401                        data => '</' . $self->{s_kwd},
1402                        line => $self->{line_prev},
1403                        column => $self->{column_prev} - 1 - length $self->{s_kwd},
1404                       });
1405              redo A;
1406            } else {
1407              !!!cp (27);
1408              $self->{ct}
1409                  = {type => END_TAG_TOKEN,
1410                     tag_name => $self->{last_stag_name},
1411                     line => $self->{line_prev},
1412                     column => $self->{column_prev} - 1 - length $self->{s_kwd}};
1413              $self->{state} = TAG_NAME_STATE;
1414              ## Reconsume.
1415              redo A;
1416            }
1417        }        }
1418      } elsif ($self->{state} eq 'tag name') {      } elsif ($self->{state} == TAG_NAME_STATE) {
1419        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1420            $self->{next_input_character} == 0x000A or # LF          !!!cp (34);
1421            $self->{next_input_character} == 0x000B or # VT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1422            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1423            $self->{next_input_character} == 0x0020) { # SP          redo A;
1424          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x003E) { # >
1425          !!!next-input-character;          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1426          redo A;            !!!cp (35);
1427        } elsif ($self->{next_input_character} == 0x003E) { # >            $self->{last_stag_name} = $self->{ct}->{tag_name};
1428          if ($self->{current_token}->{type} eq 'start tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
           $self->{current_token}->{first_start_tag}  
               = not defined $self->{last_emitted_start_tag_name};  
           $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};  
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1429            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1430            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1431              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This should never be reached.
1432            }            #  !!! cp (36);
1433              #  !!! parse-error (type => 'end tag attribute');
1434              #} else {
1435                !!!cp (37);
1436              #}
1437          } else {          } else {
1438            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1439          }          }
1440          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1441          !!!next-input-character;          !!!next-input-character;
1442    
1443          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1444    
1445          redo A;          redo A;
1446        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1447                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1448          $self->{current_token}->{tag_name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (38);
1449            $self->{ct}->{tag_name} .= chr ($self->{nc} + 0x0020);
1450            # start tag or end tag            # start tag or end tag
1451          ## Stay in this state          ## Stay in this state
1452          !!!next-input-character;          !!!next-input-character;
1453          redo A;          redo A;
1454        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1455          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1456          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1457            $self->{current_token}->{first_start_tag}            !!!cp (39);
1458                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1459            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1460            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1461            if ($self->{current_token}->{attributes}) {            #if ($self->{ct}->{attributes}) {
1462              !!!parse-error (type => 'end tag attribute');            #  ## NOTE: This state should never be reached.
1463            }            #  !!! cp (40);
1464              #  !!! parse-error (type => 'end tag attribute');
1465              #} else {
1466                !!!cp (41);
1467              #}
1468          } else {          } else {
1469            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1470          }          }
1471          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1472          # reconsume          # reconsume
1473    
1474          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1475    
1476          redo A;          redo A;
1477        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1478            !!!cp (42);
1479            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1480          !!!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  
1481          redo A;          redo A;
1482        } else {        } else {
1483          $self->{current_token}->{tag_name} .= chr $self->{next_input_character};          !!!cp (44);
1484            $self->{ct}->{tag_name} .= chr $self->{nc};
1485            # start tag or end tag            # start tag or end tag
1486          ## Stay in the state          ## Stay in the state
1487          !!!next-input-character;          !!!next-input-character;
1488          redo A;          redo A;
1489        }        }
1490      } elsif ($self->{state} eq 'before attribute name') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_NAME_STATE) {
1491        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1492            $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  
1493          ## Stay in the state          ## Stay in the state
1494          !!!next-input-character;          !!!next-input-character;
1495          redo A;          redo A;
1496        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1497          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1498            $self->{current_token}->{first_start_tag}            !!!cp (46);
1499                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1500            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1501            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1502            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1503                !!!cp (47);
1504              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1505              } else {
1506                !!!cp (48);
1507            }            }
1508          } else {          } else {
1509            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1510          }          }
1511          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1512          !!!next-input-character;          !!!next-input-character;
1513    
1514          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1515    
1516          redo A;          redo A;
1517        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1518                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1519          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (49);
1520                                value => ''};          $self->{ca}
1521          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1522                   value => '',
1523                   line => $self->{line}, column => $self->{column}};
1524            $self->{state} = ATTRIBUTE_NAME_STATE;
1525          !!!next-input-character;          !!!next-input-character;
1526          redo A;          redo A;
1527        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1528            !!!cp (50);
1529            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1530          !!!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  
1531          redo A;          redo A;
1532        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1533          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1534          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1535            $self->{current_token}->{first_start_tag}            !!!cp (52);
1536                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1537            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1538            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1539            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1540                !!!cp (53);
1541              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1542              } else {
1543                !!!cp (54);
1544            }            }
1545          } else {          } else {
1546            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1547          }          }
1548          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1549          # reconsume          # reconsume
1550    
1551          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1552    
1553          redo A;          redo A;
1554        } else {        } else {
1555          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ({
1556                                value => ''};               0x0022 => 1, # "
1557          $self->{state} = 'attribute name';               0x0027 => 1, # '
1558                 0x003D => 1, # =
1559                }->{$self->{nc}}) {
1560              !!!cp (55);
1561              !!!parse-error (type => 'bad attribute name');
1562            } else {
1563              !!!cp (56);
1564            }
1565            $self->{ca}
1566                = {name => chr ($self->{nc}),
1567                   value => '',
1568                   line => $self->{line}, column => $self->{column}};
1569            $self->{state} = ATTRIBUTE_NAME_STATE;
1570          !!!next-input-character;          !!!next-input-character;
1571          redo A;          redo A;
1572        }        }
1573      } elsif ($self->{state} eq 'attribute name') {      } elsif ($self->{state} == ATTRIBUTE_NAME_STATE) {
1574        my $before_leave = sub {        my $before_leave = sub {
1575          if (exists $self->{current_token}->{attributes} # start tag or end tag          if (exists $self->{ct}->{attributes} # start tag or end tag
1576              ->{$self->{current_attribute}->{name}}) { # MUST              ->{$self->{ca}->{name}}) { # MUST
1577            !!!parse-error (type => 'duplicate attribute:'.$self->{current_attribute}->{name});            !!!cp (57);
1578            ## Discard $self->{current_attribute} # MUST            !!!parse-error (type => 'duplicate attribute', text => $self->{ca}->{name}, line => $self->{ca}->{line}, column => $self->{ca}->{column});
1579          } else {            ## Discard $self->{ca} # MUST
1580            $self->{current_token}->{attributes}->{$self->{current_attribute}->{name}}          } else {
1581              = $self->{current_attribute};            !!!cp (58);
1582              $self->{ct}->{attributes}->{$self->{ca}->{name}}
1583                = $self->{ca};
1584          }          }
1585        }; # $before_leave        }; # $before_leave
1586    
1587        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1588            $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  
1589          $before_leave->();          $before_leave->();
1590          $self->{state} = 'after attribute name';          $self->{state} = AFTER_ATTRIBUTE_NAME_STATE;
1591          !!!next-input-character;          !!!next-input-character;
1592          redo A;          redo A;
1593        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1594            !!!cp (60);
1595          $before_leave->();          $before_leave->();
1596          $self->{state} = 'before attribute value';          $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1597          !!!next-input-character;          !!!next-input-character;
1598          redo A;          redo A;
1599        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1600          $before_leave->();          $before_leave->();
1601          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1602            $self->{current_token}->{first_start_tag}            !!!cp (61);
1603                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1604            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1605          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (62);
1606            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1607            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1608              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1609            }            }
1610          } else {          } else {
1611            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1612          }          }
1613          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1614          !!!next-input-character;          !!!next-input-character;
1615    
1616          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1617    
1618          redo A;          redo A;
1619        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1620                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1621          $self->{current_attribute}->{name} .= chr ($self->{next_input_character} + 0x0020);          !!!cp (63);
1622            $self->{ca}->{name} .= chr ($self->{nc} + 0x0020);
1623          ## Stay in the state          ## Stay in the state
1624          !!!next-input-character;          !!!next-input-character;
1625          redo A;          redo A;
1626        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1627            !!!cp (64);
1628          $before_leave->();          $before_leave->();
1629            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1630          !!!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  
1631          redo A;          redo A;
1632        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1633          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1634          $before_leave->();          $before_leave->();
1635          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1636            $self->{current_token}->{first_start_tag}            !!!cp (66);
1637                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1638            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1639            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1640            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1641                !!!cp (67);
1642              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1643              } else {
1644                ## NOTE: This state should never be reached.
1645                !!!cp (68);
1646            }            }
1647          } else {          } else {
1648            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1649          }          }
1650          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1651          # reconsume          # reconsume
1652    
1653          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1654    
1655          redo A;          redo A;
1656        } else {        } else {
1657          $self->{current_attribute}->{name} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x0022 or # "
1658                $self->{nc} == 0x0027) { # '
1659              !!!cp (69);
1660              !!!parse-error (type => 'bad attribute name');
1661            } else {
1662              !!!cp (70);
1663            }
1664            $self->{ca}->{name} .= chr ($self->{nc});
1665          ## Stay in the state          ## Stay in the state
1666          !!!next-input-character;          !!!next-input-character;
1667          redo A;          redo A;
1668        }        }
1669      } elsif ($self->{state} eq 'after attribute name') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_NAME_STATE) {
1670        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1671            $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  
1672          ## Stay in the state          ## Stay in the state
1673          !!!next-input-character;          !!!next-input-character;
1674          redo A;          redo A;
1675        } elsif ($self->{next_input_character} == 0x003D) { # =        } elsif ($self->{nc} == 0x003D) { # =
1676          $self->{state} = 'before attribute value';          !!!cp (72);
1677            $self->{state} = BEFORE_ATTRIBUTE_VALUE_STATE;
1678          !!!next-input-character;          !!!next-input-character;
1679          redo A;          redo A;
1680        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1681          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1682            $self->{current_token}->{first_start_tag}            !!!cp (73);
1683                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1684            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1685            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1686            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1687                !!!cp (74);
1688              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1689              } else {
1690                ## NOTE: This state should never be reached.
1691                !!!cp (75);
1692            }            }
1693          } else {          } else {
1694            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1695          }          }
1696          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1697          !!!next-input-character;          !!!next-input-character;
1698    
1699          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1700    
1701          redo A;          redo A;
1702        } elsif (0x0041 <= $self->{next_input_character} and        } elsif (0x0041 <= $self->{nc} and
1703                 $self->{next_input_character} <= 0x005A) { # A..Z                 $self->{nc} <= 0x005A) { # A..Z
1704          $self->{current_attribute} = {name => chr ($self->{next_input_character} + 0x0020),          !!!cp (76);
1705                                value => ''};          $self->{ca}
1706          $self->{state} = 'attribute name';              = {name => chr ($self->{nc} + 0x0020),
1707                   value => '',
1708                   line => $self->{line}, column => $self->{column}};
1709            $self->{state} = ATTRIBUTE_NAME_STATE;
1710          !!!next-input-character;          !!!next-input-character;
1711          redo A;          redo A;
1712        } elsif ($self->{next_input_character} == 0x002F) { # /        } elsif ($self->{nc} == 0x002F) { # /
1713            !!!cp (77);
1714            $self->{state} = SELF_CLOSING_START_TAG_STATE;
1715          !!!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  
1716          redo A;          redo A;
1717        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1718          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1719          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1720            $self->{current_token}->{first_start_tag}            !!!cp (79);
1721                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1722            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1723            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1724            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1725                !!!cp (80);
1726              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1727              } else {
1728                ## NOTE: This state should never be reached.
1729                !!!cp (81);
1730            }            }
1731          } else {          } else {
1732            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1733          }          }
1734          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1735          # reconsume          # reconsume
1736    
1737          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1738    
1739          redo A;          redo A;
1740        } else {        } else {
1741          $self->{current_attribute} = {name => chr ($self->{next_input_character}),          if ($self->{nc} == 0x0022 or # "
1742                                value => ''};              $self->{nc} == 0x0027) { # '
1743          $self->{state} = 'attribute name';            !!!cp (78);
1744              !!!parse-error (type => 'bad attribute name');
1745            } else {
1746              !!!cp (82);
1747            }
1748            $self->{ca}
1749                = {name => chr ($self->{nc}),
1750                   value => '',
1751                   line => $self->{line}, column => $self->{column}};
1752            $self->{state} = ATTRIBUTE_NAME_STATE;
1753          !!!next-input-character;          !!!next-input-character;
1754          redo A;                  redo A;        
1755        }        }
1756      } elsif ($self->{state} eq 'before attribute value') {      } elsif ($self->{state} == BEFORE_ATTRIBUTE_VALUE_STATE) {
1757        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1758            $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        
1759          ## Stay in the state          ## Stay in the state
1760          !!!next-input-character;          !!!next-input-character;
1761          redo A;          redo A;
1762        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
1763          $self->{state} = 'attribute value (double-quoted)';          !!!cp (84);
1764            $self->{state} = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
1765          !!!next-input-character;          !!!next-input-character;
1766          redo A;          redo A;
1767        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1768          $self->{state} = 'attribute value (unquoted)';          !!!cp (85);
1769            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1770          ## reconsume          ## reconsume
1771          redo A;          redo A;
1772        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
1773          $self->{state} = 'attribute value (single-quoted)';          !!!cp (86);
1774            $self->{state} = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
1775          !!!next-input-character;          !!!next-input-character;
1776          redo A;          redo A;
1777        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
1778          if ($self->{current_token}->{type} eq 'start tag') {          !!!parse-error (type => 'empty unquoted attribute value');
1779            $self->{current_token}->{first_start_tag}          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1780                = not defined $self->{last_emitted_start_tag_name};            !!!cp (87);
1781            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1782          } elsif ($self->{current_token}->{type} eq 'end tag') {          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1783            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1784            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1785                !!!cp (88);
1786              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1787              } else {
1788                ## NOTE: This state should never be reached.
1789                !!!cp (89);
1790            }            }
1791          } else {          } else {
1792            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1793          }          }
1794          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1795          !!!next-input-character;          !!!next-input-character;
1796    
1797          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1798    
1799          redo A;          redo A;
1800        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1801          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1802          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1803            $self->{current_token}->{first_start_tag}            !!!cp (90);
1804                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1805            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1806            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1807            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1808                !!!cp (91);
1809              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1810              } else {
1811                ## NOTE: This state should never be reached.
1812                !!!cp (92);
1813            }            }
1814          } else {          } else {
1815            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1816          }          }
1817          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1818          ## reconsume          ## reconsume
1819    
1820          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1821    
1822          redo A;          redo A;
1823        } else {        } else {
1824          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ($self->{nc} == 0x003D) { # =
1825          $self->{state} = 'attribute value (unquoted)';            !!!cp (93);
1826              !!!parse-error (type => 'bad attribute value');
1827            } else {
1828              !!!cp (94);
1829            }
1830            $self->{ca}->{value} .= chr ($self->{nc});
1831            $self->{state} = ATTRIBUTE_VALUE_UNQUOTED_STATE;
1832          !!!next-input-character;          !!!next-input-character;
1833          redo A;          redo A;
1834        }        }
1835      } elsif ($self->{state} eq 'attribute value (double-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE) {
1836        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
1837          $self->{state} = 'before attribute name';          !!!cp (95);
1838            $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1839          !!!next-input-character;          !!!next-input-character;
1840          redo A;          redo A;
1841        } elsif ($self->{next_input_character} == 0x0026) { # &        } elsif ($self->{nc} == 0x0026) { # &
1842          $self->{last_attribute_value_state} = 'attribute value (double-quoted)';          !!!cp (96);
1843          $self->{state} = 'entity in attribute value';          ## NOTE: In the spec, the tokenizer is switched to the
1844            ## "entity in attribute value state".  In this implementation, the
1845            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1846            ## implementation of the "consume a character reference" algorithm.
1847            $self->{prev_state} = $self->{state};
1848            $self->{entity_add} = 0x0022; # "
1849            $self->{state} = ENTITY_STATE;
1850          !!!next-input-character;          !!!next-input-character;
1851          redo A;          redo A;
1852        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1853          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1854          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1855            $self->{current_token}->{first_start_tag}            !!!cp (97);
1856                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1857            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1858            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1859            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1860                !!!cp (98);
1861              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1862              } else {
1863                ## NOTE: This state should never be reached.
1864                !!!cp (99);
1865            }            }
1866          } else {          } else {
1867            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1868          }          }
1869          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1870          ## reconsume          ## reconsume
1871    
1872          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1873    
1874          redo A;          redo A;
1875        } else {        } else {
1876          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (100);
1877            $self->{ca}->{value} .= chr ($self->{nc});
1878            $self->{read_until}->($self->{ca}->{value},
1879                                  q["&],
1880                                  length $self->{ca}->{value});
1881    
1882          ## Stay in the state          ## Stay in the state
1883          !!!next-input-character;          !!!next-input-character;
1884          redo A;          redo A;
1885        }        }
1886      } elsif ($self->{state} eq 'attribute value (single-quoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE) {
1887        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
1888          $self->{state} = 'before attribute name';          !!!cp (101);
1889          !!!next-input-character;          $self->{state} = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
1890          redo A;          !!!next-input-character;
1891        } elsif ($self->{next_input_character} == 0x0026) { # &          redo A;
1892          $self->{last_attribute_value_state} = 'attribute value (single-quoted)';        } elsif ($self->{nc} == 0x0026) { # &
1893          $self->{state} = 'entity in attribute value';          !!!cp (102);
1894            ## NOTE: In the spec, the tokenizer is switched to the
1895            ## "entity in attribute value state".  In this implementation, the
1896            ## tokenizer is switched to the |ENTITY_STATE|, which is an
1897            ## implementation of the "consume a character reference" algorithm.
1898            $self->{entity_add} = 0x0027; # '
1899            $self->{prev_state} = $self->{state};
1900            $self->{state} = ENTITY_STATE;
1901          !!!next-input-character;          !!!next-input-character;
1902          redo A;          redo A;
1903        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1904          !!!parse-error (type => 'unclosed attribute value');          !!!parse-error (type => 'unclosed attribute value');
1905          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1906            $self->{current_token}->{first_start_tag}            !!!cp (103);
1907                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1908            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1909            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1910            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1911                !!!cp (104);
1912              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1913              } else {
1914                ## NOTE: This state should never be reached.
1915                !!!cp (105);
1916            }            }
1917          } else {          } else {
1918            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1919          }          }
1920          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1921          ## reconsume          ## reconsume
1922    
1923          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1924    
1925          redo A;          redo A;
1926        } else {        } else {
1927          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          !!!cp (106);
1928            $self->{ca}->{value} .= chr ($self->{nc});
1929            $self->{read_until}->($self->{ca}->{value},
1930                                  q['&],
1931                                  length $self->{ca}->{value});
1932    
1933          ## Stay in the state          ## Stay in the state
1934          !!!next-input-character;          !!!next-input-character;
1935          redo A;          redo A;
1936        }        }
1937      } elsif ($self->{state} eq 'attribute value (unquoted)') {      } elsif ($self->{state} == ATTRIBUTE_VALUE_UNQUOTED_STATE) {
1938        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
1939            $self->{next_input_character} == 0x000A or # LF          !!!cp (107);
1940            $self->{next_input_character} == 0x000B or # HT          $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
1941            $self->{next_input_character} == 0x000C or # FF          !!!next-input-character;
1942            $self->{next_input_character} == 0x0020) { # SP          redo A;
1943          $self->{state} = 'before attribute name';        } elsif ($self->{nc} == 0x0026) { # &
1944          !!!next-input-character;          !!!cp (108);
1945          redo A;          ## NOTE: In the spec, the tokenizer is switched to the
1946        } elsif ($self->{next_input_character} == 0x0026) { # &          ## "entity in attribute value state".  In this implementation, the
1947          $self->{last_attribute_value_state} = 'attribute value (unquoted)';          ## tokenizer is switched to the |ENTITY_STATE|, which is an
1948          $self->{state} = 'entity in attribute value';          ## implementation of the "consume a character reference" algorithm.
1949          !!!next-input-character;          $self->{entity_add} = -1;
1950          redo A;          $self->{prev_state} = $self->{state};
1951        } elsif ($self->{next_input_character} == 0x003E) { # >          $self->{state} = ENTITY_STATE;
1952          if ($self->{current_token}->{type} eq 'start tag') {          !!!next-input-character;
1953            $self->{current_token}->{first_start_tag}          redo A;
1954                = not defined $self->{last_emitted_start_tag_name};        } elsif ($self->{nc} == 0x003E) { # >
1955            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1956          } elsif ($self->{current_token}->{type} eq 'end tag') {            !!!cp (109);
1957              $self->{last_stag_name} = $self->{ct}->{tag_name};
1958            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
1959            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1960            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1961                !!!cp (110);
1962              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1963              } else {
1964                ## NOTE: This state should never be reached.
1965                !!!cp (111);
1966            }            }
1967          } else {          } else {
1968            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1969          }          }
1970          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1971          !!!next-input-character;          !!!next-input-character;
1972    
1973          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1974    
1975          redo A;          redo A;
1976        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
1977          !!!parse-error (type => 'unclosed tag');          !!!parse-error (type => 'unclosed tag');
1978          if ($self->{current_token}->{type} eq 'start tag') {          if ($self->{ct}->{type} == START_TAG_TOKEN) {
1979            $self->{current_token}->{first_start_tag}            !!!cp (112);
1980                = not defined $self->{last_emitted_start_tag_name};            $self->{last_stag_name} = $self->{ct}->{tag_name};
1981            $self->{last_emitted_start_tag_name} = $self->{current_token}->{tag_name};          } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
         } elsif ($self->{current_token}->{type} eq 'end tag') {  
1982            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST            $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
1983            if ($self->{current_token}->{attributes}) {            if ($self->{ct}->{attributes}) {
1984                !!!cp (113);
1985              !!!parse-error (type => 'end tag attribute');              !!!parse-error (type => 'end tag attribute');
1986              } else {
1987                ## NOTE: This state should never be reached.
1988                !!!cp (114);
1989            }            }
1990          } else {          } else {
1991            die "$0: $self->{current_token}->{type}: Unknown token type";            die "$0: $self->{ct}->{type}: Unknown token type";
1992          }          }
1993          $self->{state} = 'data';          $self->{state} = DATA_STATE;
1994          ## reconsume          ## reconsume
1995    
1996          !!!emit ($self->{current_token}); # start tag or end tag          !!!emit ($self->{ct}); # start tag or end tag
1997    
1998          redo A;          redo A;
1999        } else {        } else {
2000          $self->{current_attribute}->{value} .= chr ($self->{next_input_character});          if ({
2001                 0x0022 => 1, # "
2002                 0x0027 => 1, # '
2003                 0x003D => 1, # =
2004                }->{$self->{nc}}) {
2005              !!!cp (115);
2006              !!!parse-error (type => 'bad attribute value');
2007            } else {
2008              !!!cp (116);
2009            }
2010            $self->{ca}->{value} .= chr ($self->{nc});
2011            $self->{read_until}->($self->{ca}->{value},
2012                                  q["'=& >],
2013                                  length $self->{ca}->{value});
2014    
2015          ## Stay in the state          ## Stay in the state
2016          !!!next-input-character;          !!!next-input-character;
2017          redo A;          redo A;
2018        }        }
2019      } elsif ($self->{state} eq 'entity in attribute value') {      } elsif ($self->{state} == AFTER_ATTRIBUTE_VALUE_QUOTED_STATE) {
2020        my $token = $self->_tokenize_attempt_to_consume_an_entity (1);        if ($is_space->{$self->{nc}}) {
2021            !!!cp (118);
2022            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2023            !!!next-input-character;
2024            redo A;
2025          } elsif ($self->{nc} == 0x003E) { # >
2026            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2027              !!!cp (119);
2028              $self->{last_stag_name} = $self->{ct}->{tag_name};
2029            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2030              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2031              if ($self->{ct}->{attributes}) {
2032                !!!cp (120);
2033                !!!parse-error (type => 'end tag attribute');
2034              } else {
2035                ## NOTE: This state should never be reached.
2036                !!!cp (121);
2037              }
2038            } else {
2039              die "$0: $self->{ct}->{type}: Unknown token type";
2040            }
2041            $self->{state} = DATA_STATE;
2042            !!!next-input-character;
2043    
2044            !!!emit ($self->{ct}); # start tag or end tag
2045    
2046        unless (defined $token) {          redo A;
2047          $self->{current_attribute}->{value} .= '&';        } elsif ($self->{nc} == 0x002F) { # /
2048            !!!cp (122);
2049            $self->{state} = SELF_CLOSING_START_TAG_STATE;
2050            !!!next-input-character;
2051            redo A;
2052          } elsif ($self->{nc} == -1) {
2053            !!!parse-error (type => 'unclosed tag');
2054            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2055              !!!cp (122.3);
2056              $self->{last_stag_name} = $self->{ct}->{tag_name};
2057            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2058              if ($self->{ct}->{attributes}) {
2059                !!!cp (122.1);
2060                !!!parse-error (type => 'end tag attribute');
2061              } else {
2062                ## NOTE: This state should never be reached.
2063                !!!cp (122.2);
2064              }
2065            } else {
2066              die "$0: $self->{ct}->{type}: Unknown token type";
2067            }
2068            $self->{state} = DATA_STATE;
2069            ## Reconsume.
2070            !!!emit ($self->{ct}); # start tag or end tag
2071            redo A;
2072        } else {        } else {
2073          $self->{current_attribute}->{value} .= $token->{data};          !!!cp ('124.1');
2074          ## ISSUE: spec says "append the returned character token to the current attribute's value"          !!!parse-error (type => 'no space between attributes');
2075            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2076            ## reconsume
2077            redo A;
2078        }        }
2079        } elsif ($self->{state} == SELF_CLOSING_START_TAG_STATE) {
2080          if ($self->{nc} == 0x003E) { # >
2081            if ($self->{ct}->{type} == END_TAG_TOKEN) {
2082              !!!cp ('124.2');
2083              !!!parse-error (type => 'nestc', token => $self->{ct});
2084              ## TODO: Different type than slash in start tag
2085              $self->{content_model} = PCDATA_CONTENT_MODEL; # MUST
2086              if ($self->{ct}->{attributes}) {
2087                !!!cp ('124.4');
2088                !!!parse-error (type => 'end tag attribute');
2089              } else {
2090                !!!cp ('124.5');
2091              }
2092              ## TODO: Test |<title></title/>|
2093            } else {
2094              !!!cp ('124.3');
2095              $self->{self_closing} = 1;
2096            }
2097    
2098        $self->{state} = $self->{last_attribute_value_state};          $self->{state} = DATA_STATE;
2099        # 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  
2100    
2101            !!!emit ($token);          !!!emit ($self->{ct}); # start tag or end tag
2102    
2103            redo A;          redo A;
2104          } elsif ($self->{nc} == -1) {
2105            !!!parse-error (type => 'unclosed tag');
2106            if ($self->{ct}->{type} == START_TAG_TOKEN) {
2107              !!!cp (124.7);
2108              $self->{last_stag_name} = $self->{ct}->{tag_name};
2109            } elsif ($self->{ct}->{type} == END_TAG_TOKEN) {
2110              if ($self->{ct}->{attributes}) {
2111                !!!cp (124.5);
2112                !!!parse-error (type => 'end tag attribute');
2113              } else {
2114                ## NOTE: This state should never be reached.
2115                !!!cp (124.6);
2116              }
2117          } else {          } else {
2118            $token->{data} .= chr ($self->{next_input_character});            die "$0: $self->{ct}->{type}: Unknown token type";
           !!!next-input-character;  
           redo BC;  
2119          }          }
2120        } # BC          $self->{state} = DATA_STATE;
2121      } elsif ($self->{state} eq 'markup declaration open') {          ## Reconsume.
2122            !!!emit ($self->{ct}); # start tag or end tag
2123            redo A;
2124          } else {
2125            !!!cp ('124.4');
2126            !!!parse-error (type => 'nestc');
2127            ## TODO: This error type is wrong.
2128            $self->{state} = BEFORE_ATTRIBUTE_NAME_STATE;
2129            ## Reconsume.
2130            redo A;
2131          }
2132        } elsif ($self->{state} == BOGUS_COMMENT_STATE) {
2133        ## (only happen if PCDATA state)        ## (only happen if PCDATA state)
2134    
2135        my @next_char;        ## NOTE: Unlike spec's "bogus comment state", this implementation
2136        push @next_char, $self->{next_input_character};        ## consumes characters one-by-one basis.
2137                
2138        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x003E) { # >
2139            !!!cp (124);
2140            $self->{state} = DATA_STATE;
2141          !!!next-input-character;          !!!next-input-character;
2142          push @next_char, $self->{next_input_character};  
2143          if ($self->{next_input_character} == 0x002D) { # -          !!!emit ($self->{ct}); # comment
2144            $self->{current_token} = {type => 'comment', data => ''};          redo A;
2145            $self->{state} = 'comment start';        } elsif ($self->{nc} == -1) {
2146            !!!next-input-character;          !!!cp (125);
2147            redo A;          $self->{state} = DATA_STATE;
2148          }          ## reconsume
2149        } elsif ($self->{next_input_character} == 0x0044 or # D  
2150                 $self->{next_input_character} == 0x0064) { # d          !!!emit ($self->{ct}); # comment
2151            redo A;
2152          } else {
2153            !!!cp (126);
2154            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2155            $self->{read_until}->($self->{ct}->{data},
2156                                  q[>],
2157                                  length $self->{ct}->{data});
2158    
2159            ## Stay in the state.
2160          !!!next-input-character;          !!!next-input-character;
2161          push @next_char, $self->{next_input_character};          redo A;
2162          if ($self->{next_input_character} == 0x004F or # O        }
2163              $self->{next_input_character} == 0x006F) { # o      } elsif ($self->{state} == MARKUP_DECLARATION_OPEN_STATE) {
2164            !!!next-input-character;        ## (only happen if PCDATA state)
2165            push @next_char, $self->{next_input_character};        
2166            if ($self->{next_input_character} == 0x0043 or # C        if ($self->{nc} == 0x002D) { # -
2167                $self->{next_input_character} == 0x0063) { # c          !!!cp (133);
2168              !!!next-input-character;          $self->{state} = MD_HYPHEN_STATE;
2169              push @next_char, $self->{next_input_character};          !!!next-input-character;
2170              if ($self->{next_input_character} == 0x0054 or # T          redo A;
2171                  $self->{next_input_character} == 0x0074) { # t        } elsif ($self->{nc} == 0x0044 or # D
2172                !!!next-input-character;                 $self->{nc} == 0x0064) { # d
2173                push @next_char, $self->{next_input_character};          ## ASCII case-insensitive.
2174                if ($self->{next_input_character} == 0x0059 or # Y          !!!cp (130);
2175                    $self->{next_input_character} == 0x0079) { # y          $self->{state} = MD_DOCTYPE_STATE;
2176                  !!!next-input-character;          $self->{s_kwd} = chr $self->{nc};
2177                  push @next_char, $self->{next_input_character};          !!!next-input-character;
2178                  if ($self->{next_input_character} == 0x0050 or # P          redo A;
2179                      $self->{next_input_character} == 0x0070) { # p        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM and
2180                    !!!next-input-character;                 $self->{open_elements}->[-1]->[1] & FOREIGN_EL and
2181                    push @next_char, $self->{next_input_character};                 $self->{nc} == 0x005B) { # [
2182                    if ($self->{next_input_character} == 0x0045 or # E          !!!cp (135.4);                
2183                        $self->{next_input_character} == 0x0065) { # e          $self->{state} = MD_CDATA_STATE;
2184                      ## ISSUE: What a stupid code this is!          $self->{s_kwd} = '[';
2185                      $self->{state} = 'DOCTYPE';          !!!next-input-character;
2186                      !!!next-input-character;          redo A;
2187                      redo A;        } else {
2188                    }          !!!cp (136);
                 }  
               }  
             }  
           }  
         }  
2189        }        }
2190    
2191        !!!parse-error (type => 'bogus comment');        !!!parse-error (type => 'bogus comment',
2192        $self->{next_input_character} = shift @next_char;                        line => $self->{line_prev},
2193        !!!back-next-input-character (@next_char);                        column => $self->{column_prev} - 1);
2194        $self->{state} = 'bogus comment';        ## Reconsume.
2195          $self->{state} = BOGUS_COMMENT_STATE;
2196          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2197                                    line => $self->{line_prev},
2198                                    column => $self->{column_prev} - 1,
2199                                   };
2200        redo A;        redo A;
2201              } elsif ($self->{state} == MD_HYPHEN_STATE) {
2202        ## ISSUE: typos in spec: chacacters, is is a parse error        if ($self->{nc} == 0x002D) { # -
2203        ## 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);
2204      } elsif ($self->{state} eq 'comment start') {          $self->{ct} = {type => COMMENT_TOKEN, data => '',
2205        if ($self->{next_input_character} == 0x002D) { # -                                    line => $self->{line_prev},
2206          $self->{state} = 'comment start dash';                                    column => $self->{column_prev} - 2,
2207                                     };
2208            $self->{state} = COMMENT_START_STATE;
2209            !!!next-input-character;
2210            redo A;
2211          } else {
2212            !!!cp (128);
2213            !!!parse-error (type => 'bogus comment',
2214                            line => $self->{line_prev},
2215                            column => $self->{column_prev} - 2);
2216            $self->{state} = BOGUS_COMMENT_STATE;
2217            ## Reconsume.
2218            $self->{ct} = {type => COMMENT_TOKEN,
2219                                      data => '-',
2220                                      line => $self->{line_prev},
2221                                      column => $self->{column_prev} - 2,
2222                                     };
2223            redo A;
2224          }
2225        } elsif ($self->{state} == MD_DOCTYPE_STATE) {
2226          ## ASCII case-insensitive.
2227          if ($self->{nc} == [
2228                undef,
2229                0x004F, # O
2230                0x0043, # C
2231                0x0054, # T
2232                0x0059, # Y
2233                0x0050, # P
2234              ]->[length $self->{s_kwd}] or
2235              $self->{nc} == [
2236                undef,
2237                0x006F, # o
2238                0x0063, # c
2239                0x0074, # t
2240                0x0079, # y
2241                0x0070, # p
2242              ]->[length $self->{s_kwd}]) {
2243            !!!cp (131);
2244            ## Stay in the state.
2245            $self->{s_kwd} .= chr $self->{nc};
2246            !!!next-input-character;
2247            redo A;
2248          } elsif ((length $self->{s_kwd}) == 6 and
2249                   ($self->{nc} == 0x0045 or # E
2250                    $self->{nc} == 0x0065)) { # e
2251            !!!cp (129);
2252            $self->{state} = DOCTYPE_STATE;
2253            $self->{ct} = {type => DOCTYPE_TOKEN,
2254                                      quirks => 1,
2255                                      line => $self->{line_prev},
2256                                      column => $self->{column_prev} - 7,
2257                                     };
2258            !!!next-input-character;
2259            redo A;
2260          } else {
2261            !!!cp (132);        
2262            !!!parse-error (type => 'bogus comment',
2263                            line => $self->{line_prev},
2264                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2265            $self->{state} = BOGUS_COMMENT_STATE;
2266            ## Reconsume.
2267            $self->{ct} = {type => COMMENT_TOKEN,
2268                                      data => $self->{s_kwd},
2269                                      line => $self->{line_prev},
2270                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2271                                     };
2272            redo A;
2273          }
2274        } elsif ($self->{state} == MD_CDATA_STATE) {
2275          if ($self->{nc} == {
2276                '[' => 0x0043, # C
2277                '[C' => 0x0044, # D
2278                '[CD' => 0x0041, # A
2279                '[CDA' => 0x0054, # T
2280                '[CDAT' => 0x0041, # A
2281              }->{$self->{s_kwd}}) {
2282            !!!cp (135.1);
2283            ## Stay in the state.
2284            $self->{s_kwd} .= chr $self->{nc};
2285            !!!next-input-character;
2286            redo A;
2287          } elsif ($self->{s_kwd} eq '[CDATA' and
2288                   $self->{nc} == 0x005B) { # [
2289            !!!cp (135.2);
2290            $self->{ct} = {type => CHARACTER_TOKEN,
2291                                      data => '',
2292                                      line => $self->{line_prev},
2293                                      column => $self->{column_prev} - 7};
2294            $self->{state} = CDATA_SECTION_STATE;
2295            !!!next-input-character;
2296            redo A;
2297          } else {
2298            !!!cp (135.3);
2299            !!!parse-error (type => 'bogus comment',
2300                            line => $self->{line_prev},
2301                            column => $self->{column_prev} - 1 - length $self->{s_kwd});
2302            $self->{state} = BOGUS_COMMENT_STATE;
2303            ## Reconsume.
2304            $self->{ct} = {type => COMMENT_TOKEN,
2305                                      data => $self->{s_kwd},
2306                                      line => $self->{line_prev},
2307                                      column => $self->{column_prev} - 1 - length $self->{s_kwd},
2308                                     };
2309            redo A;
2310          }
2311        } elsif ($self->{state} == COMMENT_START_STATE) {
2312          if ($self->{nc} == 0x002D) { # -
2313            !!!cp (137);
2314            $self->{state} = COMMENT_START_DASH_STATE;
2315          !!!next-input-character;          !!!next-input-character;
2316          redo A;          redo A;
2317        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2318            !!!cp (138);
2319          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2320          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2321          !!!next-input-character;          !!!next-input-character;
2322    
2323          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2324    
2325          redo A;          redo A;
2326        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2327            !!!cp (139);
2328          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2329          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2330          ## reconsume          ## reconsume
2331    
2332          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2333    
2334          redo A;          redo A;
2335        } else {        } else {
2336          $self->{current_token}->{data} # comment          !!!cp (140);
2337              .= chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2338          $self->{state} = 'comment';              .= chr ($self->{nc});
2339            $self->{state} = COMMENT_STATE;
2340          !!!next-input-character;          !!!next-input-character;
2341          redo A;          redo A;
2342        }        }
2343      } elsif ($self->{state} eq 'comment start dash') {      } elsif ($self->{state} == COMMENT_START_DASH_STATE) {
2344        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2345          $self->{state} = 'comment end';          !!!cp (141);
2346            $self->{state} = COMMENT_END_STATE;
2347          !!!next-input-character;          !!!next-input-character;
2348          redo A;          redo A;
2349        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2350            !!!cp (142);
2351          !!!parse-error (type => 'bogus comment');          !!!parse-error (type => 'bogus comment');
2352          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2353          !!!next-input-character;          !!!next-input-character;
2354    
2355          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2356    
2357          redo A;          redo A;
2358        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2359            !!!cp (143);
2360          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2361          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2362          ## reconsume          ## reconsume
2363    
2364          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2365    
2366          redo A;          redo A;
2367        } else {        } else {
2368          $self->{current_token}->{data} # comment          !!!cp (144);
2369              .= '-' . chr ($self->{next_input_character});          $self->{ct}->{data} # comment
2370          $self->{state} = 'comment';              .= '-' . chr ($self->{nc});
2371            $self->{state} = COMMENT_STATE;
2372          !!!next-input-character;          !!!next-input-character;
2373          redo A;          redo A;
2374        }        }
2375      } elsif ($self->{state} eq 'comment') {      } elsif ($self->{state} == COMMENT_STATE) {
2376        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2377          $self->{state} = 'comment end dash';          !!!cp (145);
2378            $self->{state} = COMMENT_END_DASH_STATE;
2379          !!!next-input-character;          !!!next-input-character;
2380          redo A;          redo A;
2381        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2382            !!!cp (146);
2383          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2384          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2385          ## reconsume          ## reconsume
2386    
2387          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2388    
2389          redo A;          redo A;
2390        } else {        } else {
2391          $self->{current_token}->{data} .= chr ($self->{next_input_character}); # comment          !!!cp (147);
2392            $self->{ct}->{data} .= chr ($self->{nc}); # comment
2393            $self->{read_until}->($self->{ct}->{data},
2394                                  q[-],
2395                                  length $self->{ct}->{data});
2396    
2397          ## Stay in the state          ## Stay in the state
2398          !!!next-input-character;          !!!next-input-character;
2399          redo A;          redo A;
2400        }        }
2401      } elsif ($self->{state} eq 'comment end dash') {      } elsif ($self->{state} == COMMENT_END_DASH_STATE) {
2402        if ($self->{next_input_character} == 0x002D) { # -        if ($self->{nc} == 0x002D) { # -
2403          $self->{state} = 'comment end';          !!!cp (148);
2404            $self->{state} = COMMENT_END_STATE;
2405          !!!next-input-character;          !!!next-input-character;
2406          redo A;          redo A;
2407        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2408            !!!cp (149);
2409          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2410          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2411          ## reconsume          ## reconsume
2412    
2413          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2414    
2415          redo A;          redo A;
2416        } else {        } else {
2417          $self->{current_token}->{data} .= '-' . chr ($self->{next_input_character}); # comment          !!!cp (150);
2418          $self->{state} = 'comment';          $self->{ct}->{data} .= '-' . chr ($self->{nc}); # comment
2419            $self->{state} = COMMENT_STATE;
2420          !!!next-input-character;          !!!next-input-character;
2421          redo A;          redo A;
2422        }        }
2423      } elsif ($self->{state} eq 'comment end') {      } elsif ($self->{state} == COMMENT_END_STATE) {
2424        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2425          $self->{state} = 'data';          !!!cp (151);
2426            $self->{state} = DATA_STATE;
2427          !!!next-input-character;          !!!next-input-character;
2428    
2429          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2430    
2431          redo A;          redo A;
2432        } elsif ($self->{next_input_character} == 0x002D) { # -        } elsif ($self->{nc} == 0x002D) { # -
2433          !!!parse-error (type => 'dash in comment');          !!!cp (152);
2434          $self->{current_token}->{data} .= '-'; # comment          !!!parse-error (type => 'dash in comment',
2435                            line => $self->{line_prev},
2436                            column => $self->{column_prev});
2437            $self->{ct}->{data} .= '-'; # comment
2438          ## Stay in the state          ## Stay in the state
2439          !!!next-input-character;          !!!next-input-character;
2440          redo A;          redo A;
2441        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2442            !!!cp (153);
2443          !!!parse-error (type => 'unclosed comment');          !!!parse-error (type => 'unclosed comment');
2444          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2445          ## reconsume          ## reconsume
2446    
2447          !!!emit ($self->{current_token}); # comment          !!!emit ($self->{ct}); # comment
2448    
2449          redo A;          redo A;
2450        } else {        } else {
2451          !!!parse-error (type => 'dash in comment');          !!!cp (154);
2452          $self->{current_token}->{data} .= '--' . chr ($self->{next_input_character}); # comment          !!!parse-error (type => 'dash in comment',
2453          $self->{state} = 'comment';                          line => $self->{line_prev},
2454                            column => $self->{column_prev});
2455            $self->{ct}->{data} .= '--' . chr ($self->{nc}); # comment
2456            $self->{state} = COMMENT_STATE;
2457          !!!next-input-character;          !!!next-input-character;
2458          redo A;          redo A;
2459        }        }
2460      } elsif ($self->{state} eq 'DOCTYPE') {      } elsif ($self->{state} == DOCTYPE_STATE) {
2461        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2462            $self->{next_input_character} == 0x000A or # LF          !!!cp (155);
2463            $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';  
2464          !!!next-input-character;          !!!next-input-character;
2465          redo A;          redo A;
2466        } else {        } else {
2467            !!!cp (156);
2468          !!!parse-error (type => 'no space before DOCTYPE name');          !!!parse-error (type => 'no space before DOCTYPE name');
2469          $self->{state} = 'before DOCTYPE name';          $self->{state} = BEFORE_DOCTYPE_NAME_STATE;
2470          ## reconsume          ## reconsume
2471          redo A;          redo A;
2472        }        }
2473      } elsif ($self->{state} eq 'before DOCTYPE name') {      } elsif ($self->{state} == BEFORE_DOCTYPE_NAME_STATE) {
2474        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2475            $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  
2476          ## Stay in the state          ## Stay in the state
2477          !!!next-input-character;          !!!next-input-character;
2478          redo A;          redo A;
2479        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2480            !!!cp (158);
2481          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2482          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2483          !!!next-input-character;          !!!next-input-character;
2484    
2485          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2486    
2487          redo A;          redo A;
2488        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2489            !!!cp (159);
2490          !!!parse-error (type => 'no DOCTYPE name');          !!!parse-error (type => 'no DOCTYPE name');
2491          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2492          ## reconsume          ## reconsume
2493    
2494          !!!emit ({type => 'DOCTYPE'}); # incorrect          !!!emit ($self->{ct}); # DOCTYPE (quirks)
2495    
2496          redo A;          redo A;
2497        } else {        } else {
2498          $self->{current_token}          !!!cp (160);
2499              = {type => 'DOCTYPE',          $self->{ct}->{name} = chr $self->{nc};
2500                 name => chr ($self->{next_input_character}),          delete $self->{ct}->{quirks};
2501                 correct => 1};          $self->{state} = DOCTYPE_NAME_STATE;
 ## ISSUE: "Set the token's name name to the" in the spec  
         $self->{state} = 'DOCTYPE name';  
2502          !!!next-input-character;          !!!next-input-character;
2503          redo A;          redo A;
2504        }        }
2505      } elsif ($self->{state} eq 'DOCTYPE name') {      } elsif ($self->{state} == DOCTYPE_NAME_STATE) {
2506  ## ISSUE: Redundant "First," in the spec.  ## ISSUE: Redundant "First," in the spec.
2507        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2508            $self->{next_input_character} == 0x000A or # LF          !!!cp (161);
2509            $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';  
2510          !!!next-input-character;          !!!next-input-character;
2511          redo A;          redo A;
2512        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2513          $self->{state} = 'data';          !!!cp (162);
2514            $self->{state} = DATA_STATE;
2515          !!!next-input-character;          !!!next-input-character;
2516    
2517          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2518    
2519          redo A;          redo A;
2520        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2521            !!!cp (163);
2522          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2523          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2524          ## reconsume          ## reconsume
2525    
2526          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2527          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2528    
2529          redo A;          redo A;
2530        } else {        } else {
2531          $self->{current_token}->{name}          !!!cp (164);
2532            .= chr ($self->{next_input_character}); # DOCTYPE          $self->{ct}->{name}
2533              .= chr ($self->{nc}); # DOCTYPE
2534          ## Stay in the state          ## Stay in the state
2535          !!!next-input-character;          !!!next-input-character;
2536          redo A;          redo A;
2537        }        }
2538      } elsif ($self->{state} eq 'after DOCTYPE name') {      } elsif ($self->{state} == AFTER_DOCTYPE_NAME_STATE) {
2539        if ($self->{next_input_character} == 0x0009 or # HT        if ($is_space->{$self->{nc}}) {
2540            $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  
2541          ## Stay in the state          ## Stay in the state
2542          !!!next-input-character;          !!!next-input-character;
2543          redo A;          redo A;
2544        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2545          $self->{state} = 'data';          !!!cp (166);
2546            $self->{state} = DATA_STATE;
2547          !!!next-input-character;          !!!next-input-character;
2548    
2549          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2550    
2551          redo A;          redo A;
2552        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2553            !!!cp (167);
2554          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2555          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2556          ## reconsume          ## reconsume
2557    
2558          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2559          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2560    
2561          redo A;          redo A;
2562        } elsif ($self->{next_input_character} == 0x0050 or # P        } elsif ($self->{nc} == 0x0050 or # P
2563                 $self->{next_input_character} == 0x0070) { # p                 $self->{nc} == 0x0070) { # p
2564            $self->{state} = PUBLIC_STATE;
2565            $self->{s_kwd} = chr $self->{nc};
2566          !!!next-input-character;          !!!next-input-character;
2567          if ($self->{next_input_character} == 0x0055 or # U          redo A;
2568              $self->{next_input_character} == 0x0075) { # u        } elsif ($self->{nc} == 0x0053 or # S
2569            !!!next-input-character;                 $self->{nc} == 0x0073) { # s
2570            if ($self->{next_input_character} == 0x0042 or # B          $self->{state} = SYSTEM_STATE;
2571                $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  
2572          !!!next-input-character;          !!!next-input-character;
2573          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;  
                 }  
               }  
             }  
           }  
         }  
   
         #  
2574        } else {        } else {
2575            !!!cp (180);
2576            !!!parse-error (type => 'string after DOCTYPE name');
2577            $self->{ct}->{quirks} = 1;
2578    
2579            $self->{state} = BOGUS_DOCTYPE_STATE;
2580          !!!next-input-character;          !!!next-input-character;
2581          #          redo A;
2582        }        }
2583        } elsif ($self->{state} == PUBLIC_STATE) {
2584          ## ASCII case-insensitive
2585          if ($self->{nc} == [
2586                undef,
2587                0x0055, # U
2588                0x0042, # B
2589                0x004C, # L
2590                0x0049, # I
2591              ]->[length $self->{s_kwd}] or
2592              $self->{nc} == [
2593                undef,
2594                0x0075, # u
2595                0x0062, # b
2596                0x006C, # l
2597                0x0069, # i
2598              ]->[length $self->{s_kwd}]) {
2599            !!!cp (175);
2600            ## Stay in the state.
2601            $self->{s_kwd} .= chr $self->{nc};
2602            !!!next-input-character;
2603            redo A;
2604          } elsif ((length $self->{s_kwd}) == 5 and
2605                   ($self->{nc} == 0x0043 or # C
2606                    $self->{nc} == 0x0063)) { # c
2607            !!!cp (168);
2608            $self->{state} = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2609            !!!next-input-character;
2610            redo A;
2611          } else {
2612            !!!cp (169);
2613            !!!parse-error (type => 'string after DOCTYPE name',
2614                            line => $self->{line_prev},
2615                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2616            $self->{ct}->{quirks} = 1;
2617    
2618        !!!parse-error (type => 'string after DOCTYPE name');          $self->{state} = BOGUS_DOCTYPE_STATE;
2619        $self->{state} = 'bogus DOCTYPE';          ## Reconsume.
2620        # next-input-character is already done          redo A;
2621        redo A;        }
2622      } elsif ($self->{state} eq 'before DOCTYPE public identifier') {      } elsif ($self->{state} == SYSTEM_STATE) {
2623        if ({        ## ASCII case-insensitive
2624              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,        if ($self->{nc} == [
2625              #0x000D => 1, # HT, LF, VT, FF, SP, CR              undef,
2626            }->{$self->{next_input_character}}) {              0x0059, # Y
2627                0x0053, # S
2628                0x0054, # T
2629                0x0045, # E
2630              ]->[length $self->{s_kwd}] or
2631              $self->{nc} == [
2632                undef,
2633                0x0079, # y
2634                0x0073, # s
2635                0x0074, # t
2636                0x0065, # e
2637              ]->[length $self->{s_kwd}]) {
2638            !!!cp (170);
2639            ## Stay in the state.
2640            $self->{s_kwd} .= chr $self->{nc};
2641            !!!next-input-character;
2642            redo A;
2643          } elsif ((length $self->{s_kwd}) == 5 and
2644                   ($self->{nc} == 0x004D or # M
2645                    $self->{nc} == 0x006D)) { # m
2646            !!!cp (171);
2647            $self->{state} = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2648            !!!next-input-character;
2649            redo A;
2650          } else {
2651            !!!cp (172);
2652            !!!parse-error (type => 'string after DOCTYPE name',
2653                            line => $self->{line_prev},
2654                            column => $self->{column_prev} + 1 - length $self->{s_kwd});
2655            $self->{ct}->{quirks} = 1;
2656    
2657            $self->{state} = BOGUS_DOCTYPE_STATE;
2658            ## Reconsume.
2659            redo A;
2660          }
2661        } elsif ($self->{state} == BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2662          if ($is_space->{$self->{nc}}) {
2663            !!!cp (181);
2664          ## Stay in the state          ## Stay in the state
2665          !!!next-input-character;          !!!next-input-character;
2666          redo A;          redo A;
2667        } elsif ($self->{next_input_character} eq 0x0022) { # "        } elsif ($self->{nc} eq 0x0022) { # "
2668          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (182);
2669          $self->{state} = 'DOCTYPE public identifier (double-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2670            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
2671          !!!next-input-character;          !!!next-input-character;
2672          redo A;          redo A;
2673        } elsif ($self->{next_input_character} eq 0x0027) { # '        } elsif ($self->{nc} eq 0x0027) { # '
2674          $self->{current_token}->{public_identifier} = ''; # DOCTYPE          !!!cp (183);
2675          $self->{state} = 'DOCTYPE public identifier (single-quoted)';          $self->{ct}->{pubid} = ''; # DOCTYPE
2676            $self->{state} = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
2677          !!!next-input-character;          !!!next-input-character;
2678          redo A;          redo A;
2679        } elsif ($self->{next_input_character} eq 0x003E) { # >        } elsif ($self->{nc} eq 0x003E) { # >
2680            !!!cp (184);
2681          !!!parse-error (type => 'no PUBLIC literal');          !!!parse-error (type => 'no PUBLIC literal');
2682    
2683          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2684          !!!next-input-character;          !!!next-input-character;
2685    
2686          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2687          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2688    
2689          redo A;          redo A;
2690        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2691            !!!cp (185);
2692          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2693    
2694          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2695          ## reconsume          ## reconsume
2696    
2697          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2698          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2699    
2700          redo A;          redo A;
2701        } else {        } else {
2702            !!!cp (186);
2703          !!!parse-error (type => 'string after PUBLIC');          !!!parse-error (type => 'string after PUBLIC');
2704          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2705    
2706            $self->{state} = BOGUS_DOCTYPE_STATE;
2707          !!!next-input-character;          !!!next-input-character;
2708          redo A;          redo A;
2709        }        }
2710      } elsif ($self->{state} eq 'DOCTYPE public identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2711        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2712          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (187);
2713            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2714            !!!next-input-character;
2715            redo A;
2716          } elsif ($self->{nc} == 0x003E) { # >
2717            !!!cp (188);
2718            !!!parse-error (type => 'unclosed PUBLIC literal');
2719    
2720            $self->{state} = DATA_STATE;
2721          !!!next-input-character;          !!!next-input-character;
2722    
2723            $self->{ct}->{quirks} = 1;
2724            !!!emit ($self->{ct}); # DOCTYPE
2725    
2726          redo A;          redo A;
2727        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2728            !!!cp (189);
2729          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2730    
2731          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2732          ## reconsume          ## reconsume
2733    
2734          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2735          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2736    
2737          redo A;          redo A;
2738        } else {        } else {
2739          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (190);
2740              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2741                .= chr $self->{nc};
2742            $self->{read_until}->($self->{ct}->{pubid}, q[">],
2743                                  length $self->{ct}->{pubid});
2744    
2745          ## Stay in the state          ## Stay in the state
2746          !!!next-input-character;          !!!next-input-character;
2747          redo A;          redo A;
2748        }        }
2749      } elsif ($self->{state} eq 'DOCTYPE public identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE) {
2750        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2751          $self->{state} = 'after DOCTYPE public identifier';          !!!cp (191);
2752            $self->{state} = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
2753          !!!next-input-character;          !!!next-input-character;
2754          redo A;          redo A;
2755        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2756            !!!cp (192);
2757            !!!parse-error (type => 'unclosed PUBLIC literal');
2758    
2759            $self->{state} = DATA_STATE;
2760            !!!next-input-character;
2761    
2762            $self->{ct}->{quirks} = 1;
2763            !!!emit ($self->{ct}); # DOCTYPE
2764    
2765            redo A;
2766          } elsif ($self->{nc} == -1) {
2767            !!!cp (193);
2768          !!!parse-error (type => 'unclosed PUBLIC literal');          !!!parse-error (type => 'unclosed PUBLIC literal');
2769    
2770          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2771          ## reconsume          ## reconsume
2772    
2773          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2774          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2775    
2776          redo A;          redo A;
2777        } else {        } else {
2778          $self->{current_token}->{public_identifier} # DOCTYPE          !!!cp (194);
2779              .= chr $self->{next_input_character};          $self->{ct}->{pubid} # DOCTYPE
2780                .= chr $self->{nc};
2781            $self->{read_until}->($self->{ct}->{pubid}, q['>],
2782                                  length $self->{ct}->{pubid});
2783    
2784          ## Stay in the state          ## Stay in the state
2785          !!!next-input-character;          !!!next-input-character;
2786          redo A;          redo A;
2787        }        }
2788      } elsif ($self->{state} eq 'after DOCTYPE public identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE) {
2789        if ({        if ($is_space->{$self->{nc}}) {
2790              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (195);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2791          ## Stay in the state          ## Stay in the state
2792          !!!next-input-character;          !!!next-input-character;
2793          redo A;          redo A;
2794        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2795          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (196);
2796          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2797            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2798          !!!next-input-character;          !!!next-input-character;
2799          redo A;          redo A;
2800        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2801          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (197);
2802          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2803            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2804          !!!next-input-character;          !!!next-input-character;
2805          redo A;          redo A;
2806        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2807          $self->{state} = 'data';          !!!cp (198);
2808            $self->{state} = DATA_STATE;
2809          !!!next-input-character;          !!!next-input-character;
2810    
2811          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2812    
2813          redo A;          redo A;
2814        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2815            !!!cp (199);
2816          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2817    
2818          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2819          ## reconsume          ## reconsume
2820    
2821          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2822          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2823    
2824          redo A;          redo A;
2825        } else {        } else {
2826            !!!cp (200);
2827          !!!parse-error (type => 'string after PUBLIC literal');          !!!parse-error (type => 'string after PUBLIC literal');
2828          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2829    
2830            $self->{state} = BOGUS_DOCTYPE_STATE;
2831          !!!next-input-character;          !!!next-input-character;
2832          redo A;          redo A;
2833        }        }
2834      } elsif ($self->{state} eq 'before DOCTYPE system identifier') {      } elsif ($self->{state} == BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2835        if ({        if ($is_space->{$self->{nc}}) {
2836              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (201);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2837          ## Stay in the state          ## Stay in the state
2838          !!!next-input-character;          !!!next-input-character;
2839          redo A;          redo A;
2840        } elsif ($self->{next_input_character} == 0x0022) { # "        } elsif ($self->{nc} == 0x0022) { # "
2841          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (202);
2842          $self->{state} = 'DOCTYPE system identifier (double-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2843            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
2844          !!!next-input-character;          !!!next-input-character;
2845          redo A;          redo A;
2846        } elsif ($self->{next_input_character} == 0x0027) { # '        } elsif ($self->{nc} == 0x0027) { # '
2847          $self->{current_token}->{system_identifier} = ''; # DOCTYPE          !!!cp (203);
2848          $self->{state} = 'DOCTYPE system identifier (single-quoted)';          $self->{ct}->{sysid} = ''; # DOCTYPE
2849            $self->{state} = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
2850          !!!next-input-character;          !!!next-input-character;
2851          redo A;          redo A;
2852        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2853            !!!cp (204);
2854          !!!parse-error (type => 'no SYSTEM literal');          !!!parse-error (type => 'no SYSTEM literal');
2855          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2856          !!!next-input-character;          !!!next-input-character;
2857    
2858          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2859          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2860    
2861          redo A;          redo A;
2862        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2863            !!!cp (205);
2864          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2865    
2866          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2867          ## reconsume          ## reconsume
2868    
2869          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2870          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2871    
2872          redo A;          redo A;
2873        } else {        } else {
2874            !!!cp (206);
2875          !!!parse-error (type => 'string after SYSTEM');          !!!parse-error (type => 'string after SYSTEM');
2876          $self->{state} = 'bogus DOCTYPE';          $self->{ct}->{quirks} = 1;
2877    
2878            $self->{state} = BOGUS_DOCTYPE_STATE;
2879          !!!next-input-character;          !!!next-input-character;
2880          redo A;          redo A;
2881        }        }
2882      } elsif ($self->{state} eq 'DOCTYPE system identifier (double-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE) {
2883        if ($self->{next_input_character} == 0x0022) { # "        if ($self->{nc} == 0x0022) { # "
2884          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (207);
2885            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2886          !!!next-input-character;          !!!next-input-character;
2887          redo A;          redo A;
2888        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2889            !!!cp (208);
2890          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2891    
2892          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2893            !!!next-input-character;
2894    
2895            $self->{ct}->{quirks} = 1;
2896            !!!emit ($self->{ct}); # DOCTYPE
2897    
2898            redo A;
2899          } elsif ($self->{nc} == -1) {
2900            !!!cp (209);
2901            !!!parse-error (type => 'unclosed SYSTEM literal');
2902    
2903            $self->{state} = DATA_STATE;
2904          ## reconsume          ## reconsume
2905    
2906          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2907          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2908    
2909          redo A;          redo A;
2910        } else {        } else {
2911          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (210);
2912              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2913                .= chr $self->{nc};
2914            $self->{read_until}->($self->{ct}->{sysid}, q[">],
2915                                  length $self->{ct}->{sysid});
2916    
2917          ## Stay in the state          ## Stay in the state
2918          !!!next-input-character;          !!!next-input-character;
2919          redo A;          redo A;
2920        }        }
2921      } elsif ($self->{state} eq 'DOCTYPE system identifier (single-quoted)') {      } elsif ($self->{state} == DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE) {
2922        if ($self->{next_input_character} == 0x0027) { # '        if ($self->{nc} == 0x0027) { # '
2923          $self->{state} = 'after DOCTYPE system identifier';          !!!cp (211);
2924            $self->{state} = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
2925          !!!next-input-character;          !!!next-input-character;
2926          redo A;          redo A;
2927        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == 0x003E) { # >
2928            !!!cp (212);
2929          !!!parse-error (type => 'unclosed SYSTEM literal');          !!!parse-error (type => 'unclosed SYSTEM literal');
2930    
2931          $self->{state} = 'data';          $self->{state} = DATA_STATE;
2932            !!!next-input-character;
2933    
2934            $self->{ct}->{quirks} = 1;
2935            !!!emit ($self->{ct}); # DOCTYPE
2936    
2937            redo A;
2938          } elsif ($self->{nc} == -1) {
2939            !!!cp (213);
2940            !!!parse-error (type => 'unclosed SYSTEM literal');
2941    
2942            $self->{state} = DATA_STATE;
2943          ## reconsume          ## reconsume
2944    
2945          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2946          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2947    
2948          redo A;          redo A;
2949        } else {        } else {
2950          $self->{current_token}->{system_identifier} # DOCTYPE          !!!cp (214);
2951              .= chr $self->{next_input_character};          $self->{ct}->{sysid} # DOCTYPE
2952                .= chr $self->{nc};
2953            $self->{read_until}->($self->{ct}->{sysid}, q['>],
2954                                  length $self->{ct}->{sysid});
2955    
2956          ## Stay in the state          ## Stay in the state
2957          !!!next-input-character;          !!!next-input-character;
2958          redo A;          redo A;
2959        }        }
2960      } elsif ($self->{state} eq 'after DOCTYPE system identifier') {      } elsif ($self->{state} == AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE) {
2961        if ({        if ($is_space->{$self->{nc}}) {
2962              0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, 0x0020 => 1,          !!!cp (215);
             #0x000D => 1, # HT, LF, VT, FF, SP, CR  
           }->{$self->{next_input_character}}) {  
2963          ## Stay in the state          ## Stay in the state
2964          !!!next-input-character;          !!!next-input-character;
2965          redo A;          redo A;
2966        } elsif ($self->{next_input_character} == 0x003E) { # >        } elsif ($self->{nc} == 0x003E) { # >
2967          $self->{state} = 'data';          !!!cp (216);
2968            $self->{state} = DATA_STATE;
2969          !!!next-input-character;          !!!next-input-character;
2970    
2971          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2972    
2973          redo A;          redo A;
2974        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
2975            !!!cp (217);
2976          !!!parse-error (type => 'unclosed DOCTYPE');          !!!parse-error (type => 'unclosed DOCTYPE');
2977            $self->{state} = DATA_STATE;
         $self->{state} = 'data';  
2978          ## reconsume          ## reconsume
2979    
2980          delete $self->{current_token}->{correct};          $self->{ct}->{quirks} = 1;
2981          !!!emit ($self->{current_token}); # DOCTYPE          !!!emit ($self->{ct}); # DOCTYPE
2982    
2983          redo A;          redo A;
2984        } else {        } else {
2985            !!!cp (218);
2986          !!!parse-error (type => 'string after SYSTEM literal');          !!!parse-error (type => 'string after SYSTEM literal');
2987          $self->{state} = 'bogus DOCTYPE';          #$self->{ct}->{quirks} = 1;
2988    
2989            $self->{state} = BOGUS_DOCTYPE_STATE;
2990          !!!next-input-character;          !!!next-input-character;
2991          redo A;          redo A;
2992        }        }
2993      } elsif ($self->{state} eq 'bogus DOCTYPE') {      } elsif ($self->{state} == BOGUS_DOCTYPE_STATE) {
2994        if ($self->{next_input_character} == 0x003E) { # >        if ($self->{nc} == 0x003E) { # >
2995          $self->{state} = 'data';          !!!cp (219);
2996            $self->{state} = DATA_STATE;
2997          !!!next-input-character;          !!!next-input-character;
2998    
2999          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
3000    
3001          redo A;          redo A;
3002        } elsif ($self->{next_input_character} == -1) {        } elsif ($self->{nc} == -1) {
3003          !!!parse-error (type => 'unclosed DOCTYPE');          !!!cp (220);
3004          $self->{state} = 'data';          $self->{state} = DATA_STATE;
3005          ## reconsume          ## reconsume
3006    
3007          delete $self->{current_token}->{correct};          !!!emit ($self->{ct}); # DOCTYPE
         !!!emit ($self->{current_token}); # DOCTYPE  
3008    
3009          redo A;          redo A;
3010        } else {        } else {
3011            !!!cp (221);
3012            my $s = '';
3013            $self->{read_until}->($s, q[>], 0);
3014    
3015          ## Stay in the state          ## Stay in the state
3016          !!!next-input-character;          !!!next-input-character;
3017          redo A;          redo A;
3018        }        }
3019      } else {      } elsif ($self->{state} == CDATA_SECTION_STATE) {
3020        die "$0: $self->{state}: Unknown state";        ## NOTE: "CDATA section state" in the state is jointly implemented
3021      }        ## by three states, |CDATA_SECTION_STATE|, |CDATA_SECTION_MSE1_STATE|,
3022    } # A          ## and |CDATA_SECTION_MSE2_STATE|.
3023          
3024    die "$0: _get_next_token: unexpected case";        if ($self->{nc} == 0x005D) { # ]
3025  } # _get_next_token          !!!cp (221.1);
3026            $self->{state} = CDATA_SECTION_MSE1_STATE;
3027            !!!next-input-character;
3028            redo A;
3029          } elsif ($self->{nc} == -1) {
3030            $self->{state} = DATA_STATE;
3031            !!!next-input-character;
3032            if (length $self->{ct}->{data}) { # character
3033              !!!cp (221.2);
3034              !!!emit ($self->{ct}); # character
3035            } else {
3036              !!!cp (221.3);
3037              ## No token to emit. $self->{ct} is discarded.
3038            }        
3039            redo A;
3040          } else {
3041            !!!cp (221.4);
3042            $self->{ct}->{data} .= chr $self->{nc};
3043            $self->{read_until}->($self->{ct}->{data},
3044                                  q<]>,
3045                                  length $self->{ct}->{data});
3046    
3047  sub _tokenize_attempt_to_consume_an_entity ($$) {          ## Stay in the state.
3048    my ($self, $in_attr) = @_;          !!!next-input-character;
3049            redo A;
3050          }
3051    
3052    if ({        ## ISSUE: "text tokens" in spec.
3053         0x0009 => 1, 0x000A => 1, 0x000B => 1, 0x000C => 1, # HT, LF, VT, FF,      } elsif ($self->{state} == CDATA_SECTION_MSE1_STATE) {
3054         0x0020 => 1, 0x003C => 1, 0x0026 => 1, -1 => 1, # SP, <, & # 0x000D # CR        if ($self->{nc} == 0x005D) { # ]
3055        }->{$self->{next_input_character}}) {          !!!cp (221.5);
3056      ## Don't consume          $self->{state} = CDATA_SECTION_MSE2_STATE;
3057      ## No error          !!!next-input-character;
3058      return undef;          redo A;
3059    } elsif ($self->{next_input_character} == 0x0023) { # #        } else {
3060      !!!next-input-character;          !!!cp (221.6);
3061      if ($self->{next_input_character} == 0x0078 or # x          $self->{ct}->{data} .= ']';
3062          $self->{next_input_character} == 0x0058) { # X          $self->{state} = CDATA_SECTION_STATE;
3063        my $code;          ## Reconsume.
3064        X: {          redo A;
3065          my $x_char = $self->{next_input_character};        }
3066          !!!next-input-character;      } elsif ($self->{state} == CDATA_SECTION_MSE2_STATE) {
3067          if (0x0030 <= $self->{next_input_character} and        if ($self->{nc} == 0x003E) { # >
3068              $self->{next_input_character} <= 0x0039) { # 0..9          $self->{state} = DATA_STATE;
3069            $code ||= 0;          !!!next-input-character;
3070            $code *= 0x10;          if (length $self->{ct}->{data}) { # character
3071            $code += $self->{next_input_character} - 0x0030;            !!!cp (221.7);
3072            redo X;            !!!emit ($self->{ct}); # character
         } elsif (0x0061 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0066) { # a..f  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0060 + 9;  
           redo X;  
         } elsif (0x0041 <= $self->{next_input_character} and  
                  $self->{next_input_character} <= 0x0046) { # A..F  
           $code ||= 0;  
           $code *= 0x10;  
           $code += $self->{next_input_character} - 0x0040 + 9;  
           redo X;  
         } elsif (not defined $code) { # no hexadecimal digit  
           !!!parse-error (type => 'bare hcro');  
           !!!back-next-input-character ($x_char, $self->{next_input_character});  
           $self->{next_input_character} = 0x0023; # #  
           return undef;  
         } elsif ($self->{next_input_character} == 0x003B) { # ;  
           !!!next-input-character;  
3073          } else {          } else {
3074            !!!parse-error (type => 'no refc');            !!!cp (221.8);
3075              ## No token to emit. $self->{ct} is discarded.
3076          }          }
3077            redo A;
3078          if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        } elsif ($self->{nc} == 0x005D) { # ]
3079            !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);          !!!cp (221.9); # character
3080            $code = 0xFFFD;          $self->{ct}->{data} .= ']'; ## Add first "]" of "]]]".
3081          } elsif ($code > 0x10FFFF) {          ## Stay in the state.
3082            !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!next-input-character;
3083            $code = 0xFFFD;          redo A;
3084          } elsif ($code == 0x000D) {        } else {
3085            !!!parse-error (type => 'CR character reference');          !!!cp (221.11);
3086            $code = 0x000A;          $self->{ct}->{data} .= ']]'; # character
3087          } elsif (0x80 <= $code and $code <= 0x9F) {          $self->{state} = CDATA_SECTION_STATE;
3088            !!!parse-error (type => sprintf 'C1 character reference:U+%04X', $code);          ## Reconsume.
3089            $code = $c1_entity_char->{$code};          redo A;
3090          }        }
3091        } elsif ($self->{state} == ENTITY_STATE) {
3092          return {type => 'character', data => chr $code};        if ($is_space->{$self->{nc}} or
3093        } # X            {
3094      } elsif (0x0030 <= $self->{next_input_character} and              0x003C => 1, 0x0026 => 1, -1 => 1, # <, &
3095               $self->{next_input_character} <= 0x0039) { # 0..9              $self->{entity_add} => 1,
3096        my $code = $self->{next_input_character} - 0x0030;            }->{$self->{nc}}) {
3097        !!!next-input-character;          !!!cp (1001);
3098                  ## Don't consume
3099        while (0x0030 <= $self->{next_input_character} and          ## No error
3100                  $self->{next_input_character} <= 0x0039) { # 0..9          ## Return nothing.
3101          $code *= 10;          #
3102          $code += $self->{next_input_character} - 0x0030;        } elsif ($self->{nc} == 0x0023) { # #
3103                    !!!cp (999);
3104            $self->{state} = ENTITY_HASH_STATE;
3105            $self->{s_kwd} = '#';
3106            !!!next-input-character;
3107            redo A;
3108          } elsif ((0x0041 <= $self->{nc} and
3109                    $self->{nc} <= 0x005A) or # A..Z
3110                   (0x0061 <= $self->{nc} and
3111                    $self->{nc} <= 0x007A)) { # a..z
3112            !!!cp (998);
3113            require Whatpm::_NamedEntityList;
3114            $self->{state} = ENTITY_NAME_STATE;
3115            $self->{s_kwd} = chr $self->{nc};
3116            $self->{entity__value} = $self->{s_kwd};
3117            $self->{entity__match} = 0;
3118          !!!next-input-character;          !!!next-input-character;
3119            redo A;
3120          } else {
3121            !!!cp (1027);
3122            !!!parse-error (type => 'bare ero');
3123            ## Return nothing.
3124            #
3125        }        }
3126    
3127        if ($self->{next_input_character} == 0x003B) { # ;        ## NOTE: No character is consumed by the "consume a character
3128          ## reference" algorithm.  In other word, there is an "&" character
3129          ## that does not introduce a character reference, which would be
3130          ## appended to the parent element or the attribute value in later
3131          ## process of the tokenizer.
3132    
3133          if ($self->{prev_state} == DATA_STATE) {
3134            !!!cp (997);
3135            $self->{state} = $self->{prev_state};
3136            ## Reconsume.
3137            !!!emit ({type => CHARACTER_TOKEN, data => '&',
3138                      line => $self->{line_prev},
3139                      column => $self->{column_prev},
3140                     });
3141            redo A;
3142          } else {
3143            !!!cp (996);
3144            $self->{ca}->{value} .= '&';
3145            $self->{state} = $self->{prev_state};
3146            ## Reconsume.
3147            redo A;
3148          }
3149        } elsif ($self->{state} == ENTITY_HASH_STATE) {
3150          if ($self->{nc} == 0x0078 or # x
3151              $self->{nc} == 0x0058) { # X
3152            !!!cp (995);
3153            $self->{state} = HEXREF_X_STATE;
3154            $self->{s_kwd} .= chr $self->{nc};
3155            !!!next-input-character;
3156            redo A;
3157          } elsif (0x0030 <= $self->{nc} and
3158                   $self->{nc} <= 0x0039) { # 0..9
3159            !!!cp (994);
3160            $self->{state} = NCR_NUM_STATE;
3161            $self->{s_kwd} = $self->{nc} - 0x0030;
3162          !!!next-input-character;          !!!next-input-character;
3163            redo A;
3164        } else {        } else {
3165            !!!parse-error (type => 'bare nero',
3166                            line => $self->{line_prev},
3167                            column => $self->{column_prev} - 1);
3168    
3169            ## NOTE: According to the spec algorithm, nothing is returned,
3170            ## and then "&#" is appended to the parent element or the attribute
3171            ## value in the later processing.
3172    
3173            if ($self->{prev_state} == DATA_STATE) {
3174              !!!cp (1019);
3175              $self->{state} = $self->{prev_state};
3176              ## Reconsume.
3177              !!!emit ({type => CHARACTER_TOKEN,
3178                        data => '&#',
3179                        line => $self->{line_prev},
3180                        column => $self->{column_prev} - 1,
3181                       });
3182              redo A;
3183            } else {
3184              !!!cp (993);
3185              $self->{ca}->{value} .= '&#';
3186              $self->{state} = $self->{prev_state};
3187              ## Reconsume.
3188              redo A;
3189            }
3190          }
3191        } elsif ($self->{state} == NCR_NUM_STATE) {
3192          if (0x0030 <= $self->{nc} and
3193              $self->{nc} <= 0x0039) { # 0..9
3194            !!!cp (1012);
3195            $self->{s_kwd} *= 10;
3196            $self->{s_kwd} += $self->{nc} - 0x0030;
3197            
3198            ## Stay in the state.
3199            !!!next-input-character;
3200            redo A;
3201          } elsif ($self->{nc} == 0x003B) { # ;
3202            !!!cp (1013);
3203            !!!next-input-character;
3204            #
3205          } else {
3206            !!!cp (1014);
3207          !!!parse-error (type => 'no refc');          !!!parse-error (type => 'no refc');
3208            ## Reconsume.
3209            #
3210        }        }
3211    
3212        if ($code == 0 or (0xD800 <= $code and $code <= 0xDFFF)) {        my $code = $self->{s_kwd};
3213          !!!parse-error (type => sprintf 'invalid character reference:U+%04X', $code);        my $l = $self->{line_prev};
3214          my $c = $self->{column_prev};
3215          if ($charref_map->{$code}) {
3216            !!!cp (1015);
3217            !!!parse-error (type => 'invalid character reference',
3218                            text => (sprintf 'U+%04X', $code),
3219                            line => $l, column => $c);
3220            $code = $charref_map->{$code};
3221          } elsif ($code > 0x10FFFF) {
3222            !!!cp (1016);
3223            !!!parse-error (type => 'invalid character reference',
3224                            text => (sprintf 'U-%08X', $code),
3225                            line => $l, column => $c);
3226          $code = 0xFFFD;          $code = 0xFFFD;
3227          }
3228    
3229          if ($self->{prev_state} == DATA_STATE) {
3230            !!!cp (992);
3231            $self->{state} = $self->{prev_state};
3232            ## Reconsume.
3233            !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3234                      line => $l, column => $c,
3235                     });
3236            redo A;
3237          } else {
3238            !!!cp (991);
3239            $self->{ca}->{value} .= chr $code;
3240            $self->{ca}->{has_reference} = 1;
3241            $self->{state} = $self->{prev_state};
3242            ## Reconsume.
3243            redo A;
3244          }
3245        } elsif ($self->{state} == HEXREF_X_STATE) {
3246          if ((0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) or
3247              (0x0041 <= $self->{nc} and $self->{nc} <= 0x0046) or
3248              (0x0061 <= $self->{nc} and $self->{nc} <= 0x0066)) {
3249            # 0..9, A..F, a..f
3250            !!!cp (990);
3251            $self->{state} = HEXREF_HEX_STATE;
3252            $self->{s_kwd} = 0;
3253            ## Reconsume.
3254            redo A;
3255          } else {
3256            !!!parse-error (type => 'bare hcro',
3257                            line => $self->{line_prev},
3258                            column => $self->{column_prev} - 2);
3259    
3260            ## NOTE: According to the spec algorithm, nothing is returned,
3261            ## and then "&#" followed by "X" or "x" is appended to the parent
3262            ## element or the attribute value in the later processing.
3263    
3264            if ($self->{prev_state} == DATA_STATE) {
3265              !!!cp (1005);
3266              $self->{state} = $self->{prev_state};
3267              ## Reconsume.
3268              !!!emit ({type => CHARACTER_TOKEN,
3269                        data => '&' . $self->{s_kwd},
3270                        line => $self->{line_prev},
3271                        column => $self->{column_prev} - length $self->{s_kwd},
3272                       });
3273              redo A;
3274            } else {
3275              !!!cp (989);
3276              $self->{ca}->{value} .= '&' . $self->{s_kwd};
3277              $self->{state} = $self->{prev_state};
3278              ## Reconsume.
3279              redo A;
3280            }
3281          }
3282        } elsif ($self->{state} == HEXREF_HEX_STATE) {
3283          if (0x0030 <= $self->{nc} and $self->{nc} <= 0x0039) {
3284            # 0..9
3285            !!!cp (1002);
3286            $self->{s_kwd} *= 0x10;
3287            $self->{s_kwd} += $self->{nc} - 0x0030;
3288            ## Stay in the state.
3289            !!!next-input-character;
3290            redo A;
3291          } elsif (0x0061 <= $self->{nc} and
3292                   $self->{nc} <= 0x0066) { # a..f
3293            !!!cp (1003);
3294            $self->{s_kwd} *= 0x10;
3295            $self->{s_kwd} += $self->{nc} - 0x0060 + 9;
3296            ## Stay in the state.
3297            !!!next-input-character;
3298            redo A;
3299          } elsif (0x0041 <= $self->{nc} and
3300                   $self->{nc} <= 0x0046) { # A..F
3301            !!!cp (1004);
3302            $self->{s_kwd} *= 0x10;
3303            $self->{s_kwd} += $self->{nc} - 0x0040 + 9;
3304            ## Stay in the state.
3305            !!!next-input-character;
3306            redo A;
3307          } elsif ($self->{nc} == 0x003B) { # ;
3308            !!!cp (1006);
3309            !!!next-input-character;
3310            #
3311          } else {
3312            !!!cp (1007);
3313            !!!parse-error (type => 'no refc',
3314                            line => $self->{line},
3315                            column => $self->{column});
3316            ## Reconsume.
3317            #
3318          }
3319    
3320          my $code = $self->{s_kwd};
3321          my $l = $self->{line_prev};
3322          my $c = $self->{column_prev};
3323          if ($charref_map->{$code}) {
3324            !!!cp (1008);
3325            !!!parse-error (type => 'invalid character reference',
3326                            text => (sprintf 'U+%04X', $code),
3327                            line => $l, column => $c);
3328            $code = $charref_map->{$code};
3329        } elsif ($code > 0x10FFFF) {        } elsif ($code > 0x10FFFF) {
3330          !!!parse-error (type => sprintf 'invalid character reference:U-%08X', $code);          !!!cp (1009);
3331            !!!parse-error (type => 'invalid character reference',
3332                            text => (sprintf 'U-%08X', $code),
3333                            line => $l, column => $c);
3334          $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};  
3335        }        }
3336          
3337        return {type => 'character', data => chr $code};        if ($self->{prev_state} == DATA_STATE) {
3338      } else {          !!!cp (988);
3339        !!!parse-error (type => 'bare nero');          $self->{state} = $self->{prev_state};
3340        !!!back-next-input-character ($self->{next_input_character});          ## Reconsume.
3341        $self->{next_input_character} = 0x0023; # #          !!!emit ({type => CHARACTER_TOKEN, data => chr $code,
3342        return undef;                    line => $l, column => $c,
3343      }                   });
3344    } elsif ((0x0041 <= $self->{next_input_character} and          redo A;
3345              $self->{next_input_character} <= 0x005A) or        } else {
3346             (0x0061 <= $self->{next_input_character} and          !!!cp (987);
3347              $self->{next_input_character} <= 0x007A)) {          $self->{ca}->{value} .= chr $code;
3348      my $entity_name = chr $self->{next_input_character};          $self->{ca}->{has_reference} = 1;
3349      !!!next-input-character;          $self->{state} = $self->{prev_state};
3350            ## Reconsume.
3351      my $value = $entity_name;          redo A;
3352      my $match = 0;        }
3353      require Whatpm::_NamedEntityList;      } elsif ($self->{state} == ENTITY_NAME_STATE) {
3354      our $EntityChar;        if (length $self->{s_kwd} < 30 and
3355              ## NOTE: Some number greater than the maximum length of entity name
3356      while (length $entity_name < 10 and            ((0x0041 <= $self->{nc} and # a
3357             ## NOTE: Some number greater than the maximum length of entity name              $self->{nc} <= 0x005A) or # x
3358             ((0x0041 <= $self->{next_input_character} and # a             (0x0061 <= $self->{nc} and # a
3359               $self->{next_input_character} <= 0x005A) or # x              $self->{nc} <= 0x007A) or # z
3360              (0x0061 <= $self->{next_input_character} and # a             (0x0030 <= $self->{nc} and # 0
3361               $self->{next_input_character} <= 0x007A) or # z              $self->{nc} <= 0x0039) or # 9
3362              (0x0030 <= $self->{next_input_character} and # 0             $self->{nc} == 0x003B)) { # ;
3363               $self->{next_input_character} <= 0x0039) or # 9          our $EntityChar;
3364              $self->{next_input_character} == 0x003B)) { # ;          $self->{s_kwd} .= chr $self->{nc};
3365        $entity_name .= chr $self->{next_input_character};          if (defined $EntityChar->{$self->{s_kwd}}) {
3366        if (defined $EntityChar->{$entity_name}) {            if ($self->{nc} == 0x003B) { # ;
3367          if ($self->{next_input_character} == 0x003B) { # ;              !!!cp (1020);
3368            $value = $EntityChar->{$entity_name};              $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3369            $match = 1;              $self->{entity__match} = 1;
3370            !!!next-input-character;              !!!next-input-character;
3371            last;              #
3372              } else {
3373                !!!cp (1021);
3374                $self->{entity__value} = $EntityChar->{$self->{s_kwd}};
3375                $self->{entity__match} = -1;
3376                ## Stay in the state.
3377                !!!next-input-character;
3378                redo A;
3379              }
3380          } else {          } else {
3381            $value = $EntityChar->{$entity_name};            !!!cp (1022);
3382            $match = -1;            $self->{entity__value} .= chr $self->{nc};
3383              $self->{entity__match} *= 2;
3384              ## Stay in the state.
3385            !!!next-input-character;            !!!next-input-character;
3386              redo A;
3387          }          }
       } else {  
         $value .= chr $self->{next_input_character};  
         $match *= 2;  
         !!!next-input-character;  
3388        }        }
3389      }  
3390              my $data;
3391      if ($match > 0) {        my $has_ref;
3392        return {type => 'character', data => $value};        if ($self->{entity__match} > 0) {
3393      } elsif ($match < 0) {          !!!cp (1023);
3394        !!!parse-error (type => 'no refc');          $data = $self->{entity__value};
3395        if ($in_attr and $match < -1) {          $has_ref = 1;
3396          return {type => 'character', data => '&'.$entity_name};          #
3397          } elsif ($self->{entity__match} < 0) {
3398            !!!parse-error (type => 'no refc');
3399            if ($self->{prev_state} != DATA_STATE and # in attribute
3400                $self->{entity__match} < -1) {
3401              !!!cp (1024);
3402              $data = '&' . $self->{s_kwd};
3403              #
3404            } else {
3405              !!!cp (1025);
3406              $data = $self->{entity__value};
3407              $has_ref = 1;
3408              #
3409            }
3410        } else {        } else {
3411          return {type => 'character', data => $value};          !!!cp (1026);
3412            !!!parse-error (type => 'bare ero',
3413                            line => $self->{line_prev},
3414                            column => $self->{column_prev} - length $self->{s_kwd});
3415            $data = '&' . $self->{s_kwd};
3416            #
3417          }
3418      
3419          ## NOTE: In these cases, when a character reference is found,
3420          ## it is consumed and a character token is returned, or, otherwise,
3421          ## nothing is consumed and returned, according to the spec algorithm.
3422          ## In this implementation, anything that has been examined by the
3423          ## tokenizer is appended to the parent element or the attribute value
3424          ## as string, either literal string when no character reference or
3425          ## entity-replaced string otherwise, in this stage, since any characters
3426          ## that would not be consumed are appended in the data state or in an
3427          ## appropriate attribute value state anyway.
3428    
3429          if ($self->{prev_state} == DATA_STATE) {
3430            !!!cp (986);
3431            $self->{state} = $self->{prev_state};
3432            ## Reconsume.
3433            !!!emit ({type => CHARACTER_TOKEN,
3434                      data => $data,
3435                      line => $self->{line_prev},
3436                      column => $self->{column_prev} + 1 - length $self->{s_kwd},
3437                     });
3438            redo A;
3439          } else {
3440            !!!cp (985);
3441            $self->{ca}->{value} .= $data;
3442            $self->{ca}->{has_reference} = 1 if $has_ref;
3443            $self->{state} = $self->{prev_state};
3444            ## Reconsume.
3445            redo A;
3446        }        }
3447      } else {      } else {
3448        !!!parse-error (type => 'bare ero');        die "$0: $self->{state}: Unknown state";
       ## NOTE: No characters are consumed in the spec.  
       return {type => 'character', data => '&'.$value};  
3449      }      }
3450    } else {    } # A  
3451      ## no characters are consumed  
3452      !!!parse-error (type => 'bare ero');    die "$0: _get_next_token: unexpected case";
3453      return undef;  } # _get_next_token
   }  
 } # _tokenize_attempt_to_consume_an_entity  
3454    
3455  sub _initialize_tree_constructor ($) {  sub _initialize_tree_constructor ($) {
3456    my $self = shift;    my $self = shift;
# Line 1780  sub _initialize_tree_constructor ($) { Line 3459  sub _initialize_tree_constructor ($) {
3459    ## TODO: Turn mutation events off # MUST    ## TODO: Turn mutation events off # MUST
3460    ## TODO: Turn loose Document option (manakai extension) on    ## TODO: Turn loose Document option (manakai extension) on
3461    $self->{document}->manakai_is_html (1); # MUST    $self->{document}->manakai_is_html (1); # MUST
3462      $self->{document}->set_user_data (manakai_source_line => 1);
3463      $self->{document}->set_user_data (manakai_source_column => 1);
3464  } # _initialize_tree_constructor  } # _initialize_tree_constructor
3465    
3466  sub _terminate_tree_constructor ($) {  sub _terminate_tree_constructor ($) {
# Line 1799  sub _construct_tree ($) { Line 3480  sub _construct_tree ($) {
3480    ## When an interactive UA render the $self->{document} available    ## When an interactive UA render the $self->{document} available
3481    ## to the user, or when it begin accepting user input, are    ## to the user, or when it begin accepting user input, are
3482    ## 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  
3483        
3484    !!!next-token;    !!!next-token;
3485    
   $self->{insertion_mode} = 'before head';  
3486    undef $self->{form_element};    undef $self->{form_element};
3487    undef $self->{head_element};    undef $self->{head_element};
3488      undef $self->{head_element_inserted};
3489    $self->{open_elements} = [];    $self->{open_elements} = [];
3490    undef $self->{inner_html_node};    undef $self->{inner_html_node};
3491      undef $self->{ignore_newline};
3492    
3493      ## NOTE: The "initial" insertion mode.
3494    $self->_tree_construction_initial; # MUST    $self->_tree_construction_initial; # MUST
3495    
3496      ## NOTE: The "before html" insertion mode.
3497    $self->_tree_construction_root_element;    $self->_tree_construction_root_element;
3498      $self->{insertion_mode} = BEFORE_HEAD_IM;
3499    
3500      ## NOTE: The "before head" insertion mode and so on.
3501    $self->_tree_construction_main;    $self->_tree_construction_main;
3502  } # _construct_tree  } # _construct_tree
3503    
3504  sub _tree_construction_initial ($) {  sub _tree_construction_initial ($) {
3505    my $self = shift;    my $self = shift;
3506    
3507      ## NOTE: "initial" insertion mode
3508    
3509    INITIAL: {    INITIAL: {
3510      if ($token->{type} eq 'DOCTYPE') {      if ($token->{type} == DOCTYPE_TOKEN) {
3511        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"        ## NOTE: Conformance checkers MAY, instead of reporting "not HTML5"
3512        ## error, switch to a conformance checking mode for another        ## error, switch to a conformance checking mode for another
3513        ## language.        ## language.
3514        my $doctype_name = $token->{name};        my $doctype_name = $token->{name};
3515        $doctype_name = '' unless defined $doctype_name;        $doctype_name = '' unless defined $doctype_name;
3516        $doctype_name =~ tr/a-z/A-Z/;        $doctype_name =~ tr/a-z/A-Z/; # ASCII case-insensitive
3517        if (not defined $token->{name} or # <!DOCTYPE>        if (not defined $token->{name} or # <!DOCTYPE>
3518            defined $token->{public_identifier} or            defined $token->{sysid}) {
3519            defined $token->{system_identifier}) {          !!!cp ('t1');
3520          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3521        } elsif ($doctype_name ne 'HTML') {        } elsif ($doctype_name ne 'HTML') {
3522          ## ISSUE: ASCII case-insensitive? (in fact it does not matter)          !!!cp ('t2');
3523          !!!parse-error (type => 'not HTML5');          !!!parse-error (type => 'not HTML5', token => $token);
3524          } elsif (defined $token->{pubid}) {
3525            if ($token->{pubid} eq 'XSLT-compat') {
3526              !!!cp ('t1.2');
3527              !!!parse-error (type => 'XSLT-compat', token => $token,
3528                              level => $self->{level}->{should});
3529            } else {
3530              !!!parse-error (type => 'not HTML5', token => $token);
3531            }
3532          } else {
3533            !!!cp ('t3');
3534            #
3535        }        }
3536                
3537        my $doctype = $self->{document}->create_document_type_definition        my $doctype = $self->{document}->create_document_type_definition
3538          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?          ($token->{name}); ## ISSUE: If name is missing (e.g. <!DOCTYPE>)?
3539        $doctype->public_id ($token->{public_identifier})        ## NOTE: Default value for both |public_id| and |system_id| attributes
3540            if defined $token->{public_identifier};        ## are empty strings, so that we don't set any value in missing cases.
3541        $doctype->system_id ($token->{system_identifier})        $doctype->public_id ($token->{pubid}) if defined $token->{pubid};
3542            if defined $token->{system_identifier};        $doctype->system_id ($token->{sysid}) if defined $token->{sysid};
3543        ## NOTE: Other DocumentType attributes are null or empty lists.        ## NOTE: Other DocumentType attributes are null or empty lists.
3544        ## ISSUE: internalSubset = null??        ## ISSUE: internalSubset = null??
3545        $self->{document}->append_child ($doctype);        $self->{document}->append_child ($doctype);
3546                
3547        if (not $token->{correct} or $doctype_name ne 'HTML') {        if ($token->{quirks} or $doctype_name ne 'HTML') {
3548            !!!cp ('t4');
3549          $self->{document}->manakai_compat_mode ('quirks');          $self->{document}->manakai_compat_mode ('quirks');
3550        } elsif (defined $token->{public_identifier}) {        } elsif (defined $token->{pubid}) {
3551          my $pubid = $token->{public_identifier};          my $pubid = $token->{pubid};
3552          $pubid =~ tr/a-z/A-z/;          $pubid =~ tr/a-z/A-z/;
3553          if ({          my $prefix = [
3554            "+//SILMARIL//DTD HTML PRO V0R11 19970101//EN" => 1,            "+//SILMARIL//DTD HTML PRO V0R11 19970101//",
3555            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//ADVASOFT LTD//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3556            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//EN" => 1,            "-//AS//DTD HTML 3.0 ASWEDIT + EXTENSIONS//",
3557            "-//IETF//DTD HTML 2.0 LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 1//",
3558            "-//IETF//DTD HTML 2.0 LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 LEVEL 2//",
3559            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 1//",
3560            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT LEVEL 2//",
3561            "-//IETF//DTD HTML 2.0 STRICT//EN" => 1,            "-//IETF//DTD HTML 2.0 STRICT//",
3562            "-//IETF//DTD HTML 2.0//EN" => 1,            "-//IETF//DTD HTML 2.0//",
3563            "-//IETF//DTD HTML 2.1E//EN" => 1,            "-//IETF//DTD HTML 2.1E//",
3564            "-//IETF//DTD HTML 3.0//EN" => 1,            "-//IETF//DTD HTML 3.0//",
3565            "-//IETF//DTD HTML 3.0//EN//" => 1,            "-//IETF//DTD HTML 3.2 FINAL//",
3566            "-//IETF//DTD HTML 3.2 FINAL//EN" => 1,            "-//IETF//DTD HTML 3.2//",
3567            "-//IETF//DTD HTML 3.2//EN" => 1,            "-//IETF//DTD HTML 3//",
3568            "-//IETF//DTD HTML 3//EN" => 1,            "-//IETF//DTD HTML LEVEL 0//",
3569            "-//IETF//DTD HTML LEVEL 0//EN" => 1,            "-//IETF//DTD HTML LEVEL 1//",
3570            "-//IETF//DTD HTML LEVEL 0//EN//2.0" => 1,            "-//IETF//DTD HTML LEVEL 2//",
3571            "-//IETF//DTD HTML LEVEL 1//EN" => 1,            "-//IETF//DTD HTML LEVEL 3//",
3572            "-//IETF//DTD HTML LEVEL 1//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 0//",
3573            "-//IETF//DTD HTML LEVEL 2//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 1//",
3574            "-//IETF//DTD HTML LEVEL 2//EN//2.0" => 1,            "-//IETF//DTD HTML STRICT LEVEL 2//",
3575            "-//IETF//DTD HTML LEVEL 3//EN" => 1,            "-//IETF//DTD HTML STRICT LEVEL 3//",
3576            "-//IETF//DTD HTML LEVEL 3//EN//3.0" => 1,            "-//IETF//DTD HTML STRICT//",
3577            "-//IETF//DTD HTML STRICT LEVEL 0//EN" => 1,            "-//IETF//DTD HTML//",
3578            "-//IETF//DTD HTML STRICT LEVEL 0//EN//2.0" => 1,            "-//METRIUS//DTD METRIUS PRESENTATIONAL//",
3579            "-//IETF//DTD HTML STRICT LEVEL 1//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//",
3580            "-//IETF//DTD HTML STRICT LEVEL 1//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//",
3581            "-//IETF//DTD HTML STRICT LEVEL 2//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//",
3582            "-//IETF//DTD HTML STRICT LEVEL 2//EN//2.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//",
3583            "-//IETF//DTD HTML STRICT LEVEL 3//EN" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//",
3584            "-//IETF//DTD HTML STRICT LEVEL 3//EN//3.0" => 1,            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//",
3585            "-//IETF//DTD HTML STRICT//EN" => 1,            "-//NETSCAPE COMM. CORP.//DTD HTML//",
3586            "-//IETF//DTD HTML STRICT//EN//2.0" => 1,            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//",
3587            "-//IETF//DTD HTML STRICT//EN//3.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//",
3588            "-//IETF//DTD HTML//EN" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//",
3589            "-//IETF//DTD HTML//EN//2.0" => 1,            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED RELAXED 1.0//",
3590            "-//IETF//DTD HTML//EN//3.0" => 1,            "-//SOFTQUAD SOFTWARE//DTD HOTMETAL PRO 6.0::19990601::EXTENSIONS TO HTML 4.0//",
3591            "-//METRIUS//DTD METRIUS PRESENTATIONAL//EN" => 1,            "-//SOFTQUAD//DTD HOTMETAL PRO 4.0::19971010::EXTENSIONS TO HTML 4.0//",
3592            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML STRICT//EN" => 1,            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//",
3593            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 HTML//EN" => 1,            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//",
3594            "-//MICROSOFT//DTD INTERNET EXPLORER 2.0 TABLES//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//",
3595            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML STRICT//EN" => 1,            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//",
3596            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 HTML//EN" => 1,            "-//W3C//DTD HTML 3 1995-03-24//",
3597            "-//MICROSOFT//DTD INTERNET EXPLORER 3.0 TABLES//EN" => 1,            "-//W3C//DTD HTML 3.2 DRAFT//",
3598            "-//NETSCAPE COMM. CORP.//DTD HTML//EN" => 1,            "-//W3C//DTD HTML 3.2 FINAL//",
3599            "-//NETSCAPE COMM. CORP.//DTD STRICT HTML//EN" => 1,            "-//W3C//DTD HTML 3.2//",
3600            "-//O'REILLY AND ASSOCIATES//DTD HTML 2.0//EN" => 1,            "-//W3C//DTD HTML 3.2S DRAFT//",
3601            "-//O'REILLY AND ASSOCIATES//DTD HTML EXTENDED 1.0//EN" => 1,            "-//W3C//DTD HTML 4.0 FRAMESET//",
3602            "-//SPYGLASS//DTD HTML 2.0 EXTENDED//EN" => 1,            "-//W3C//DTD HTML 4.0 TRANSITIONAL//",
3603            "-//SQ//DTD HTML 2.0 HOTMETAL + EXTENSIONS//EN" => 1,            "-//W3C//DTD HTML EXPERIMETNAL 19960712//",
3604            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA HTML//EN" => 1,            "-//W3C//DTD HTML EXPERIMENTAL 970421//",
3605            "-//SUN MICROSYSTEMS CORP.//DTD HOTJAVA STRICT HTML//EN" => 1,            "-//W3C//DTD W3 HTML//",
3606            "-//W3C//DTD HTML 3 1995-03-24//EN" => 1,            "-//W3O//DTD W3 HTML 3.0//",
3607            "-//W3C//DTD HTML 3.2 DRAFT//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML 2.0//",
3608            "-//W3C//DTD HTML 3.2 FINAL//EN" => 1,            "-//WEBTECHS//DTD MOZILLA HTML//",
3609            "-//W3C//DTD HTML 3.2//EN" => 1,          ]; # $prefix
3610            "-//W3C//DTD HTML 3.2S DRAFT//EN" => 1,          my $match;
3611            "-//W3C//DTD HTML 4.0 FRAMESET//EN" => 1,          for (@$prefix) {
3612            "-//W3C//DTD HTML 4.0 TRANSITIONAL//EN" => 1,            if (substr ($prefix, 0, length $_) eq $_) {
3613            "-//W3C//DTD HTML EXPERIMETNAL 19960712//EN" => 1,              $match = 1;
3614            "-//W3C//DTD HTML EXPERIMENTAL 970421//EN" => 1,              last;
3615            "-//W3C//DTD W3 HTML//EN" => 1,            }
3616            "-//W3O//DTD W3 HTML 3.0//EN" => 1,          }
3617            "-//W3O//DTD W3 HTML 3.0//EN//" => 1,          if ($match or
3618            "-//W3O//DTD W3 HTML STRICT 3.0//EN//" => 1,              $pubid eq "-//W3O//DTD W3 HTML STRICT 3.0//EN//" or
3619            "-//WEBTECHS//DTD MOZILLA HTML 2.0//EN" => 1,              $pubid eq "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" or
3620            "-//WEBTECHS//DTD MOZILLA HTML//EN" => 1,              $pubid eq "HTML") {
3621            "-/W3C/DTD HTML 4.0 TRANSITIONAL/EN" => 1,            !!!cp ('t5');
           "HTML" => 1,  
         }->{$pubid}) {  
3622            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3623          } elsif ($pubid eq "-//W3C//DTD HTML 4.01 FRAMESET//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD HTML 4.01 FRAMESET//] or
3624                   $pubid eq "-//W3C//DTD HTML 4.01 TRANSITIONAL//EN") {                   $pubid =~ m[^-//W3C//DTD HTML 4.01 TRANSITIONAL//]) {
3625            if (defined $token->{system_identifier}) {            if (defined $token->{sysid}) {
3626                !!!cp ('t6');
3627              $self->{document}->manakai_compat_mode ('quirks');              $self->{document}->manakai_compat_mode ('quirks');
3628            } else {            } else {
3629                !!!cp ('t7');
3630              $self->{document}->manakai_compat_mode ('limited quirks');              $self->{document}->manakai_compat_mode ('limited quirks');
3631            }            }
3632          } elsif ($pubid eq "-//W3C//DTD XHTML 1.0 Frameset//EN" or          } elsif ($pubid =~ m[^-//W3C//DTD XHTML 1.0 FRAMESET//] or
3633                   $pubid eq "-//W3C//DTD XHTML 1.0 Transitional//EN") {                   $pubid =~ m[^-//W3C//DTD XHTML 1.0 TRANSITIONAL//]) {
3634              !!!cp ('t8');
3635            $self->{document}->manakai_compat_mode ('limited quirks');            $self->{document}->manakai_compat_mode ('limited quirks');
3636            } else {
3637              !!!cp ('t9');
3638          }          }
3639          } else {
3640            !!!cp ('t10');
3641        }        }
3642        if (defined $token->{system_identifier}) {        if (defined $token->{sysid}) {
3643          my $sysid = $token->{system_identifier};          my $sysid = $token->{sysid};
3644          $sysid =~ tr/A-Z/a-z/;          $sysid =~ tr/A-Z/a-z/;
3645          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") {
3646              ## NOTE: Ensure that |PUBLIC "(limited quirks)" "(quirks)"| is
3647              ## marked as quirks.
3648            $self->{document}->manakai_compat_mode ('quirks');            $self->{document}->manakai_compat_mode ('quirks');
3649              !!!cp ('t11');
3650            } else {
3651              !!!cp ('t12');
3652          }          }
3653          } else {
3654            !!!cp ('t13');
3655        }        }
3656                
3657        ## Go to the root element phase.        ## Go to the "before html" insertion mode.
3658        !!!next-token;        !!!next-token;
3659        return;        return;
3660      } elsif ({      } elsif ({
3661                'start tag' => 1,                START_TAG_TOKEN, 1,
3662                'end tag' => 1,                END_TAG_TOKEN, 1,
3663                'end-of-file' => 1,                END_OF_FILE_TOKEN, 1,
3664               }->{$token->{type}}) {               }->{$token->{type}}) {
3665        !!!parse-error (type => 'no DOCTYPE');        !!!cp ('t14');
3666          !!!parse-error (type => 'no DOCTYPE', token => $token);
3667        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3668        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3669        ## reprocess        ## reprocess
3670          !!!ack-later;
3671        return;        return;
3672      } elsif ($token->{type} eq 'character') {      } elsif ($token->{type} == CHARACTER_TOKEN) {
3673        if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D        if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3674          ## Ignore the token          ## Ignore the token
3675    
3676          unless (length $token->{data}) {          unless (length $token->{data}) {
3677            ## Stay in the phase            !!!cp ('t15');
3678              ## Stay in the insertion mode.
3679            !!!next-token;            !!!next-token;
3680            redo INITIAL;            redo INITIAL;
3681            } else {
3682              !!!cp ('t16');
3683          }          }
3684          } else {
3685            !!!cp ('t17');
3686        }        }
3687    
3688        !!!parse-error (type => 'no DOCTYPE');        !!!parse-error (type => 'no DOCTYPE', token => $token);
3689        $self->{document}->manakai_compat_mode ('quirks');        $self->{document}->manakai_compat_mode ('quirks');
3690        ## Go to the root element phase        ## Go to the "before html" insertion mode.
3691        ## reprocess        ## reprocess
3692        return;        return;
3693      } elsif ($token->{type} eq 'comment') {      } elsif ($token->{type} == COMMENT_TOKEN) {
3694          !!!cp ('t18');
3695        my $comment = $self->{document}->create_comment ($token->{data});        my $comment = $self->{document}->create_comment ($token->{data});
3696        $self->{document}->append_child ($comment);        $self->{document}->append_child ($comment);
3697                
3698        ## Stay in the phase.        ## Stay in the insertion mode.
3699        !!!next-token;        !!!next-token;
3700        redo INITIAL;        redo INITIAL;
3701      } else {      } else {
3702        die "$0: $token->{type}: Unknown token";        die "$0: $token->{type}: Unknown token type";
3703      }      }
3704    } # INITIAL    } # INITIAL
3705    
3706      die "$0: _tree_construction_initial: This should be never reached";
3707  } # _tree_construction_initial  } # _tree_construction_initial
3708    
3709  sub _tree_construction_root_element ($) {  sub _tree_construction_root_element ($) {
3710    my $self = shift;    my $self = shift;
3711    
3712      ## NOTE: "before html" insertion mode.
3713        
3714    B: {    B: {
3715        if ($token->{type} eq 'DOCTYPE') {        if ($token->{type} == DOCTYPE_TOKEN) {
3716          !!!parse-error (type => 'in html:#DOCTYPE');          !!!cp ('t19');
3717            !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
3718          ## Ignore the token          ## Ignore the token
3719          ## Stay in the phase          ## Stay in the insertion mode.
3720          !!!next-token;          !!!next-token;
3721          redo B;          redo B;
3722        } elsif ($token->{type} eq 'comment') {        } elsif ($token->{type} == COMMENT_TOKEN) {
3723            !!!cp ('t20');
3724          my $comment = $self->{document}->create_comment ($token->{data});          my $comment = $self->{document}->create_comment ($token->{data});
3725          $self->{document}->append_child ($comment);          $self->{document}->append_child ($comment);
3726          ## Stay in the phase          ## Stay in the insertion mode.
3727          !!!next-token;          !!!next-token;
3728          redo B;          redo B;
3729        } elsif ($token->{type} eq 'character') {        } elsif ($token->{type} == CHARACTER_TOKEN) {
3730          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) { # \x0D          if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
3731            ## Ignore the token.            ## Ignore the token.
3732    
3733            unless (length $token->{data}) {            unless (length $token->{data}) {
3734              ## Stay in the phase              !!!cp ('t21');
3735                ## Stay in the insertion mode.
3736              !!!next-token;              !!!next-token;
3737              redo B;              redo B;
3738              } else {
3739                !!!cp ('t22');
3740            }            }
3741            } else {
3742              !!!cp ('t23');
3743          }          }
3744    
3745            $self->{application_cache_selection}->(undef);
3746    
3747          #          #
3748          } elsif ($token->{type} == START_TAG_TOKEN) {
3749            if ($token->{tag_name} eq 'html') {
3750              my $root_element;
3751              !!!create-element ($root_element, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
3752              $self->{document}->append_child ($root_element);
3753              push @{$self->{open_elements}},
3754                  [$root_element, $el_category->{html}];
3755    
3756              if ($token->{attributes}->{manifest}) {
3757                !!!cp ('t24');
3758                $self->{application_cache_selection}
3759                    ->($token->{attributes}->{manifest}->{value});
3760                ## ISSUE: Spec is unclear on relative references.
3761                ## According to Hixie (#whatwg 2008-03-19), it should be
3762                ## resolved against the base URI of the document in HTML
3763                ## or xml:base of the element in XHTML.
3764              } else {
3765                !!!cp ('t25');
3766                $self->{application_cache_selection}->(undef);
3767              }
3768    
3769              !!!nack ('t25c');
3770    
3771              !!!next-token;
3772              return; ## Go to the "before head" insertion mode.
3773            } else {
3774              !!!cp ('t25.1');
3775              #
3776            }
3777        } elsif ({        } elsif ({
3778                  'start tag' => 1,                  END_TAG_TOKEN, 1,
3779                  'end tag' => 1,                  END_OF_FILE_TOKEN, 1,
                 'end-of-file' => 1,  
3780                 }->{$token->{type}}) {                 }->{$token->{type}}) {
3781          ## ISSUE: There is an issue in the spec          !!!cp ('t26');
3782          #          #
3783        } else {        } else {
3784          die "$0: $token->{type}: Unknown token";          die "$0: $token->{type}: Unknown token type";
3785        }        }
3786        my $root_element; !!!create-element ($root_element, 'html');  
3787        $self->{document}->append_child ($root_element);      my $root_element;
3788        push @{$self->{open_elements}}, [$root_element, 'html'];      !!!create-element ($root_element, $HTML_NS, 'html',, $token);
3789        ## reprocess      $self->{document}->append_child ($root_element);
3790        #redo B;      push @{$self->{open_elements}}, [$root_element, $el_category->{html}];
3791        return; ## Go to the main phase.  
3792        $self->{application_cache_selection}->(undef);
3793    
3794        ## NOTE: Reprocess the token.
3795        !!!ack-later;
3796        return; ## Go to the "before head" insertion mode.
3797    } # B    } # B
3798    
3799      die "$0: _tree_construction_root_element: This should never be reached";
3800  } # _tree_construction_root_element  } # _tree_construction_root_element
3801    
3802  sub _reset_insertion_mode ($) {  sub _reset_insertion_mode ($) {
# Line 2043  sub _reset_insertion_mode ($) { Line 3811  sub _reset_insertion_mode ($) {
3811            
3812      ## Step 3      ## Step 3
3813      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"!?  
3814        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {        if ($self->{open_elements}->[0]->[0] eq $node->[0]) {
3815          $last = 1;          $last = 1;
3816          if (defined $self->{inner_html_node}) {          if (defined $self->{inner_html_node}) {
3817            if ($self->{inner_html_node}->[1] eq 'td' or            !!!cp ('t28');
3818                $self->{inner_html_node}->[1] eq 'th') {            $node = $self->{inner_html_node};
3819              #          } else {
3820            } else {            die "_reset_insertion_mode: t27";
             $node = $self->{inner_html_node};  
           }  
3821          }          }
3822        }        }
3823              
3824        ## Step 4..13        ## Step 4..14
3825        my $new_mode = {        my $new_mode;
3826                        select => 'in select',        if ($node->[1] & FOREIGN_EL) {
3827                        td => 'in cell',          !!!cp ('t28.1');
3828                        th => 'in cell',          ## NOTE: Strictly spaking, the line below only applies to MathML and
3829                        tr => 'in row',          ## SVG elements.  Currently the HTML syntax supports only MathML and
3830                        tbody => 'in table body',          ## SVG elements as foreigners.
3831                        thead => 'in table head',          $new_mode = IN_BODY_IM | IN_FOREIGN_CONTENT_IM;
3832                        tfoot => 'in table foot',        } elsif ($node->[1] == TABLE_CELL_EL) {
3833                        caption => 'in caption',          if ($last) {
3834                        colgroup => 'in column group',            !!!cp ('t28.2');
3835                        table => 'in table',            #
3836                        head => 'in body', # not in head!          } else {
3837                        body => 'in body',            !!!cp ('t28.3');
3838                        frameset => 'in frameset',            $new_mode = IN_CELL_IM;
3839                       }->{$node->[1]};          }
3840          } else {
3841            !!!cp ('t28.4');
3842            $new_mode = {
3843                          select => IN_SELECT_IM,
3844                          ## NOTE: |option| and |optgroup| do not set
3845                          ## insertion mode to "in select" by themselves.
3846                          tr => IN_ROW_IM,
3847                          tbody => IN_TABLE_BODY_IM,
3848                          thead => IN_TABLE_BODY_IM,
3849                          tfoot => IN_TABLE_BODY_IM,
3850                          caption => IN_CAPTION_IM,
3851                          colgroup => IN_COLUMN_GROUP_IM,
3852                          table => IN_TABLE_IM,
3853                          head => IN_BODY_IM, # not in head!
3854                          body => IN_BODY_IM,
3855                          frameset => IN_FRAMESET_IM,
3856                         }->{$node->[0]->manakai_local_name};
3857          }
3858        $self->{insertion_mode} = $new_mode and return if defined $new_mode;        $self->{insertion_mode} = $new_mode and return if defined $new_mode;
3859                
3860        ## Step 14        ## Step 15
3861        if ($node->[1] eq 'html') {        if ($node->[1] == HTML_EL) {
3862          unless (defined $self->{head_element}) {          unless (defined $self->{head_element}) {
3863            $self->{insertion_mode} = 'before head';            !!!cp ('t29');
3864              $self->{insertion_mode} = BEFORE_HEAD_IM;
3865          } else {          } else {
3866            $self->{insertion_mode} = 'after head';            ## ISSUE: Can this state be reached?
3867              !!!cp ('t30');
3868              $self->{insertion_mode} = AFTER_HEAD_IM;
3869          }          }
3870          return;          return;
3871          } else {
3872            !!!cp ('t31');
3873        }        }
3874                
       ## Step 15  
       $self->{insertion_mode} = 'in body' and return if $last;  
         
3875        ## Step 16        ## Step 16
3876          $self->{insertion_mode} = IN_BODY_IM and return if $last;
3877          
3878          ## Step 17
3879        $i--;        $i--;
3880        $node = $self->{open_elements}->[$i];        $node = $self->{open_elements}->[$i];
3881                
3882        ## Step 17        ## Step 18
3883        redo S3;        redo S3;
3884      } # S3      } # S3
3885    
3886      die "$0: _reset_insertion_mode: This line should never be reached";
3887  } # _reset_insertion_mode  } # _reset_insertion_mode
3888    
3889  sub _tree_construction_main ($) {  sub _tree_construction_main ($) {
3890    my $self = shift;    my $self = shift;
3891    
   my $previous_insertion_mode;  
   
3892    my $active_formatting_elements = [];    my $active_formatting_elements = [];
3893    
3894    my $reconstruct_active_formatting_elements = sub { # MUST    my $reconstruct_active_formatting_elements = sub { # MUST
# Line 2121  sub _tree_construction_main ($) { Line 3905  sub _tree_construction_main ($) {
3905      return if $entry->[0] eq '#marker';      return if $entry->[0] eq '#marker';
3906      for (@{$self->{open_elements}}) {      for (@{$self->{open_elements}}) {
3907        if ($entry->[0] eq $_->[0]) {        if ($entry->[0] eq $_->[0]) {
3908            !!!cp ('t32');
3909          return;          return;
3910        }        }
3911      }      }
# Line 2135  sub _tree_construction_main ($) { Line 3920  sub _tree_construction_main ($) {
3920    
3921        ## Step 6        ## Step 6
3922        if ($entry->[0] eq '#marker') {        if ($entry->[0] eq '#marker') {
3923            !!!cp ('t33_1');
3924          #          #
3925        } else {        } else {
3926          my $in_open_elements;          my $in_open_elements;
3927          OE: for (@{$self->{open_elements}}) {          OE: for (@{$self->{open_elements}}) {
3928            if ($entry->[0] eq $_->[0]) {            if ($entry->[0] eq $_->[0]) {
3929                !!!cp ('t33');
3930              $in_open_elements = 1;              $in_open_elements = 1;
3931              last OE;              last OE;
3932            }            }
3933          }          }
3934          if ($in_open_elements) {          if ($in_open_elements) {
3935              !!!cp ('t34');
3936            #            #
3937          } else {          } else {
3938              ## NOTE: <!DOCTYPE HTML><p><b><i><u></p> <p>X
3939              !!!cp ('t35');
3940            redo S4;            redo S4;
3941          }          }
3942        }        }
# Line 2169  sub _tree_construction_main ($) { Line 3959  sub _tree_construction_main ($) {
3959    
3960        ## Step 11        ## Step 11
3961        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {        unless ($clone->[0] eq $active_formatting_elements->[-1]->[0]) {
3962            !!!cp ('t36');
3963          ## Step 7'          ## Step 7'
3964          $i++;          $i++;
3965          $entry = $active_formatting_elements->[$i];          $entry = $active_formatting_elements->[$i];
3966                    
3967          redo S7;          redo S7;
3968        }        }
3969    
3970          !!!cp ('t37');
3971      } # S7      } # S7
3972    }; # $reconstruct_active_formatting_elements    }; # $reconstruct_active_formatting_elements
3973    
3974    my $clear_up_to_marker = sub {    my $clear_up_to_marker = sub {
3975      for (reverse 0..$#$active_formatting_elements) {      for (reverse 0..$#$active_formatting_elements) {
3976        if ($active_formatting_elements->[$_]->[0] eq '#marker') {        if ($active_formatting_elements->[$_]->[0] eq '#marker') {
3977            !!!cp ('t38');
3978          splice @$active_formatting_elements, $_;          splice @$active_formatting_elements, $_;
3979          return;          return;
3980        }        }
3981      }      }
3982    
3983        !!!cp ('t39');
3984    }; # $clear_up_to_marker    }; # $clear_up_to_marker
3985    
3986    my $parse_rcdata = sub ($$) {    my $insert;
3987      my ($content_model_flag, $insert) = @_;  
3988      my $parse_rcdata = sub ($) {
3989        my ($content_model_flag) = @_;
3990    
3991      ## Step 1      ## Step 1
3992      my $start_tag_name = $token->{tag_name};      my $start_tag_name = $token->{tag_name};
3993      my $el;      !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
     !!!create-element ($el, $start_tag_name, $token->{attributes});  
3994    
3995      ## Step 2      ## Step 2
     $insert->($el); # /context node/->append_child ($el)  
   
     ## Step 3  
3996      $self->{content_model} = $content_model_flag; # CDATA or RCDATA      $self->{content_model} = $content_model_flag; # CDATA or RCDATA
3997      delete $self->{escape}; # MUST      delete $self->{escape}; # MUST
3998    
3999      ## Step 4      ## Step 3, 4
4000      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);  
     }  
4001    
4002      ## Step 6      !!!nack ('t40.1');
     $self->{content_model} = PCDATA_CONTENT_MODEL;  
   
     ## Step 7  
     if ($token->{type} eq 'end tag' and $token->{tag_name} eq $start_tag_name) {  
       ## Ignore the token  
     } elsif ($content_model_flag == CDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in CDATA:#'.$token->{type});  
     } elsif ($content_model_flag == RCDATA_CONTENT_MODEL) {  
       !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
     } else {  
       die "$0: $content_model_flag in parse_rcdata";  
     }  
4003      !!!next-token;      !!!next-token;
4004    }; # $parse_rcdata    }; # $parse_rcdata
4005    
4006    my $script_start_tag = sub ($) {    my $script_start_tag = sub () {
4007      my $insert = $_[0];      ## Step 1
4008      my $script_el;      my $script_el;
4009      !!!create-element ($script_el, 'script', $token->{attributes});      !!!create-element ($script_el, $HTML_NS, 'script', $token->{attributes}, $token);
4010    
4011        ## Step 2
4012      ## TODO: mark as "parser-inserted"      ## TODO: mark as "parser-inserted"
4013    
4014        ## Step 3
4015        ## TODO: Mark as "already executed", if ...
4016    
4017        ## Step 4
4018        $insert->($script_el);
4019    
4020        ## ISSUE: $script_el is not put into the stack
4021        push @{$self->{open_elements}}, [$script_el, $el_category->{script}];
4022    
4023        ## Step 5
4024      $self->{content_model} = CDATA_CONTENT_MODEL;      $self->{content_model} = CDATA_CONTENT_MODEL;
4025      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} = PCDATA_CONTENT_MODEL;  
4026    
4027      if ($token->{type} eq 'end tag' and      ## Step 6-7
4028          $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  
4029    
4030        $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...  
     }  
       
4031      !!!next-token;      !!!next-token;
4032    }; # $script_start_tag    }; # $script_start_tag
4033    
4034      ## NOTE: $open_tables->[-1]->[0] is the "current table" element node.
4035      ## NOTE: $open_tables->[-1]->[1] is the "tainted" flag.
4036      ## NOTE: $open_tables->[-1]->[2] is set false when non-Text node inserted.
4037      my $open_tables = [[$self->{open_elements}->[0]->[0]]];
4038    
4039    my $formatting_end_tag = sub {    my $formatting_end_tag = sub {
4040      my $tag_name = shift;      my $end_tag_token = shift;
4041        my $tag_name = $end_tag_token->{tag_name};
4042    
4043        ## NOTE: The adoption agency algorithm (AAA).
4044    
4045      FET: {      FET: {
4046        ## Step 1        ## Step 1
4047        my $formatting_element;        my $formatting_element;
4048        my $formatting_element_i_in_active;        my $formatting_element_i_in_active;
4049        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4050          if ($active_formatting_elements->[$_]->[1] eq $tag_name) {          if ($active_formatting_elements->[$_]->[0] eq '#marker') {
4051              !!!cp ('t52');
4052              last AFE;
4053            } elsif ($active_formatting_elements->[$_]->[0]->manakai_local_name
4054                         eq $tag_name) {
4055              !!!cp ('t51');
4056            $formatting_element = $active_formatting_elements->[$_];            $formatting_element = $active_formatting_elements->[$_];
4057            $formatting_element_i_in_active = $_;            $formatting_element_i_in_active = $_;
4058            last AFE;            last AFE;
         } elsif ($active_formatting_elements->[$_]->[0] eq '#marker') {  
           last AFE;  
4059          }          }
4060        } # AFE        } # AFE
4061        unless (defined $formatting_element) {        unless (defined $formatting_element) {
4062          !!!parse-error (type => 'unmatched end tag:'.$tag_name);          !!!cp ('t53');
4063            !!!parse-error (type => 'unmatched end tag', text => $tag_name, token => $end_tag_token);
4064          ## Ignore the token          ## Ignore the token
4065          !!!next-token;          !!!next-token;
4066          return;          return;
# Line 2307  sub _tree_construction_main ($) { Line 4072  sub _tree_construction_main ($) {
4072          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4073          if ($node->[0] eq $formatting_element->[0]) {          if ($node->[0] eq $formatting_element->[0]) {
4074            if ($in_scope) {            if ($in_scope) {
4075                !!!cp ('t54');
4076              $formatting_element_i_in_open = $_;              $formatting_element_i_in_open = $_;
4077              last INSCOPE;              last INSCOPE;
4078            } else { # in open elements but not in scope            } else { # in open elements but not in scope
4079              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t55');
4080                !!!parse-error (type => 'unmatched end tag',
4081                                text => $token->{tag_name},
4082                                token => $end_tag_token);
4083              ## Ignore the token              ## Ignore the token
4084              !!!next-token;              !!!next-token;
4085              return;              return;
4086            }            }
4087          } elsif ({          } elsif ($node->[1] & SCOPING_EL) {
4088                    table => 1, caption => 1, td => 1, th => 1,            !!!cp ('t56');
                   button => 1, marquee => 1, object => 1, html => 1,  
                  }->{$node->[1]}) {  
4089            $in_scope = 0;            $in_scope = 0;
4090          }          }
4091        } # INSCOPE        } # INSCOPE
4092        unless (defined $formatting_element_i_in_open) {        unless (defined $formatting_element_i_in_open) {
4093          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});          !!!cp ('t57');
4094            !!!parse-error (type => 'unmatched end tag',
4095                            text => $token->{tag_name},
4096                            token => $end_tag_token);
4097          pop @$active_formatting_elements; # $formatting_element          pop @$active_formatting_elements; # $formatting_element
4098          !!!next-token; ## TODO: ok?          !!!next-token; ## TODO: ok?
4099          return;          return;
4100        }        }
4101        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {        if (not $self->{open_elements}->[-1]->[0] eq $formatting_element->[0]) {
4102          !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);          !!!cp ('t58');
4103            !!!parse-error (type => 'not closed',
4104                            text => $self->{open_elements}->[-1]->[0]
4105                                ->manakai_local_name,
4106                            token => $end_tag_token);
4107        }        }
4108                
4109        ## Step 2        ## Step 2
# Line 2337  sub _tree_construction_main ($) { Line 4111  sub _tree_construction_main ($) {
4111        my $furthest_block_i_in_open;        my $furthest_block_i_in_open;
4112        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4113          my $node = $self->{open_elements}->[$_];          my $node = $self->{open_elements}->[$_];
4114          if (not $formatting_category->{$node->[1]} and          if (not ($node->[1] & FORMATTING_EL) and
4115              #not $phrasing_category->{$node->[1]} and              #not $phrasing_category->{$node->[1]} and
4116              ($special_category->{$node->[1]} or              ($node->[1] & SPECIAL_EL or
4117               $scoping_category->{$node->[1]})) {               $node->[1] & SCOPING_EL)) { ## Scoping is redundant, maybe
4118              !!!cp ('t59');
4119            $furthest_block = $node;            $furthest_block = $node;
4120            $furthest_block_i_in_open = $_;            $furthest_block_i_in_open = $_;
4121              ## NOTE: The topmost (eldest) node.
4122          } elsif ($node->[0] eq $formatting_element->[0]) {          } elsif ($node->[0] eq $formatting_element->[0]) {
4123              !!!cp ('t60');
4124            last OE;            last OE;
4125          }          }
4126        } # OE        } # OE
4127                
4128        ## Step 3        ## Step 3
4129        unless (defined $furthest_block) { # MUST        unless (defined $furthest_block) { # MUST
4130            !!!cp ('t61');
4131          splice @{$self->{open_elements}}, $formatting_element_i_in_open;          splice @{$self->{open_elements}}, $formatting_element_i_in_open;
4132          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;          splice @$active_formatting_elements, $formatting_element_i_in_active, 1;
4133          !!!next-token;          !!!next-token;
# Line 2362  sub _tree_construction_main ($) { Line 4140  sub _tree_construction_main ($) {
4140        ## Step 5        ## Step 5
4141        my $furthest_block_parent = $furthest_block->[0]->parent_node;        my $furthest_block_parent = $furthest_block->[0]->parent_node;
4142        if (defined $furthest_block_parent) {        if (defined $furthest_block_parent) {
4143            !!!cp ('t62');
4144          $furthest_block_parent->remove_child ($furthest_block->[0]);          $furthest_block_parent->remove_child ($furthest_block->[0]);
4145        }        }
4146                
# Line 2384  sub _tree_construction_main ($) { Line 4163  sub _tree_construction_main ($) {
4163          S7S2: {          S7S2: {
4164            for (reverse 0..$#$active_formatting_elements) {            for (reverse 0..$#$active_formatting_elements) {
4165              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {              if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
4166                  !!!cp ('t63');
4167                $node_i_in_active = $_;                $node_i_in_active = $_;
4168                last S7S2;                last S7S2;
4169              }              }
# Line 2397  sub _tree_construction_main ($) { Line 4177  sub _tree_construction_main ($) {
4177                    
4178          ## Step 4          ## Step 4
4179          if ($last_node->[0] eq $furthest_block->[0]) {          if ($last_node->[0] eq $furthest_block->[0]) {
4180              !!!cp ('t64');
4181            $bookmark_prev_el = $node->[0];            $bookmark_prev_el = $node->[0];
4182          }          }
4183                    
4184          ## Step 5          ## Step 5
4185          if ($node->[0]->has_child_nodes ()) {          if ($node->[0]->has_child_nodes ()) {
4186              !!!cp ('t65');
4187            my $clone = [$node->[0]->clone_node (0), $node->[1]];            my $clone = [$node->[0]->clone_node (0), $node->[1]];
4188            $active_formatting_elements->[$node_i_in_active] = $clone;            $active_formatting_elements->[$node_i_in_active] = $clone;
4189            $self->{open_elements}->[$node_i_in_open] = $clone;            $self->{open_elements}->[$node_i_in_open] = $clone;
# Line 2419  sub _tree_construction_main ($) { Line 4201  sub _tree_construction_main ($) {
4201        } # S7          } # S7  
4202                
4203        ## Step 8        ## Step 8
4204        $common_ancestor_node->[0]->append_child ($last_node->[0]);        if ($common_ancestor_node->[1] & TABLE_ROWS_EL) {
4205            my $foster_parent_element;
4206            my $next_sibling;
4207            OE: for (reverse 0..$#{$self->{open_elements}}) {
4208              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
4209                                 my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4210                                 if (defined $parent and $parent->node_type == 1) {
4211                                   !!!cp ('t65.1');
4212                                   $foster_parent_element = $parent;
4213                                   $next_sibling = $self->{open_elements}->[$_]->[0];
4214                                 } else {
4215                                   !!!cp ('t65.2');
4216                                   $foster_parent_element
4217                                     = $self->{open_elements}->[$_ - 1]->[0];
4218                                 }
4219                                 last OE;
4220                               }
4221                             } # OE
4222                             $foster_parent_element = $self->{open_elements}->[0]->[0]
4223                               unless defined $foster_parent_element;
4224            $foster_parent_element->insert_before ($last_node->[0], $next_sibling);
4225            $open_tables->[-1]->[1] = 1; # tainted
4226          } else {
4227            !!!cp ('t65.3');
4228            $common_ancestor_node->[0]->append_child ($last_node->[0]);
4229          }
4230                
4231        ## Step 9        ## Step 9
4232        my $clone = [$formatting_element->[0]->clone_node (0),        my $clone = [$formatting_element->[0]->clone_node (0),
# Line 2436  sub _tree_construction_main ($) { Line 4243  sub _tree_construction_main ($) {
4243        my $i;        my $i;
4244        AFE: for (reverse 0..$#$active_formatting_elements) {        AFE: for (reverse 0..$#$active_formatting_elements) {
4245          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {          if ($active_formatting_elements->[$_]->[0] eq $formatting_element->[0]) {
4246              !!!cp ('t66');
4247            splice @$active_formatting_elements, $_, 1;            splice @$active_formatting_elements, $_, 1;
4248            $i-- and last AFE if defined $i;            $i-- and last AFE if defined $i;
4249          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {          } elsif ($active_formatting_elements->[$_]->[0] eq $bookmark_prev_el) {
4250              !!!cp ('t67');
4251            $i = $_;            $i = $_;
4252          }          }
4253        } # AFE        } # AFE
# Line 2448  sub _tree_construction_main ($) { Line 4257  sub _tree_construction_main ($) {
4257        undef $i;        undef $i;
4258        OE: for (reverse 0..$#{$self->{open_elements}}) {        OE: for (reverse 0..$#{$self->{open_elements}}) {
4259          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {          if ($self->{open_elements}->[$_]->[0] eq $formatting_element->[0]) {
4260              !!!cp ('t68');
4261            splice @{$self->{open_elements}}, $_, 1;            splice @{$self->{open_elements}}, $_, 1;
4262            $i-- and last OE if defined $i;            $i-- and last OE if defined $i;
4263          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {          } elsif ($self->{open_elements}->[$_]->[0] eq $furthest_block->[0]) {
4264              !!!cp ('t69');
4265            $i = $_;            $i = $_;
4266          }          }
4267        } # OE        } # OE
4268        splice @{$self->{open_elements}}, $i + 1, 1, $clone;        splice @{$self->{open_elements}}, $i + 1, 0, $clone;
4269                
4270        ## Step 14        ## Step 14
4271        redo FET;        redo FET;
4272      } # FET      } # FET
4273    }; # $formatting_end_tag    }; # $formatting_end_tag
4274    
4275    my $insert_to_current = sub {    $insert = my $insert_to_current = sub {
4276      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);      $self->{open_elements}->[-1]->[0]->append_child ($_[0]);
4277    }; # $insert_to_current    }; # $insert_to_current
4278    
4279    my $insert_to_foster = sub {    my $insert_to_foster = sub {
4280                         my $child = shift;      my $child = shift;
4281                         if ({      if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
4282                              table => 1, tbody => 1, tfoot => 1,        # MUST
4283                              thead => 1, tr => 1,        my $foster_parent_element;
4284                             }->{$self->{open_elements}->[-1]->[1]}) {        my $next_sibling;
4285                           # MUST        OE: for (reverse 0..$#{$self->{open_elements}}) {
4286                           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') {  
4287                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                               my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
4288                               if (defined $parent and $parent->node_type == 1) {                               if (defined $parent and $parent->node_type == 1) {
4289                                   !!!cp ('t70');
4290                                 $foster_parent_element = $parent;                                 $foster_parent_element = $parent;
4291                                 $next_sibling = $self->{open_elements}->[$_]->[0];                                 $next_sibling = $self->{open_elements}->[$_]->[0];
4292                               } else {                               } else {
4293                                   !!!cp ('t71');
4294                                 $foster_parent_element                                 $foster_parent_element
4295                                   = $self->{open_elements}->[$_ - 1]->[0];                                   = $self->{open_elements}->[$_ - 1]->[0];
4296                               }                               }
# Line 2491  sub _tree_construction_main ($) { Line 4301  sub _tree_construction_main ($) {
4301                             unless defined $foster_parent_element;                             unless defined $foster_parent_element;
4302                           $foster_parent_element->insert_before                           $foster_parent_element->insert_before
4303                             ($child, $next_sibling);                             ($child, $next_sibling);
4304                         } else {        $open_tables->[-1]->[1] = 1; # tainted
4305                           $self->{open_elements}->[-1]->[0]->append_child ($child);      } else {
4306                         }        !!!cp ('t72');
4307          $self->{open_elements}->[-1]->[0]->append_child ($child);
4308        }
4309    }; # $insert_to_foster    }; # $insert_to_foster
4310    
4311    my $in_body = sub {    ## NOTE: Insert a character (MUST): When a character is inserted, if
4312      my $insert = shift;    ## the last node that was inserted by the parser is a Text node and
4313      if ($token->{type} eq 'start tag') {    ## the character has to be inserted after that node, then the
4314        if ($token->{tag_name} eq 'script') {    ## character is appended to the Text node.  However, if any other
4315          ## NOTE: This is an "as if in head" code clone    ## node is inserted by the parser, then a new Text node is created
4316          $script_start_tag->($insert);    ## and the character is appended as that Text node.  If I'm not
4317          return;    ## wrong, for a parser with scripting disabled, there are only two
4318        } elsif ($token->{tag_name} eq 'style') {    ## cases where this occurs.  One is the case where an element node
4319          ## NOTE: This is an "as if in head" code clone    ## is inserted to the |head| element.  This is covered by using the
4320          $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);    ## |$self->{head_element_inserted}| flag.  Another is the case where
4321          return;    ## an element or comment is inserted into the |table| subtree while
4322        } elsif ({    ## foster parenting happens.  This is covered by using the [2] flag
4323                  base => 1, link => 1,    ## of the |$open_tables| structure.  All other cases are handled
4324                 }->{$token->{tag_name}}) {    ## simply by calling |manakai_append_text| method.
4325          ## NOTE: This is an "as if in head" code clone, only "-t" differs  
4326          !!!insert-element-t ($token->{tag_name}, $token->{attributes});    ## TODO: |<body><script>document.write("a<br>");
4327          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.    ## document.body.removeChild (document.body.lastChild);
4328          !!!next-token;    ## document.write ("b")</script>|
4329          return;  
4330        } elsif ($token->{tag_name} eq 'meta') {    B: while (1) {
4331          ## NOTE: This is an "as if in head" code clone, only "-t" differs      if ($token->{type} == DOCTYPE_TOKEN) {
4332          !!!insert-element-t ($token->{tag_name}, $token->{attributes});        !!!cp ('t73');
4333          pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.        !!!parse-error (type => 'in html:#DOCTYPE', token => $token);
4334          ## Ignore the token
4335          ## Stay in the phase
4336          !!!next-token;
4337          next B;
4338        } elsif ($token->{type} == START_TAG_TOKEN and
4339                 $token->{tag_name} eq 'html') {
4340          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
4341            !!!cp ('t79');
4342            !!!parse-error (type => 'after html', text => 'html', token => $token);
4343            $self->{insertion_mode} = AFTER_BODY_IM;
4344          } elsif ($self->{insertion_mode} == AFTER_HTML_FRAMESET_IM) {
4345            !!!cp ('t80');
4346            !!!parse-error (type => 'after html', text => 'html', token => $token);
4347            $self->{insertion_mode} = AFTER_FRAMESET_IM;
4348          } else {
4349            !!!cp ('t81');
4350          }
4351    
4352          unless ($self->{confident}) {        !!!cp ('t82');
4353            my $charset;        !!!parse-error (type => 'not first start tag', token => $token);
4354            if ($token->{attributes}->{charset}) { ## TODO: And if supported        my $top_el = $self->{open_elements}->[0]->[0];
4355              $charset = $token->{attributes}->{charset}->{value};        for my $attr_name (keys %{$token->{attributes}}) {
4356            }          unless ($top_el->has_attribute_ns (undef, $attr_name)) {
4357            if ($token->{attributes}->{'http-equiv'}) {            !!!cp ('t84');
4358              ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.            $top_el->set_attribute_ns
4359              if ($token->{attributes}->{'http-equiv'}->{value}              (undef, [undef, $attr_name],
4360                  =~ /\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  
4361          }          }
4362          }
4363          !!!next-token;        !!!nack ('t84.1');
4364          return;        !!!next-token;
4365        } elsif ($token->{tag_name} eq 'title') {        next B;
4366          !!!parse-error (type => 'in body:title');      } elsif ($token->{type} == COMMENT_TOKEN) {
4367          ## NOTE: This is an "as if in head" code clone        my $comment = $self->{document}->create_comment ($token->{data});
4368          $parse_rcdata->(RCDATA_CONTENT_MODEL, sub {        if ($self->{insertion_mode} & AFTER_HTML_IMS) {
4369            if (defined $self->{head_element}) {          !!!cp ('t85');
4370              $self->{head_element}->append_child ($_[0]);          $self->{document}->append_child ($comment);
4371            } else {        } elsif ($self->{insertion_mode} == AFTER_BODY_IM) {
4372              $insert->($_[0]);          !!!cp ('t86');
4373            }          $self->{open_elements}->[0]->[0]->append_child ($comment);
4374          });        } else {
4375          return;          !!!cp ('t87');
4376        } elsif ($token->{tag_name} eq 'body') {          $self->{open_elements}->[-1]->[0]->append_child ($comment);
4377          !!!parse-error (type => 'in body:body');          $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
4378                        }
4379          if (@{$self->{open_elements}} == 1 or        !!!next-token;
4380              $self->{open_elements}->[1]->[1] ne 'body') {        next B;
4381            ## Ignore the token      } elsif ($self->{insertion_mode} & IN_CDATA_RCDATA_IM) {
4382          if ($token->{type} == CHARACTER_TOKEN) {
4383            $token->{data} =~ s/^\x0A// if $self->{ignore_newline};
4384            delete $self->{ignore_newline};
4385    
4386            if (length $token->{data}) {
4387              !!!cp ('t43');
4388              $self->{open_elements}->[-1]->[0]->manakai_append_text
4389                  ($token->{data});
4390          } else {          } else {
4391            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});  
             }  
           }  
4392          }          }
4393          !!!next-token;          !!!next-token;
4394          return;          next B;
4395        } elsif ({        } elsif ($token->{type} == END_TAG_TOKEN) {
4396                  address => 1, blockquote => 1, center => 1, dir => 1,          delete $self->{ignore_newline};
4397                  div => 1, dl => 1, fieldset => 1, listing => 1,  
4398                  menu => 1, ol => 1, p => 1, ul => 1,          if ($token->{tag_name} eq 'script') {
4399                  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  
4400                        
4401          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            ## Para 1-2
4402          if ($token->{tag_name} eq 'pre') {            my $script = pop @{$self->{open_elements}};
4403            !!!next-token;            
4404            if ($token->{type} eq 'character') {            ## Para 3
4405              $token->{data} =~ s/^\x0A//;            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4406              unless (length $token->{data}) {  
4407                !!!next-token;            ## Para 4
4408              }            ## TODO: $old_insertion_point = $current_insertion_point;
4409            }            ## TODO: $current_insertion_point = just before $self->{nc};
4410          } else {  
4411            !!!next-token;            ## Para 5
4412          }            ## TODO: Run the $script->[0].
4413          return;  
4414        } elsif ($token->{tag_name} eq 'form') {            ## Para 6
4415          if (defined $self->{form_element}) {            ## TODO: $current_insertion_point = $old_insertion_point;
4416            !!!parse-error (type => 'in form:form');  
4417            ## Ignore the token            ## Para 7
4418              ## TODO: if ($pending_external_script) {
4419                ## TODO: ...
4420              ## TODO: }
4421    
4422            !!!next-token;            !!!next-token;
4423            return;            next B;
4424          } else {          } else {
4425            ## has a p element in scope            !!!cp ('t42');
4426            INSCOPE: for (reverse @{$self->{open_elements}}) {  
4427              if ($_->[1] eq 'p') {            pop @{$self->{open_elements}};
4428                !!!back-token;  
4429                $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];  
4430            !!!next-token;            !!!next-token;
4431            return;            next B;
4432          }          }
4433        } elsif ($token->{tag_name} eq 'li') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4434          ## has a p element in scope          delete $self->{ignore_newline};
4435          INSCOPE: for (reverse @{$self->{open_elements}}) {  
4436            if ($_->[1] eq 'p') {          !!!cp ('t44');
4437              !!!back-token;          !!!parse-error (type => 'not closed',
4438              $token = {type => 'end tag', tag_name => 'p'};                          text => $self->{open_elements}->[-1]->[0]
4439              return;                              ->manakai_local_name,
4440            } elsif ({                          token => $token);
4441                      table => 1, caption => 1, td => 1, th => 1,  
4442                      button => 1, marquee => 1, object => 1, html => 1,          #if ($self->{open_elements}->[-1]->[1] == SCRIPT_EL) {
4443                     }->{$_->[1]}) {          #  ## TODO: Mark as "already executed"
             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} = PLAINTEXT_CONTENT_MODEL;  
             
         !!!next-token;  
         return;  
       } elsif ({  
                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
                }->{$token->{tag_name}}) {  
         ## has a p element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         ## NOTE: See <http://html5.org/tools/web-apps-tracker?from=925&to=926>  
         ## has an element in scope  
         #my $i;  
         #INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
         #  my $node = $self->{open_elements}->[$_];  
         #  if ({  
         #       h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,  
         #      }->{$node->[1]}) {  
         #    $i = $_;  
         #    last INSCOPE;  
         #  } elsif ({  
         #            table => 1, caption => 1, td => 1, th => 1,  
         #            button => 1, marquee => 1, object => 1, html => 1,  
         #           }->{$node->[1]}) {  
         #    last INSCOPE;  
         #  }  
         #} # INSCOPE  
         #    
         #if (defined $i) {  
         #  !!! parse-error (type => 'in hn:hn');  
         #  splice @{$self->{open_elements}}, $i;  
4444          #}          #}
4445              
4446          !!!insert-element-t ($token->{tag_name}, $token->{attributes});          pop @{$self->{open_elements}};
4447              
4448            $self->{insertion_mode} &= ~ IN_CDATA_RCDATA_IM;
4449            ## Reprocess.
4450            next B;
4451          } else {
4452            die "$0: $token->{type}: In CDATA/RCDATA: Unknown token type";        
4453          }
4454        } elsif ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
4455          if ($token->{type} == CHARACTER_TOKEN) {
4456            !!!cp ('t87.1');
4457            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
4458          !!!next-token;          !!!next-token;
4459          return;          next B;
4460        } elsif ($token->{tag_name} eq 'a') {        } elsif ($token->{type} == START_TAG_TOKEN) {
4461          AFE: for my $i (reverse 0..$#$active_formatting_elements) {          if ((not {mglyph => 1, malignmark => 1}->{$token->{tag_name}} and
4462            my $node = $active_formatting_elements->[$i];               $self->{open_elements}->[-1]->[1] & FOREIGN_FLOW_CONTENT_EL) or
4463            if ($node->[1] eq 'a') {              not ($self->{open_elements}->[-1]->[1] & FOREIGN_EL) or
4464              !!!parse-error (type => 'in a:a');              ($token->{tag_name} eq 'svg' and
4465                             $self->{open_elements}->[-1]->[1] == MML_AXML_EL)) {
4466              !!!back-token;            ## NOTE: "using the rules for secondary insertion mode"then"continue"
4467              $token = {type => 'end tag', tag_name => 'a'};            !!!cp ('t87.2');
4468              $formatting_end_tag->($token->{tag_name});            #
4469                        } elsif ({
4470              AFE2: for (reverse 0..$#$active_formatting_elements) {                    b => 1, big => 1, blockquote => 1, body => 1, br => 1,
4471                if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {                    center => 1, code => 1, dd => 1, div => 1, dl => 1, dt => 1,
4472                  splice @$active_formatting_elements, $_, 1;                    em => 1, embed => 1, font => 1, h1 => 1, h2 => 1, h3 => 1,
4473                  last AFE2;                    h4 => 1, h5 => 1, h6 => 1, head => 1, hr => 1, i => 1,
4474                }                    img => 1, li => 1, listing => 1, menu => 1, meta => 1,
4475              } # AFE2                    nobr => 1, ol => 1, p => 1, pre => 1, ruby => 1, s => 1,
4476              OE: for (reverse 0..$#{$self->{open_elements}}) {                    small => 1, span => 1, strong => 1, strike => 1, sub => 1,
4477                if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {                    sup => 1, table => 1, tt => 1, u => 1, ul => 1, var => 1,
4478                  splice @{$self->{open_elements}}, $_, 1;                   }->{$token->{tag_name}}) {
4479                  last OE;            !!!cp ('t87.2');
4480                }            !!!parse-error (type => 'not closed',
4481              } # OE                            text => $self->{open_elements}->[-1]->[0]
4482              last AFE;                                ->manakai_local_name,
4483            } elsif ($node->[0] eq '#marker') {                            token => $token);
4484              last AFE;  
4485              pop @{$self->{open_elements}}
4486                  while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4487    
4488              $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4489              ## Reprocess.
4490              next B;
4491            } else {
4492              my $nsuri = $self->{open_elements}->[-1]->[0]->namespace_uri;
4493              my $tag_name = $token->{tag_name};
4494              if ($nsuri eq $SVG_NS) {
4495                $tag_name = {
4496                   altglyph => 'altGlyph',
4497                   altglyphdef => 'altGlyphDef',
4498                   altglyphitem => 'altGlyphItem',
4499                   animatecolor => 'animateColor',
4500                   animatemotion => 'animateMotion',
4501                   animatetransform => 'animateTransform',
4502                   clippath => 'clipPath',
4503                   feblend => 'feBlend',
4504                   fecolormatrix => 'feColorMatrix',
4505                   fecomponenttransfer => 'feComponentTransfer',
4506                   fecomposite => 'feComposite',
4507                   feconvolvematrix => 'feConvolveMatrix',
4508                   fediffuselighting => 'feDiffuseLighting',
4509                   fedisplacementmap => 'feDisplacementMap',
4510                   fedistantlight => 'feDistantLight',
4511                   feflood => 'feFlood',
4512                   fefunca => 'feFuncA',
4513                   fefuncb => 'feFuncB',
4514                   fefuncg => 'feFuncG',
4515                   fefuncr => 'feFuncR',
4516                   fegaussianblur => 'feGaussianBlur',
4517                   feimage => 'feImage',
4518                   femerge => 'feMerge',
4519                   femergenode => 'feMergeNode',
4520                   femorphology => 'feMorphology',
4521                   feoffset => 'feOffset',
4522                   fepointlight => 'fePointLight',
4523                   fespecularlighting => 'feSpecularLighting',
4524                   fespotlight => 'feSpotLight',
4525                   fetile => 'feTile',
4526                   feturbulence => 'feTurbulence',
4527                   foreignobject => 'foreignObject',
4528                   glyphref => 'glyphRef',
4529                   lineargradient => 'linearGradient',
4530                   radialgradient => 'radialGradient',
4531                   #solidcolor => 'solidColor', ## NOTE: Commented in spec (SVG1.2)
4532                   textpath => 'textPath',  
4533                }->{$tag_name} || $tag_name;
4534            }            }
         } # AFE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
4535    
4536          !!!insert-element-t ($token->{tag_name}, $token->{attributes});            ## "adjust SVG attributes" (SVG only) - done in insert-element-f
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
4537    
4538          !!!next-token;            ## "adjust foreign attributes" - done in insert-element-f
         return;  
       } elsif ({  
                 b => 1, big => 1, em => 1, font => 1, i => 1,  
                 s => 1, small => 1, strile => 1,  
                 strong => 1, tt => 1, u => 1,  
                }->{$token->{tag_name}}) {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'nobr') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
4539    
4540          ## has a |nobr| element in scope            !!!insert-element-f ($nsuri, $tag_name, $token->{attributes}, $token);
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'nobr') {  
             !!!parse-error (type => 'not closed:nobr');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'nobr'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, $self->{open_elements}->[-1];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'button') {  
         ## has a button element in scope  
         INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
           my $node = $self->{open_elements}->[$_];  
           if ($node->[1] eq 'button') {  
             !!!parse-error (type => 'in button:button');  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'button'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         $reconstruct_active_formatting_elements->($insert_to_current);  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
4541    
4542          !!!next-token;            if ($self->{self_closing}) {
4543          return;              pop @{$self->{open_elements}};
4544        } elsif ($token->{tag_name} eq 'marquee' or              !!!ack ('t87.3');
4545                 $token->{tag_name} eq 'object') {            } else {
4546          $reconstruct_active_formatting_elements->($insert_to_current);              !!!cp ('t87.4');
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         push @$active_formatting_elements, ['#marker', ''];  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'xmp') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'table') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
4547            }            }
         } # 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';  
         }  
4548    
         ## NOTE: There is an "as if <br>" code clone.  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'hr') {  
         ## has a p element in scope  
         INSCOPE: for (reverse @{$self->{open_elements}}) {  
           if ($_->[1] eq 'p') {  
             !!!back-token;  
             $token = {type => 'end tag', tag_name => 'p'};  
             return;  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$_->[1]}) {  
             last INSCOPE;  
           }  
         } # INSCOPE  
             
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         pop @{$self->{open_elements}};  
             
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'input') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
         ## TODO: associate with $self->{form_element} if defined  
         pop @{$self->{open_elements}};  
           
         !!!next-token;  
         return;  
       } elsif ($token->{tag_name} eq 'isindex') {  
         !!!parse-error (type => 'isindex');  
           
         if (defined $self->{form_element}) {  
           ## Ignore the token  
4549            !!!next-token;            !!!next-token;
4550            return;            next B;
4551          } else {          }
4552            my $at = $token->{attributes};        } elsif ($token->{type} == END_TAG_TOKEN) {
4553            my $form_attrs;          ## NOTE: "using the rules for secondary insertion mode" then "continue"
4554            $form_attrs->{action} = $at->{action} if $at->{action};          !!!cp ('t87.5');
4555            my $prompt_attr = $at->{prompt};          #
4556            $at->{name} = {name => 'name', value => 'isindex'};        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
4557            delete $at->{action};          !!!cp ('t87.6');
4558            delete $at->{prompt};          !!!parse-error (type => 'not closed',
4559            my @tokens = (                          text => $self->{open_elements}->[-1]->[0]
4560                          {type => 'start tag', tag_name => 'form',                              ->manakai_local_name,
4561                           attributes => $form_attrs},                          token => $token);
4562                          {type => 'start tag', tag_name => 'hr'},  
4563                          {type => 'start tag', tag_name => 'p'},          pop @{$self->{open_elements}}
4564                          {type => 'start tag', tag_name => 'label'},              while $self->{open_elements}->[-1]->[1] & FOREIGN_EL;
4565                         );  
4566            if ($prompt_attr) {          ## NOTE: |<span><svg>| ... two parse errors, |<svg>| ... a parse error.
4567              push @tokens, {type => 'character', data => $prompt_attr->{value}};  
4568            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
4569            ## Reprocess.
4570            next B;
4571          } else {
4572            die "$0: $token->{type}: Unknown token type";        
4573          }
4574        }
4575    
4576        if ($self->{insertion_mode} & HEAD_IMS) {
4577          if ($token->{type} == CHARACTER_TOKEN) {
4578            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
4579              unless ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4580                if ($self->{head_element_inserted}) {
4581                  !!!cp ('t88.3');
4582                  $self->{open_elements}->[-1]->[0]->append_child
4583                    ($self->{document}->create_text_node ($1));
4584                  delete $self->{head_element_inserted};
4585                  ## NOTE: |</head> <link> |
4586                  #
4587                } else {
4588                  !!!cp ('t88.2');
4589                  $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
4590                  ## NOTE: |</head> &#x20;|
4591                  #
4592                }
4593            } else {            } else {
4594              push @tokens, {type => 'character',              !!!cp ('t88.1');
4595                             data => 'This is a searchable index. Insert your search keywords here: '}; # SHOULD              ## Ignore the token.
4596              ## TODO: make this configurable              #
4597            }            }
           push @tokens,  
                         {type => 'start tag', tag_name => 'input', attributes => $at},  
                         #{type => 'character', data => ''}, # SHOULD  
                         {type => 'end tag', tag_name => 'label'},  
                         {type => 'end tag', tag_name => 'p'},  
                         {type => 'start tag', tag_name => 'hr'},  
                         {type => 'end tag', tag_name => 'form'};  
           $token = shift @tokens;  
           !!!back-token (@tokens);  
           return;  
         }  
       } elsif ($token->{tag_name} eq 'textarea') {  
         my $tag_name = $token->{tag_name};  
         my $el;  
         !!!create-element ($el, $token->{tag_name}, $token->{attributes});  
           
         ## TODO: $self->{form_element} if defined  
         $self->{content_model} = RCDATA_CONTENT_MODEL;  
         delete $self->{escape}; # MUST  
           
         $insert->($el);  
           
         my $text = '';  
         !!!next-token;  
         if ($token->{type} eq 'character') {  
           $token->{data} =~ s/^\x0A//;  
4598            unless (length $token->{data}) {            unless (length $token->{data}) {
4599                !!!cp ('t88');
4600              !!!next-token;              !!!next-token;
4601                next B;
4602            }            }
4603    ## TODO: set $token->{column} appropriately
4604          }          }
         while ($token->{type} eq 'character') {  
           $text .= $token->{data};  
           !!!next-token;  
         }  
         if (length $text) {  
           $el->manakai_append_text ($text);  
         }  
           
         $self->{content_model} = PCDATA_CONTENT_MODEL;  
           
         if ($token->{type} eq 'end tag' and  
             $token->{tag_name} eq $tag_name) {  
           ## Ignore the token  
         } else {  
           !!!parse-error (type => 'in RCDATA:#'.$token->{type});  
         }  
         !!!next-token;  
         return;  
       } elsif ({  
                 iframe => 1,  
                 noembed => 1,  
                 noframes => 1,  
                 noscript => 0, ## TODO: 1 if scripting is enabled  
                }->{$token->{tag_name}}) {  
         $parse_rcdata->(CDATA_CONTENT_MODEL, $insert);  
         return;  
       } elsif ($token->{tag_name} eq 'select') {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         $self->{insertion_mode} = 'in select';  
         !!!next-token;  
         return;  
       } elsif ({  
                 caption => 1, col => 1, colgroup => 1, frame => 1,  
                 frameset => 1, head => 1, option => 1, optgroup => 1,  
                 tbody => 1, td => 1, tfoot => 1, th => 1,  
                 thead => 1, tr => 1,  
                }->{$token->{tag_name}}) {  
         !!!parse-error (type => 'in body:'.$token->{tag_name});  
         ## Ignore the token  
         !!!next-token;  
         return;  
           
         ## ISSUE: An issue on HTML5 new elements in the spec.  
       } else {  
         $reconstruct_active_formatting_elements->($insert_to_current);  
           
         !!!insert-element-t ($token->{tag_name}, $token->{attributes});  
           
         !!!next-token;  
         return;  
       }  
     } 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]);  
             }  
           }  
4605    
4606            $self->{insertion_mode} = 'after body';          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4607            !!!next-token;            !!!cp ('t89');
4608            return;            ## As if <head>
4609              !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4610              $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4611              push @{$self->{open_elements}},
4612                  [$self->{head_element}, $el_category->{head}];
4613    
4614              ## Reprocess in the "in head" insertion mode...
4615              pop @{$self->{open_elements}};
4616    
4617              ## Reprocess in the "after head" insertion mode...
4618            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4619              !!!cp ('t90');
4620              ## As if </noscript>
4621              pop @{$self->{open_elements}};
4622              !!!parse-error (type => 'in noscript:#text', token => $token);
4623              
4624              ## Reprocess in the "in head" insertion mode...
4625              ## As if </head>
4626              pop @{$self->{open_elements}};
4627    
4628              ## Reprocess in the "after head" insertion mode...
4629            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4630              !!!cp ('t91');
4631              pop @{$self->{open_elements}};
4632    
4633              ## Reprocess in the "after head" insertion mode...
4634          } else {          } else {
4635            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t92');
           ## Ignore the token  
           !!!next-token;  
           return;  
4636          }          }
4637        } elsif ($token->{tag_name} eq 'html') {  
4638          if (@{$self->{open_elements}} > 1 and $self->{open_elements}->[1]->[1] eq 'body') {          ## "after head" insertion mode
4639            ## ISSUE: There is an issue in the spec.          ## As if <body>
4640            if ($self->{open_elements}->[-1]->[1] ne 'body') {          !!!insert-element ('body',, $token);
4641              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[1]->[1]);          $self->{insertion_mode} = IN_BODY_IM;
4642            ## reprocess
4643            next B;
4644          } elsif ($token->{type} == START_TAG_TOKEN) {
4645            if ($token->{tag_name} eq 'head') {
4646              if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4647                !!!cp ('t93');
4648                !!!create-element ($self->{head_element}, $HTML_NS, $token->{tag_name}, $token->{attributes}, $token);
4649                $self->{open_elements}->[-1]->[0]->append_child
4650                    ($self->{head_element});
4651                push @{$self->{open_elements}},
4652                    [$self->{head_element}, $el_category->{head}];
4653                $self->{insertion_mode} = IN_HEAD_IM;
4654                !!!nack ('t93.1');
4655                !!!next-token;
4656                next B;
4657              } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4658                !!!cp ('t93.2');
4659                !!!parse-error (type => 'after head', text => 'head',
4660                                token => $token);
4661                ## Ignore the token
4662                !!!nack ('t93.3');
4663                !!!next-token;
4664                next B;
4665              } else {
4666                !!!cp ('t95');
4667                !!!parse-error (type => 'in head:head',
4668                                token => $token); # or in head noscript
4669                ## Ignore the token
4670                !!!nack ('t95.1');
4671                !!!next-token;
4672                next B;
4673            }            }
4674            $self->{insertion_mode} = 'after body';          } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
4675            ## reprocess            !!!cp ('t96');
4676            return;            ## As if <head>
4677          } else {            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
4678            !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
4679            ## Ignore the token            push @{$self->{open_elements}},
4680            !!!next-token;                [$self->{head_element}, $el_category->{head}];
4681            return;  
4682          }            $self->{insertion_mode} = IN_HEAD_IM;
4683        } elsif ({            ## Reprocess in the "in head" insertion mode...
4684                  address => 1, blockquote => 1, center => 1, dir => 1,          } else {
4685                  div => 1, dl => 1, fieldset => 1, listing => 1,            !!!cp ('t97');
4686                  menu => 1, ol => 1, pre => 1, ul => 1,          }
4687                  p => 1,  
4688                  dd => 1, dt => 1, li => 1,          if ($token->{tag_name} eq 'base') {
4689                  button => 1, marquee => 1, object => 1,            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4690                 }->{$token->{tag_name}}) {              !!!cp ('t98');
4691          ## has an element in scope              ## As if </noscript>
4692          my $i;              pop @{$self->{open_elements}};
4693          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!parse-error (type => 'in noscript', text => 'base',
4694            my $node = $self->{open_elements}->[$_];                              token => $token);
4695            if ($node->[1] eq $token->{tag_name}) {            
4696              ## generate implied end tags              $self->{insertion_mode} = IN_HEAD_IM;
4697              if ({              ## Reprocess in the "in head" insertion mode...
4698                   dd => ($token->{tag_name} ne 'dd'),            } else {
4699                   dt => ($token->{tag_name} ne 'dt'),              !!!cp ('t99');
                  li => ($token->{tag_name} ne 'li'),  
                  p => ($token->{tag_name} ne 'p'),  
                  td => 1, th => 1, tr => 1,  
                  tbody => 1, tfoot=> 1, thead => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               !!!back-token;  
               $token = {type => 'end tag',  
                         tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
               return;  
             }  
             $i = $_;  
             last INSCOPE unless $token->{tag_name} eq 'p';  
           } elsif ({  
                     table => 1, caption => 1, td => 1, th => 1,  
                     button => 1, marquee => 1, object => 1, html => 1,  
                    }->{$node->[1]}) {  
             last INSCOPE;  
4700            }            }
4701          } # INSCOPE  
4702                      ## NOTE: There is a "as if in head" code clone.
4703          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4704            if (defined $i) {              !!!cp ('t100');
4705              !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!parse-error (type => 'after head',
4706                                text => $token->{tag_name}, token => $token);
4707                push @{$self->{open_elements}},
4708                    [$self->{head_element}, $el_category->{head}];
4709                $self->{head_element_inserted} = 1;
4710            } else {            } else {
4711              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t101');
4712            }            }
4713          }            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4714                      pop @{$self->{open_elements}};
4715          if (defined $i) {            pop @{$self->{open_elements}} # <head>
4716            splice @{$self->{open_elements}}, $i;                if $self->{insertion_mode} == AFTER_HEAD_IM;
4717          } elsif ($token->{tag_name} eq 'p') {            !!!nack ('t101.1');
4718            ## As if <p>, then reprocess the current token            !!!next-token;
4719            my $el;            next B;
4720            !!!create-element ($el, 'p');          } elsif ($token->{tag_name} eq 'link') {
4721            $insert->($el);            ## NOTE: There is a "as if in head" code clone.
4722          }            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4723          $clear_up_to_marker->()              !!!cp ('t102');
4724            if {              !!!parse-error (type => 'after head',
4725              button => 1, marquee => 1, object => 1,                              text => $token->{tag_name}, token => $token);
4726            }->{$token->{tag_name}};              push @{$self->{open_elements}},
4727          !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4728          return;              $self->{head_element_inserted} = 1;
4729        } elsif ($token->{tag_name} eq 'form') {            } else {
4730          ## has an element in scope              !!!cp ('t103');
         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;  
4731            }            }
4732          } # INSCOPE            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
           
         if ($self->{open_elements}->[-1]->[1] eq $token->{tag_name}) {  
4733            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
4734          } else {            pop @{$self->{open_elements}} # <head>
4735            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                if $self->{insertion_mode} == AFTER_HEAD_IM;
4736          }            !!!ack ('t103.1');
4737              !!!next-token;
4738              next B;
4739            } elsif ($token->{tag_name} eq 'command' or
4740                     $token->{tag_name} eq 'eventsource') {
4741              if ($self->{insertion_mode} == IN_HEAD_IM) {
4742                ## NOTE: If the insertion mode at the time of the emission
4743                ## of the token was "before head", $self->{insertion_mode}
4744                ## is already changed to |IN_HEAD_IM|.
4745    
4746          undef $self->{form_element};              ## NOTE: There is a "as if in head" code clone.
4747          !!!next-token;              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4748          return;              pop @{$self->{open_elements}};
4749        } elsif ({              pop @{$self->{open_elements}} # <head>
4750                  h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,                  if $self->{insertion_mode} == AFTER_HEAD_IM;
4751                 }->{$token->{tag_name}}) {              !!!ack ('t103.2');
4752          ## has an element in scope              !!!next-token;
4753          my $i;              next B;
4754          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {            } else {
4755            my $node = $self->{open_elements}->[$_];              ## NOTE: "in head noscript" or "after head" insertion mode
4756            if ({              ## - in these cases, these tags are treated as same as
4757                 h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,              ## normal in-body tags.
4758                }->{$node->[1]}) {              !!!cp ('t103.3');
4759              ## 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;  
4760            }            }
4761          } # INSCOPE          } elsif ($token->{tag_name} eq 'meta') {
4762                      ## NOTE: There is a "as if in head" code clone.
4763          if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {            if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4764            !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);              !!!cp ('t104');
4765          }              !!!parse-error (type => 'after head',
4766                                        text => $token->{tag_name}, token => $token);
4767          splice @{$self->{open_elements}}, $i if defined $i;              push @{$self->{open_elements}},
4768          !!!next-token;                  [$self->{head_element}, $el_category->{head}];
4769          return;              $self->{head_element_inserted} = 1;
4770        } elsif ({            } else {
4771                  a => 1,              !!!cp ('t105');
4772                  b => 1, big => 1, em => 1, font => 1, i => 1,            }
4773                  nobr => 1, s => 1, small => 1, strile => 1,            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4774                  strong => 1, tt => 1, u => 1,            my $meta_el = pop @{$self->{open_elements}};
                }->{$token->{tag_name}}) {  
         $formatting_end_tag->($token->{tag_name});  
         return;  
       } elsif ($token->{tag_name} eq 'br') {  
         !!!parse-error (type => 'unmatched end tag:br');  
4775    
4776          ## As if <br>                unless ($self->{confident}) {
4777          $reconstruct_active_formatting_elements->($insert_to_current);                  if ($token->{attributes}->{charset}) {
4778                              !!!cp ('t106');
4779          my $el;                    ## NOTE: Whether the encoding is supported or not is handled
4780          !!!create-element ($el, 'br');                    ## in the {change_encoding} callback.
4781          $insert->($el);                    $self->{change_encoding}
4782                                  ->($self, $token->{attributes}->{charset}->{value},
4783          ## Ignore the token.                           $token);
4784          !!!next-token;                    
4785          return;                    $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4786        } elsif ({                        ->set_user_data (manakai_has_reference =>
4787                  caption => 1, col => 1, colgroup => 1, frame => 1,                                             $token->{attributes}->{charset}
4788                  frameset => 1, head => 1, option => 1, optgroup => 1,                                                 ->{has_reference});
4789                  tbody => 1, td => 1, tfoot => 1, th => 1,                  } elsif ($token->{attributes}->{content}) {
4790                  thead => 1, tr => 1,                    if ($token->{attributes}->{content}->{value}
4791                  area => 1, basefont => 1, bgsound => 1,                        =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
4792                  embed => 1, hr => 1, iframe => 1, image => 1,                            [\x09\x0A\x0C\x0D\x20]*=
4793                  img => 1, input => 1, isindex => 1, noembed => 1,                            [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
4794                  noframes => 1, param => 1, select => 1, spacer => 1,                            ([^"'\x09\x0A\x0C\x0D\x20]
4795                  table => 1, textarea => 1, wbr => 1,                             [^\x09\x0A\x0C\x0D\x20\x3B]*))/x) {
4796                  noscript => 0, ## TODO: if scripting is enabled                      !!!cp ('t107');
4797                 }->{$token->{tag_name}}) {                      ## NOTE: Whether the encoding is supported or not is handled
4798          !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                      ## in the {change_encoding} callback.
4799          ## Ignore the token                      $self->{change_encoding}
4800          !!!next-token;                          ->($self, defined $1 ? $1 : defined $2 ? $2 : $3,
4801          return;                             $token);
4802                                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4803          ## ISSUE: Issue on HTML5 new elements in spec                          ->set_user_data (manakai_has_reference =>
4804                                                         $token->{attributes}->{content}
4805        } else {                                                     ->{has_reference});
4806          ## Step 1                    } else {
4807          my $node_i = -1;                      !!!cp ('t108');
4808          my $node = $self->{open_elements}->[$node_i];                    }
4809                    }
4810                  } else {
4811                    if ($token->{attributes}->{charset}) {
4812                      !!!cp ('t109');
4813                      $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
4814                          ->set_user_data (manakai_has_reference =>
4815                                               $token->{attributes}->{charset}
4816                                                   ->{has_reference});
4817                    }
4818                    if ($token->{attributes}->{content}) {
4819                      !!!cp ('t110');
4820                      $meta_el->[0]->get_attribute_node_ns (undef, 'content')
4821                          ->set_user_data (manakai_has_reference =>
4822                                               $token->{attributes}->{content}
4823                                                   ->{has_reference});
4824                    }
4825                  }
4826    
4827          ## Step 2                pop @{$self->{open_elements}} # <head>
4828          S2: {                    if $self->{insertion_mode} == AFTER_HEAD_IM;
4829            if ($node->[1] eq $token->{tag_name}) {                !!!ack ('t110.1');
4830              ## Step 1                !!!next-token;
4831              ## generate implied end tags                next B;
4832              if ({          } elsif ($token->{tag_name} eq 'title') {
4833                   dd => 1, dt => 1, li => 1, p => 1,            if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4834                   td => 1, th => 1, tr => 1,              !!!cp ('t111');
4835                   tbody => 1, tfoot=> 1, thead => 1,              ## As if </noscript>
4836                  }->{$self->{open_elements}->[-1]->[1]}) {              pop @{$self->{open_elements}};
4837                !!!back-token;              !!!parse-error (type => 'in noscript', text => 'title',
4838                $token = {type => 'end tag',                              token => $token);
4839                          tag_name => $self->{open_elements}->[-1]->[1]}; # MUST            
4840                return;              $self->{insertion_mode} = IN_HEAD_IM;
4841              }              ## Reprocess in the "in head" insertion mode...
4842                      } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4843              ## Step 2              !!!cp ('t112');
4844              if ($token->{tag_name} ne $self->{open_elements}->[-1]->[1]) {              !!!parse-error (type => 'after head',
4845                !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                              text => $token->{tag_name}, token => $token);
4846              }              push @{$self->{open_elements}},
4847                                [$self->{head_element}, $el_category->{head}];
4848              ## Step 3              $self->{head_element_inserted} = 1;
4849              splice @{$self->{open_elements}}, $node_i;            } else {
4850                !!!cp ('t113');
4851              }
4852    
4853              !!!next-token;            ## NOTE: There is a "as if in head" code clone.
4854              last S2;            $parse_rcdata->(RCDATA_CONTENT_MODEL);
4855              ## ISSUE: A spec bug [Bug 6038]
4856              splice @{$self->{open_elements}}, -2, 1, () # <head>
4857                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4858              next B;
4859            } elsif ($token->{tag_name} eq 'style' or
4860                     $token->{tag_name} eq 'noframes') {
4861              ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and
4862              ## insertion mode IN_HEAD_IM)
4863              ## NOTE: There is a "as if in head" code clone.
4864              if ($self->{insertion_mode} == AFTER_HEAD_IM) {
4865                !!!cp ('t114');
4866                !!!parse-error (type => 'after head',
4867                                text => $token->{tag_name}, token => $token);
4868                push @{$self->{open_elements}},
4869                    [$self->{head_element}, $el_category->{head}];
4870                $self->{head_element_inserted} = 1;
4871            } else {            } else {
4872              ## Step 3              !!!cp ('t115');
             if (not $formatting_category->{$node->[1]} and  
                 #not $phrasing_category->{$node->[1]} and  
                 ($special_category->{$node->[1]} or  
                  $scoping_category->{$node->[1]})) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               last S2;  
             }  
4873            }            }
4874              $parse_rcdata->(CDATA_CONTENT_MODEL);
4875              ## ISSUE: A spec bug [Bug 6038]
4876              splice @{$self->{open_elements}}, -2, 1, () # <head>
4877                  if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4878              next B;
4879            } elsif ($token->{tag_name} eq 'noscript') {
4880                  if ($self->{insertion_mode} == IN_HEAD_IM) {
4881                    !!!cp ('t116');
4882                    ## NOTE: and scripting is disalbed
4883                    !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4884                    $self->{insertion_mode} = IN_HEAD_NOSCRIPT_IM;
4885                    !!!nack ('t116.1');
4886                    !!!next-token;
4887                    next B;
4888                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4889                    !!!cp ('t117');
4890                    !!!parse-error (type => 'in noscript', text => 'noscript',
4891                                    token => $token);
4892                    ## Ignore the token
4893                    !!!nack ('t117.1');
4894                    !!!next-token;
4895                    next B;
4896                  } else {
4897                    !!!cp ('t118');
4898                    #
4899                  }
4900            } elsif ($token->{tag_name} eq 'script') {
4901              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4902                !!!cp ('t119');
4903                ## As if </noscript>
4904                pop @{$self->{open_elements}};
4905                !!!parse-error (type => 'in noscript', text => 'script',
4906                                token => $token);
4907                        
4908            ## Step 4              $self->{insertion_mode} = IN_HEAD_IM;
4909            $node_i--;              ## Reprocess in the "in head" insertion mode...
4910            $node = $self->{open_elements}->[$node_i];            } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
4911                          !!!cp ('t120');
4912            ## Step 5;              !!!parse-error (type => 'after head',
4913            redo S2;                              text => $token->{tag_name}, token => $token);
4914          } # S2              push @{$self->{open_elements}},
4915          return;                  [$self->{head_element}, $el_category->{head}];
4916        }              $self->{head_element_inserted} = 1;
4917      }            } else {
4918    }; # $in_body              !!!cp ('t121');
4919              }
   B: {  
     if ($token->{type} eq 'DOCTYPE') {  
       !!!parse-error (type => 'DOCTYPE in the middle');  
       ## Ignore the token  
       ## Stay in the phase  
       !!!next-token;  
       redo B;  
     } elsif ($token->{type} eq 'end-of-file') {  
       if ($token->{insertion_mode} ne 'trailing end') {  
         ## Generate implied end tags  
         if ({  
              dd => 1, dt => 1, li => 1, p => 1, td => 1, th => 1, tr => 1,  
              tbody => 1, tfoot=> 1, thead => 1,  
             }->{$self->{open_elements}->[-1]->[1]}) {  
           !!!back-token;  
           $token = {type => 'end tag', tag_name => $self->{open_elements}->[-1]->[1]};  
           redo B;  
         }  
           
         if (@{$self->{open_elements}} > 2 or  
             (@{$self->{open_elements}} == 2 and $self->{open_elements}->[1]->[1] ne 'body')) {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         } elsif (defined $self->{inner_html_node} and  
                  @{$self->{open_elements}} > 1 and  
                  $self->{open_elements}->[1]->[1] ne 'body') {  
           !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
         }  
   
         ## ISSUE: There is an issue in the spec.  
       }  
4920    
4921        ## Stop parsing            ## NOTE: There is a "as if in head" code clone.
4922        last B;            $script_start_tag->();
4923      } elsif ($token->{type} eq 'start tag' and            ## ISSUE: A spec bug  [Bug 6038]
4924               $token->{tag_name} eq 'html') {            splice @{$self->{open_elements}}, -2, 1 # <head>
4925        if ($self->{insertion_mode} eq 'trailing end') {                if ($self->{insertion_mode} & AFTER_HEAD_IM) == AFTER_HEAD_IM;
4926          ## Turn into the main phase            next B;
4927          !!!parse-error (type => 'after html:html');          } elsif ($token->{tag_name} eq 'body' or
4928          $self->{insertion_mode} = $previous_insertion_mode;                   $token->{tag_name} eq 'frameset') {
4929        }                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4930                    !!!cp ('t122');
4931                    ## As if </noscript>
4932                    pop @{$self->{open_elements}};
4933                    !!!parse-error (type => 'in noscript',
4934                                    text => $token->{tag_name}, token => $token);
4935                    
4936                    ## Reprocess in the "in head" insertion mode...
4937                    ## As if </head>
4938                    pop @{$self->{open_elements}};
4939                    
4940                    ## Reprocess in the "after head" insertion mode...
4941                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4942                    !!!cp ('t124');
4943                    pop @{$self->{open_elements}};
4944                    
4945                    ## Reprocess in the "after head" insertion mode...
4946                  } else {
4947                    !!!cp ('t125');
4948                  }
4949    
4950  ## ISSUE: "aa<html>" is not a parse error.                ## "after head" insertion mode
4951  ## ISSUE: "<html>" in fragment is not a parse error.                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
4952        unless ($token->{first_start_tag}) {                if ($token->{tag_name} eq 'body') {
4953          !!!parse-error (type => 'not first start tag');                  !!!cp ('t126');
4954        }                  $self->{insertion_mode} = IN_BODY_IM;
4955        my $top_el = $self->{open_elements}->[0]->[0];                } elsif ($token->{tag_name} eq 'frameset') {
4956        for my $attr_name (keys %{$token->{attributes}}) {                  !!!cp ('t127');
4957          unless ($top_el->has_attribute_ns (undef, $attr_name)) {                  $self->{insertion_mode} = IN_FRAMESET_IM;
4958            $top_el->set_attribute_ns                } else {
4959              (undef, [undef, $attr_name],                  die "$0: tag name: $self->{tag_name}";
              $token->{attributes}->{$attr_name}->{value});  
         }  
       }  
       !!!next-token;  
       redo B;  
     } elsif ($token->{type} eq 'comment') {  
       my $comment = $self->{document}->create_comment ($token->{data});  
       if ($self->{insertion_mode} eq 'trailing end') {  
         $self->{document}->append_child ($comment);  
       } elsif ($self->{insertion_mode} eq 'after body') {  
         $self->{open_elements}->[0]->[0]->append_child ($comment);  
       } else {  
         $self->{open_elements}->[-1]->[0]->append_child ($comment);  
       }  
       !!!next-token;  
       redo B;  
     } elsif ($self->{insertion_mode} eq 'before head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
4960                }                }
4961              }                !!!nack ('t127.1');
             ## 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 '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') {  
4962                !!!next-token;                !!!next-token;
4963              #} elsif ({                next B;
             #          base => 1, link => 1, meta => 1,  
             #          script => 1, style => 1, title => 1,  
             #         }->{$token->{tag_name}}) {  
             #  ## reprocess  
4964              } else {              } else {
4965                ## reprocess                !!!cp ('t128');
4966                  #
4967              }              }
4968              redo B;  
4969            } elsif ($token->{type} eq 'end tag') {              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
4970              if ({                !!!cp ('t129');
4971                   head => 1, body => 1, html => 1,                ## As if </noscript>
4972                   p => 1, br => 1,                pop @{$self->{open_elements}};
4973                  }->{$token->{tag_name}}) {                !!!parse-error (type => 'in noscript:/',
4974                ## As if <head>                                text => $token->{tag_name}, token => $token);
4975                !!!create-element ($self->{head_element}, 'head');                
4976                $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});                ## Reprocess in the "in head" insertion mode...
4977                push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                ## As if </head>
4978                $self->{insertion_mode} = 'in head';                pop @{$self->{open_elements}};
4979                ## reprocess  
4980                redo B;                ## Reprocess in the "after head" insertion mode...
4981                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
4982                  !!!cp ('t130');
4983                  ## As if </head>
4984                  pop @{$self->{open_elements}};
4985    
4986                  ## Reprocess in the "after head" insertion mode...
4987              } else {              } else {
4988                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t131');
               ## Ignore the token ## ISSUE: An issue in the spec.  
               !!!next-token;  
               redo B;  
4989              }              }
           } else {  
             die "$0: $token->{type}: Unknown type";  
           }  
         } elsif ($self->{insertion_mode} eq 'in head' or  
                  $self->{insertion_mode} eq 'in head noscript' or  
                  $self->{insertion_mode} eq 'after head') {  
           if ($token->{type} eq 'character') {  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
               }  
             }  
               
             #  
           } elsif ($token->{type} eq 'start tag') {  
             if ({base => ($self->{insertion_mode} eq 'in head' or  
                           $self->{insertion_mode} eq 'after head'),  
                  link => 1}->{$token->{tag_name}}) {  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} eq 'after head') {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               }  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
               pop @{$self->{open_elements}}  
                   if $self->{insertion_mode} eq 'after head';  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'meta') {  
               ## NOTE: There is a "as if in head" code clone.  
               if ($self->{insertion_mode} eq 'after head') {  
                 !!!parse-error (type => 'after head:'.$token->{tag_name});  
                 push @{$self->{open_elements}}, [$self->{head_element}, 'head'];  
               }  
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               pop @{$self->{open_elements}}; ## ISSUE: This step is missing in the spec.  
4990    
4991                unless ($self->{confident}) {              ## "after head" insertion mode
4992                  my $charset;              ## As if <body>
4993                  if ($token->{attributes}->{charset}) { ## TODO: And if supported              !!!insert-element ('body',, $token);
4994                    $charset = $token->{attributes}->{charset}->{value};              $self->{insertion_mode} = IN_BODY_IM;
4995                  }              ## reprocess
4996                  if ($token->{attributes}->{'http-equiv'}) {              !!!ack-later;
4997                    ## ISSUE: Algorithm name in the spec was incorrect so that not linked to the definition.              next B;
4998                    if ($token->{attributes}->{'http-equiv'}->{value}            } elsif ($token->{type} == END_TAG_TOKEN) {
4999                        =~ /\A[^;]*;[\x09-\x0D\x20]*charset[\x09-\x0D\x20]*=              if ($token->{tag_name} eq 'head') {
5000                            [\x09-\x0D\x20]*(?>"([^"]*)"|'([^']*)'|                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5001                            ([^"'\x09-\x0D\x20][^\x09-\x0D\x20]*))/x) {                  !!!cp ('t132');
5002                      $charset = defined $1 ? $1 : defined $2 ? $2 : $3;                  ## As if <head>
5003                    } ## TODO: And if supported                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5004                  }                  $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5005                  ## TODO: Change the encoding                  push @{$self->{open_elements}},
5006                }                      [$self->{head_element}, $el_category->{head}];
5007    
5008                ## TODO: Extracting |charset| from |meta|.                  ## Reprocess in the "in head" insertion mode...
5009                pop @{$self->{open_elements}}                  pop @{$self->{open_elements}};
5010                    if $self->{insertion_mode} eq 'after head';                  $self->{insertion_mode} = AFTER_HEAD_IM;
5011                !!!next-token;                  !!!next-token;
5012                redo B;                  next B;
5013              } elsif ($token->{tag_name} eq 'title' and                } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5014                       $self->{insertion_mode} eq 'in head') {                  !!!cp ('t133');
5015                ## NOTE: There is a "as if in head" code clone.                  ## As if </noscript>
5016                if ($self->{insertion_mode} eq 'after head') {                  pop @{$self->{open_elements}};
5017                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!parse-error (type => 'in noscript:/',
5018                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                                  text => 'head', token => $token);
5019                }                  
5020                my $parent = defined $self->{head_element} ? $self->{head_element}                  ## Reprocess in the "in head" insertion mode...
5021                    : $self->{open_elements}->[-1]->[0];                  pop @{$self->{open_elements}};
5022                $parse_rcdata->(RCDATA_CONTENT_MODEL,                  $self->{insertion_mode} = AFTER_HEAD_IM;
5023                                sub { $parent->append_child ($_[0]) });                  !!!next-token;
5024                pop @{$self->{open_elements}}                  next B;
5025                    if $self->{insertion_mode} eq 'after head';                } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5026                redo B;                  !!!cp ('t134');
5027              } elsif ($token->{tag_name} eq 'style') {                  pop @{$self->{open_elements}};
5028                ## NOTE: Or (scripting is enabled and tag_name eq 'noscript' and                  $self->{insertion_mode} = AFTER_HEAD_IM;
5029                ## insertion mode 'in head')                  !!!next-token;
5030                ## NOTE: There is a "as if in head" code clone.                  next B;
5031                if ($self->{insertion_mode} eq 'after head') {                } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5032                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  !!!cp ('t134.1');
5033                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!parse-error (type => 'unmatched end tag', text => 'head',
5034                                    token => $token);
5035                    ## Ignore the token
5036                    !!!next-token;
5037                    next B;
5038                  } else {
5039                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5040                }                }
               $parse_rcdata->(CDATA_CONTENT_MODEL, $insert_to_current);  
               pop @{$self->{open_elements}}  
                   if $self->{insertion_mode} eq 'after head';  
               redo B;  
5041              } elsif ($token->{tag_name} eq 'noscript') {              } elsif ($token->{tag_name} eq 'noscript') {
5042                if ($self->{insertion_mode} eq 'in head') {                if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5043                  ## NOTE: and scripting is disalbed                  !!!cp ('t136');
5044                  !!!insert-element ($token->{tag_name}, $token->{attributes});                  pop @{$self->{open_elements}};
5045                  $self->{insertion_mode} = 'in head noscript';                  $self->{insertion_mode} = IN_HEAD_IM;
5046                  !!!next-token;                  !!!next-token;
5047                  redo B;                  next B;
5048                } elsif ($self->{insertion_mode} eq 'in head noscript') {                } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM or
5049                  !!!parse-error (type => 'in noscript:noscript');                         $self->{insertion_mode} == AFTER_HEAD_IM) {
5050                  ## Ignore the token                  !!!cp ('t137');
5051                    !!!parse-error (type => 'unmatched end tag',
5052                                    text => 'noscript', token => $token);
5053                    ## Ignore the token ## ISSUE: An issue in the spec.
5054                  !!!next-token;                  !!!next-token;
5055                  redo B;                  next B;
5056                } else {                } else {
5057                    !!!cp ('t138');
5058                  #                  #
5059                }                }
5060              } elsif ($token->{tag_name} eq 'head' and              } elsif ({
5061                       $self->{insertion_mode} ne 'after head') {                        body => 1, html => 1,
5062                !!!parse-error (type => 'in head:head'); # or in head noscript                       }->{$token->{tag_name}}) {
5063                  ## TODO: This branch is entirely redundant.
5064                  if ($self->{insertion_mode} == BEFORE_HEAD_IM or
5065                      $self->{insertion_mode} == IN_HEAD_IM or
5066                      $self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5067                    !!!cp ('t140');
5068                    !!!parse-error (type => 'unmatched end tag',
5069                                    text => $token->{tag_name}, token => $token);
5070                    ## Ignore the token
5071                    !!!next-token;
5072                    next B;
5073                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5074                    !!!cp ('t140.1');
5075                    !!!parse-error (type => 'unmatched end tag',
5076                                    text => $token->{tag_name}, token => $token);
5077                    ## Ignore the token
5078                    !!!next-token;
5079                    next B;
5080                  } else {
5081                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5082                  }
5083                } elsif ($token->{tag_name} eq 'p') {
5084                  !!!cp ('t142');
5085                  !!!parse-error (type => 'unmatched end tag',
5086                                  text => $token->{tag_name}, token => $token);
5087                ## Ignore the token                ## Ignore the token
5088                !!!next-token;                !!!next-token;
5089                redo B;                next B;
5090              } elsif ($self->{insertion_mode} ne 'in head noscript' and              } elsif ($token->{tag_name} eq 'br') {
5091                       $token->{tag_name} eq 'script') {                if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5092                if ($self->{insertion_mode} eq 'after head') {                  !!!cp ('t142.2');
5093                  !!!parse-error (type => 'after head:'.$token->{tag_name});                  ## (before head) as if <head>, (in head) as if </head>
5094                  push @{$self->{open_elements}}, [$self->{head_element}, 'head'];                  !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5095                    $self->{open_elements}->[-1]->[0]->append_child ($self->{head_element});
5096                    $self->{insertion_mode} = AFTER_HEAD_IM;
5097      
5098                    ## Reprocess in the "after head" insertion mode...
5099                  } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5100                    !!!cp ('t143.2');
5101                    ## As if </head>
5102                    pop @{$self->{open_elements}};
5103                    $self->{insertion_mode} = AFTER_HEAD_IM;
5104      
5105                    ## Reprocess in the "after head" insertion mode...
5106                  } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5107                    !!!cp ('t143.3');
5108                    ## ISSUE: Two parse errors for <head><noscript></br>
5109                    !!!parse-error (type => 'unmatched end tag',
5110                                    text => 'br', token => $token);
5111                    ## As if </noscript>
5112                    pop @{$self->{open_elements}};
5113                    $self->{insertion_mode} = IN_HEAD_IM;
5114    
5115                    ## Reprocess in the "in head" insertion mode...
5116                    ## As if </head>
5117                    pop @{$self->{open_elements}};
5118                    $self->{insertion_mode} = AFTER_HEAD_IM;
5119    
5120                    ## Reprocess in the "after head" insertion mode...
5121                  } elsif ($self->{insertion_mode} == AFTER_HEAD_IM) {
5122                    !!!cp ('t143.4');
5123                    #
5124                  } else {
5125                    die "$0: $self->{insertion_mode}: Unknown insertion mode";
5126                }                }
5127                ## NOTE: There is a "as if in head" code clone.  
5128                $script_start_tag->($insert_to_current);                ## ISSUE: does not agree with IE7 - it doesn't ignore </br>.
5129                pop @{$self->{open_elements}}                !!!parse-error (type => 'unmatched end tag',
5130                    if $self->{insertion_mode} eq 'after head';                                text => 'br', token => $token);
5131                redo B;                ## Ignore the token
             } elsif ($self->{insertion_mode} eq 'after head' and  
                      $token->{tag_name} eq 'body') {  
               !!!insert-element ('body', $token->{attributes});  
               $self->{insertion_mode} = 'in body';  
               !!!next-token;  
               redo B;  
             } elsif ($self->{insertion_mode} eq 'after head' and  
                      $token->{tag_name} eq 'frameset') {  
               !!!insert-element ('frameset', $token->{attributes});  
               $self->{insertion_mode} = 'in frameset';  
5132                !!!next-token;                !!!next-token;
5133                redo B;                next B;
5134              } else {              } else {
5135                #                !!!cp ('t145');
5136                  !!!parse-error (type => 'unmatched end tag',
5137                                  text => $token->{tag_name}, token => $token);
5138                  ## Ignore the token
5139                  !!!next-token;
5140                  next B;
5141              }              }
5142            } elsif ($token->{type} eq 'end tag') {  
5143              if ($self->{insertion_mode} eq 'in head' and              if ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5144                  $token->{tag_name} eq 'head') {                !!!cp ('t146');
5145                  ## As if </noscript>
5146                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5147                $self->{insertion_mode} = 'after head';                !!!parse-error (type => 'in noscript:/',
5148                !!!next-token;                                text => $token->{tag_name}, token => $token);
5149                redo B;                
5150              } elsif ($self->{insertion_mode} eq 'in head noscript' and                ## Reprocess in the "in head" insertion mode...
5151                  $token->{tag_name} eq 'noscript') {                ## As if </head>
5152                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5153                $self->{insertion_mode} = 'in head';  
5154                !!!next-token;                ## Reprocess in the "after head" insertion mode...
5155                redo B;              } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5156              } elsif ($self->{insertion_mode} eq 'in head' and                !!!cp ('t147');
5157                       {                ## As if </head>
5158                        body => 1, html => 1,                pop @{$self->{open_elements}};
5159                        p => 1, br => 1,  
5160                       }->{$token->{tag_name}}) {                ## Reprocess in the "after head" insertion mode...
5161                #              } elsif ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5162              } elsif ($self->{insertion_mode} eq 'in head noscript' and  ## ISSUE: This case cannot be reached?
5163                       {                !!!cp ('t148');
5164                        p => 1, br => 1,                !!!parse-error (type => 'unmatched end tag',
5165                       }->{$token->{tag_name}}) {                                text => $token->{tag_name}, token => $token);
5166                #                ## Ignore the token ## ISSUE: An issue in the spec.
             } elsif ($self->{insertion_mode} ne 'after head') {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
5167                !!!next-token;                !!!next-token;
5168                redo B;                next B;
5169              } else {              } else {
5170                #                !!!cp ('t149');
5171              }              }
           } else {  
             #  
           }  
5172    
5173            ## As if </head> or </noscript> or <body>              ## "after head" insertion mode
5174            if ($self->{insertion_mode} eq 'in head') {              ## As if <body>
5175              pop @{$self->{open_elements}};              !!!insert-element ('body',, $token);
5176              $self->{insertion_mode} = 'after head';              $self->{insertion_mode} = IN_BODY_IM;
5177            } elsif ($self->{insertion_mode} eq 'in head noscript') {              ## reprocess
5178              pop @{$self->{open_elements}};              next B;
5179              !!!parse-error (type => 'in noscript:'.(defined $token->{tag_name} ? ($token->{type} eq 'end tag' ? '/' : '') . $token->{tag_name} : '#' . $token->{type}));        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5180              $self->{insertion_mode} = 'in head';          if ($self->{insertion_mode} == BEFORE_HEAD_IM) {
5181            } else { # 'after head'            !!!cp ('t149.1');
5182              !!!insert-element ('body');  
5183              $self->{insertion_mode} = 'in body';            ## NOTE: As if <head>
5184            }            !!!create-element ($self->{head_element}, $HTML_NS, 'head',, $token);
5185            ## reprocess            $self->{open_elements}->[-1]->[0]->append_child
5186            redo B;                ($self->{head_element});
5187              #push @{$self->{open_elements}},
5188              #    [$self->{head_element}, $el_category->{head}];
5189              #$self->{insertion_mode} = IN_HEAD_IM;
5190              ## NOTE: Reprocess.
5191    
5192              ## NOTE: As if </head>
5193              #pop @{$self->{open_elements}};
5194              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5195              ## NOTE: Reprocess.
5196              
5197              #
5198            } elsif ($self->{insertion_mode} == IN_HEAD_IM) {
5199              !!!cp ('t149.2');
5200    
5201              ## NOTE: As if </head>
5202              pop @{$self->{open_elements}};
5203              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5204              ## NOTE: Reprocess.
5205    
5206              #
5207            } elsif ($self->{insertion_mode} == IN_HEAD_NOSCRIPT_IM) {
5208              !!!cp ('t149.3');
5209    
5210              !!!parse-error (type => 'in noscript:#eof', token => $token);
5211    
5212              ## As if </noscript>
5213              pop @{$self->{open_elements}};
5214              #$self->{insertion_mode} = IN_HEAD_IM;
5215              ## NOTE: Reprocess.
5216    
5217              ## NOTE: As if </head>
5218              pop @{$self->{open_elements}};
5219              #$self->{insertion_mode} = IN_AFTER_HEAD_IM;
5220              ## NOTE: Reprocess.
5221    
5222              #
5223            } else {
5224              !!!cp ('t149.4');
5225              #
5226            }
5227    
5228            ## ISSUE: An issue in the spec.          ## NOTE: As if <body>
5229          } elsif ($self->{insertion_mode} eq 'in body' or          !!!insert-element ('body',, $token);
5230                   $self->{insertion_mode} eq 'in caption') {          $self->{insertion_mode} = IN_BODY_IM;
5231            if ($token->{type} eq 'character') {          ## NOTE: Reprocess.
5232            next B;
5233          } else {
5234            die "$0: $token->{type}: Unknown token type";
5235          }
5236        } elsif ($self->{insertion_mode} & BODY_IMS) {
5237              if ($token->{type} == CHARACTER_TOKEN) {
5238                !!!cp ('t150');
5239              ## NOTE: There is a code clone of "character in body".              ## NOTE: There is a code clone of "character in body".
5240              $reconstruct_active_formatting_elements->($insert_to_current);              $reconstruct_active_formatting_elements->($insert_to_current);
5241                            
5242              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
5243    
5244              !!!next-token;              !!!next-token;
5245              redo B;              next B;
5246            } elsif ($token->{type} eq 'start tag') {            } elsif ($token->{type} == START_TAG_TOKEN) {
5247              if ({              if ({
5248                   caption => 1, col => 1, colgroup => 1, tbody => 1,                   caption => 1, col => 1, colgroup => 1, tbody => 1,
5249                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,                   td => 1, tfoot => 1, th => 1, thead => 1, tr => 1,
5250                  }->{$token->{tag_name}} and                  }->{$token->{tag_name}}) {
5251                  $self->{insertion_mode} eq 'in caption') {                if ($self->{insertion_mode} == IN_CELL_IM) {
5252                !!!parse-error (type => 'not closed:caption');                  ## have an element in table scope
5253                    for (reverse 0..$#{$self->{open_elements}}) {
5254                ## As if </caption>                    my $node = $self->{open_elements}->[$_];
5255                ## have a table element in table scope                    if ($node->[1] == TABLE_CELL_EL) {
5256                my $i;                      !!!cp ('t151');
5257                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
5258                  my $node = $self->{open_elements}->[$_];                      ## Close the cell
5259                  if ($node->[1] eq 'caption') {                      !!!back-token; # <x>
5260                    $i = $_;                      $token = {type => END_TAG_TOKEN,
5261                    last INSCOPE;                                tag_name => $node->[0]->manakai_local_name,
5262                  } elsif ({                                line => $token->{line},
5263                            table => 1, html => 1,                                column => $token->{column}};
5264                           }->{$node->[1]}) {                      next B;
5265                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5266                        !!!cp ('t152');
5267                        ## ISSUE: This case can never be reached, maybe.
5268                        last;
5269                      }
5270                  }                  }
5271                } # INSCOPE  
5272                unless (defined $i) {                  !!!cp ('t153');
5273                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!parse-error (type => 'start tag not allowed',
5274                        text => $token->{tag_name}, token => $token);
5275                  ## Ignore the token                  ## Ignore the token
5276                    !!!nack ('t153.1');
5277                  !!!next-token;                  !!!next-token;
5278                  redo B;                  next B;
5279                }                } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5280                                  !!!parse-error (type => 'not closed', text => 'caption',
5281                ## generate implied end tags                                  token => $token);
5282                if ({                  
5283                     dd => 1, dt => 1, li => 1, p => 1,                  ## NOTE: As if </caption>.
5284                     td => 1, th => 1, tr => 1,                  ## have a table element in table scope
5285                     tbody => 1, tfoot=> 1, thead => 1,                  my $i;
5286                    }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: {
5287                  !!!back-token; # <?>                    for (reverse 0..$#{$self->{open_elements}}) {
5288                  $token = {type => 'end tag', tag_name => 'caption'};                      my $node = $self->{open_elements}->[$_];
5289                  !!!back-token;                      if ($node->[1] == CAPTION_EL) {
5290                  $token = {type => 'end tag',                        !!!cp ('t155');
5291                            tag_name => $self->{open_elements}->[-1]->[1]}; # MUST                        $i = $_;
5292                  redo B;                        last INSCOPE;
5293                }                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5294                          !!!cp ('t156');
5295                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                        last;
5296                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      }
5297                }                    }
   
               splice @{$self->{open_elements}}, $i;  
   
               $clear_up_to_marker->();  
5298    
5299                $self->{insertion_mode} = 'in table';                    !!!cp ('t157');
5300                      !!!parse-error (type => 'start tag not allowed',
5301                                      text => $token->{tag_name}, token => $token);
5302                      ## Ignore the token
5303                      !!!nack ('t157.1');
5304                      !!!next-token;
5305                      next B;
5306                    } # INSCOPE
5307                    
5308                    ## generate implied end tags
5309                    while ($self->{open_elements}->[-1]->[1]
5310                               & END_TAG_OPTIONAL_EL) {
5311                      !!!cp ('t158');
5312                      pop @{$self->{open_elements}};
5313                    }
5314    
5315                ## reprocess                  unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5316                redo B;                    !!!cp ('t159');
5317                      !!!parse-error (type => 'not closed',
5318                                      text => $self->{open_elements}->[-1]->[0]
5319                                          ->manakai_local_name,
5320                                      token => $token);
5321                    } else {
5322                      !!!cp ('t160');
5323                    }
5324                    
5325                    splice @{$self->{open_elements}}, $i;
5326                    
5327                    $clear_up_to_marker->();
5328                    
5329                    $self->{insertion_mode} = IN_TABLE_IM;
5330                    
5331                    ## reprocess
5332                    !!!ack-later;
5333                    next B;
5334                  } else {
5335                    !!!cp ('t161');
5336                    #
5337                  }
5338              } else {              } else {
5339                  !!!cp ('t162');
5340                #                #
5341              }              }
5342            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
5343              if ($token->{tag_name} eq 'caption' and              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {
5344                  $self->{insertion_mode} eq 'in caption') {                if ($self->{insertion_mode} == IN_CELL_IM) {
5345                ## have a table element in table scope                  ## have an element in table scope
5346                my $i;                  my $i;
5347                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5348                  my $node = $self->{open_elements}->[$_];                    my $node = $self->{open_elements}->[$_];
5349                  if ($node->[1] eq $token->{tag_name}) {                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5350                    $i = $_;                      !!!cp ('t163');
5351                    last INSCOPE;                      $i = $_;
5352                  } elsif ({                      last INSCOPE;
5353                            table => 1, html => 1,                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5354                           }->{$node->[1]}) {                      !!!cp ('t164');
5355                    last INSCOPE;                      last INSCOPE;
5356                      }
5357                    } # INSCOPE
5358                      unless (defined $i) {
5359                        !!!cp ('t165');
5360                        !!!parse-error (type => 'unmatched end tag',
5361                                        text => $token->{tag_name},
5362                                        token => $token);
5363                        ## Ignore the token
5364                        !!!next-token;
5365                        next B;
5366                      }
5367                    
5368                    ## generate implied end tags
5369                    while ($self->{open_elements}->[-1]->[1]
5370                               & END_TAG_OPTIONAL_EL) {
5371                      !!!cp ('t166');
5372                      pop @{$self->{open_elements}};
5373                  }                  }
5374                } # INSCOPE  
5375                unless (defined $i) {                  if ($self->{open_elements}->[-1]->[0]->manakai_local_name
5376                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                          ne $token->{tag_name}) {
5377                      !!!cp ('t167');
5378                      !!!parse-error (type => 'not closed',
5379                                      text => $self->{open_elements}->[-1]->[0]
5380                                          ->manakai_local_name,
5381                                      token => $token);
5382                    } else {
5383                      !!!cp ('t168');
5384                    }
5385                    
5386                    splice @{$self->{open_elements}}, $i;
5387                    
5388                    $clear_up_to_marker->();
5389                    
5390                    $self->{insertion_mode} = IN_ROW_IM;
5391                    
5392                    !!!next-token;
5393                    next B;
5394                  } elsif ($self->{insertion_mode} == IN_CAPTION_IM) {
5395                    !!!cp ('t169');
5396                    !!!parse-error (type => 'unmatched end tag',
5397                                    text => $token->{tag_name}, token => $token);
5398                  ## Ignore the token                  ## Ignore the token
5399                  !!!next-token;                  !!!next-token;
5400                  redo B;                  next B;
5401                }                } else {
5402                                  !!!cp ('t170');
5403                ## 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;  
5404                }                }
5405                } elsif ($token->{tag_name} eq 'caption') {
5406                  if ($self->{insertion_mode} == IN_CAPTION_IM) {
5407                    ## have a table element in table scope
5408                    my $i;
5409                    INSCOPE: {
5410                      for (reverse 0..$#{$self->{open_elements}}) {
5411                        my $node = $self->{open_elements}->[$_];
5412                        if ($node->[1] == CAPTION_EL) {
5413                          !!!cp ('t171');
5414                          $i = $_;
5415                          last INSCOPE;
5416                        } elsif ($node->[1] & TABLE_SCOPING_EL) {
5417                          !!!cp ('t172');
5418                          last;
5419                        }
5420                      }
5421    
5422                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                    !!!cp ('t173');
5423                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    !!!parse-error (type => 'unmatched end tag',
5424                                      text => $token->{tag_name}, token => $token);
5425                      ## Ignore the token
5426                      !!!next-token;
5427                      next B;
5428                    } # INSCOPE
5429                    
5430                    ## generate implied end tags
5431                    while ($self->{open_elements}->[-1]->[1]
5432                               & END_TAG_OPTIONAL_EL) {
5433                      !!!cp ('t174');
5434                      pop @{$self->{open_elements}};
5435                    }
5436                    
5437                    unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5438                      !!!cp ('t175');
5439                      !!!parse-error (type => 'not closed',
5440                                      text => $self->{open_elements}->[-1]->[0]
5441                                          ->manakai_local_name,
5442                                      token => $token);
5443                    } else {
5444                      !!!cp ('t176');
5445                    }
5446                    
5447                    splice @{$self->{open_elements}}, $i;
5448                    
5449                    $clear_up_to_marker->();
5450                    
5451                    $self->{insertion_mode} = IN_TABLE_IM;
5452                    
5453                    !!!next-token;
5454                    next B;
5455                  } elsif ($self->{insertion_mode} == IN_CELL_IM) {
5456                    !!!cp ('t177');
5457                    !!!parse-error (type => 'unmatched end tag',
5458                                    text => $token->{tag_name}, token => $token);
5459                    ## Ignore the token
5460                    !!!next-token;
5461                    next B;
5462                  } else {
5463                    !!!cp ('t178');
5464                    #
5465                }                }
5466                } elsif ({
5467                          table => 1, tbody => 1, tfoot => 1,
5468                          thead => 1, tr => 1,
5469                         }->{$token->{tag_name}} and
5470                         $self->{insertion_mode} == IN_CELL_IM) {
5471                  ## have an element in table scope
5472                  my $i;
5473                  my $tn;
5474                  INSCOPE: {
5475                    for (reverse 0..$#{$self->{open_elements}}) {
5476                      my $node = $self->{open_elements}->[$_];
5477                      if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
5478                        !!!cp ('t179');
5479                        $i = $_;
5480    
5481                        ## Close the cell
5482                        !!!back-token; # </x>
5483                        $token = {type => END_TAG_TOKEN, tag_name => $tn,
5484                                  line => $token->{line},
5485                                  column => $token->{column}};
5486                        next B;
5487                      } elsif ($node->[1] == TABLE_CELL_EL) {
5488                        !!!cp ('t180');
5489                        $tn = $node->[0]->manakai_local_name;
5490                        ## NOTE: There is exactly one |td| or |th| element
5491                        ## in scope in the stack of open elements by definition.
5492                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
5493                        ## ISSUE: Can this be reached?
5494                        !!!cp ('t181');
5495                        last;
5496                      }
5497                    }
5498    
5499                splice @{$self->{open_elements}}, $i;                  !!!cp ('t182');
5500                    !!!parse-error (type => 'unmatched end tag',
5501                $clear_up_to_marker->();                      text => $token->{tag_name}, token => $token);
5502                    ## Ignore the token
5503                $self->{insertion_mode} = 'in table';                  !!!next-token;
5504                    next B;
5505                !!!next-token;                } # INSCOPE
               redo B;  
5506              } elsif ($token->{tag_name} eq 'table' and              } elsif ($token->{tag_name} eq 'table' and
5507                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5508                !!!parse-error (type => 'not closed:caption');                !!!parse-error (type => 'not closed', text => 'caption',
5509                                  token => $token);
5510    
5511                ## As if </caption>                ## As if </caption>
5512                ## have a table element in table scope                ## have a table element in table scope
5513                my $i;                my $i;
5514                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5515                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5516                  if ($node->[1] eq 'caption') {                  if ($node->[1] == CAPTION_EL) {
5517                      !!!cp ('t184');
5518                    $i = $_;                    $i = $_;
5519                    last INSCOPE;                    last INSCOPE;
5520                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5521                            table => 1, html => 1,                    !!!cp ('t185');
                          }->{$node->[1]}) {  
5522                    last INSCOPE;                    last INSCOPE;
5523                  }                  }
5524                } # INSCOPE                } # INSCOPE
5525                unless (defined $i) {                unless (defined $i) {
5526                  !!!parse-error (type => 'unmatched end tag:caption');                  !!!cp ('t186');
5527                    !!!parse-error (type => 'unmatched end tag',
5528                                    text => 'caption', token => $token);
5529                  ## Ignore the token                  ## Ignore the token
5530                  !!!next-token;                  !!!next-token;
5531                  redo B;                  next B;
5532                }                }
5533                                
5534                ## generate implied end tags                ## generate implied end tags
5535                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5536                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t187');
5537                     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;  
5538                }                }
5539    
5540                if ($self->{open_elements}->[-1]->[1] ne 'caption') {                unless ($self->{open_elements}->[-1]->[1] == CAPTION_EL) {
5541                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t188');
5542                    !!!parse-error (type => 'not closed',
5543                                    text => $self->{open_elements}->[-1]->[0]
5544                                        ->manakai_local_name,
5545                                    token => $token);
5546                  } else {
5547                    !!!cp ('t189');
5548                }                }
5549    
5550                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5551    
5552                $clear_up_to_marker->();                $clear_up_to_marker->();
5553    
5554                $self->{insertion_mode} = 'in table';                $self->{insertion_mode} = IN_TABLE_IM;
5555    
5556                ## reprocess                ## reprocess
5557                redo B;                next B;
5558              } elsif ({              } elsif ({
5559                        body => 1, col => 1, colgroup => 1,                        body => 1, col => 1, colgroup => 1, html => 1,
5560                        html => 1, tbody => 1, td => 1, tfoot => 1,                       }->{$token->{tag_name}}) {
5561                        th => 1, thead => 1, tr => 1,                if ($self->{insertion_mode} & BODY_TABLE_IMS) {
5562                    !!!cp ('t190');
5563                    !!!parse-error (type => 'unmatched end tag',
5564                                    text => $token->{tag_name}, token => $token);
5565                    ## Ignore the token
5566                    !!!next-token;
5567                    next B;
5568                  } else {
5569                    !!!cp ('t191');
5570                    #
5571                  }
5572                } elsif ({
5573                          tbody => 1, tfoot => 1,
5574                          thead => 1, tr => 1,
5575                       }->{$token->{tag_name}} and                       }->{$token->{tag_name}} and
5576                       $self->{insertion_mode} eq 'in caption') {                       $self->{insertion_mode} == IN_CAPTION_IM) {
5577                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                !!!cp ('t192');
5578                  !!!parse-error (type => 'unmatched end tag',
5579                                  text => $token->{tag_name}, token => $token);
5580                ## Ignore the token                ## Ignore the token
5581                !!!next-token;                !!!next-token;
5582                redo B;                next B;
5583              } else {              } else {
5584                  !!!cp ('t193');
5585                #                #
5586              }              }
5587            } else {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
5588              #          for my $entry (@{$self->{open_elements}}) {
5589              unless ($entry->[1] & ALL_END_TAG_OPTIONAL_EL) {
5590                !!!cp ('t75');
5591                !!!parse-error (type => 'in body:#eof', token => $token);
5592                last;
5593            }            }
5594            }
5595    
5596            $in_body->($insert_to_current);          ## Stop parsing.
5597            redo B;          last B;
5598          } elsif ($self->{insertion_mode} eq 'in table') {        } else {
5599            if ($token->{type} eq 'character') {          die "$0: $token->{type}: Unknown token type";
5600              ## NOTE: There are "character in table" code clones.        }
5601              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
5602                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);        $insert = $insert_to_current;
5603          #
5604        } elsif ($self->{insertion_mode} & TABLE_IMS) {
5605          if ($token->{type} == CHARACTER_TOKEN) {
5606            if (not $open_tables->[-1]->[1] and # tainted
5607                $token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
5608              $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
5609                                
5610                unless (length $token->{data}) {            unless (length $token->{data}) {
5611                  !!!next-token;              !!!cp ('t194');
5612                  redo B;              !!!next-token;
5613                }              next B;
5614              }            } else {
5615                !!!cp ('t195');
5616              }
5617            }
5618    
5619              !!!parse-error (type => 'in table:#character');          !!!parse-error (type => 'in table:#text', token => $token);
5620    
5621              ## As if in body, but insert into foster parent element          ## NOTE: As if in body, but insert into the foster parent element.
5622              ## ISSUE: Spec says that "whenever a node would be inserted          $reconstruct_active_formatting_elements->($insert_to_foster);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5623                            
5624              if ({          if ($self->{open_elements}->[-1]->[1] & TABLE_ROWS_EL) {
5625                   table => 1, tbody => 1, tfoot => 1,            # MUST
5626                   thead => 1, tr => 1,            my $foster_parent_element;
5627                  }->{$self->{open_elements}->[-1]->[1]}) {            my $next_sibling;
5628                # MUST            my $prev_sibling;
5629                my $foster_parent_element;            OE: for (reverse 0..$#{$self->{open_elements}}) {
5630                my $next_sibling;              if ($self->{open_elements}->[$_]->[1] == TABLE_EL) {
5631                my $prev_sibling;                my $parent = $self->{open_elements}->[$_]->[0]->parent_node;
5632                OE: for (reverse 0..$#{$self->{open_elements}}) {                if (defined $parent and $parent->node_type == 1) {
5633                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                  $foster_parent_element = $parent;
5634                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;                  !!!cp ('t196');
5635                    if (defined $parent and $parent->node_type == 1) {                  $next_sibling = $self->{open_elements}->[$_]->[0];
5636                      $foster_parent_element = $parent;                  $prev_sibling = $next_sibling->previous_sibling;
5637                      $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});  
5638                } else {                } else {
5639                  $foster_parent_element->insert_before                  !!!cp ('t197');
5640                    ($self->{document}->create_text_node ($token->{data}),                  $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];
5641                     $next_sibling);                  $prev_sibling = $foster_parent_element->last_child;
5642                }                  #
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!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]);  
                 pop @{$self->{open_elements}};  
               }  
   
               push @$active_formatting_elements, ['#marker', '']  
                 if $token->{tag_name} eq 'caption';  
   
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = {  
                                  caption => 'in caption',  
                                  colgroup => 'in column group',  
                                  tbody => 'in table body',  
                                  tfoot => 'in table body',  
                                  thead => 'in table body',  
                                 }->{$token->{tag_name}};  
               !!!next-token;  
               redo B;  
             } elsif ({  
                       col => 1,  
                       td => 1, th => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## Clear back to table context  
               while ($self->{open_elements}->[-1]->[1] ne 'table' and  
                      $self->{open_elements}->[-1]->[1] ne 'html') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
   
               !!!insert-element ($token->{tag_name} eq 'col' ? 'colgroup' : 'tbody');  
               $self->{insertion_mode} = $token->{tag_name} eq 'col'  
                 ? 'in column group' : 'in table body';  
               ## reprocess  
               redo B;  
             } elsif ($token->{tag_name} eq 'table') {  
               ## NOTE: There are code clones for this "table in table"  
               !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
   
               ## As if </table>  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'table') {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:table');  
                 ## Ignore tokens </table><table>  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'table') {  
               ## have a table element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
5643                }                }
5644                  last OE;
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1, colgroup => 1,  
                       html => 1, tbody => 1, td => 1, tfoot => 1, th => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
               ## Ignore the token  
               !!!next-token;  
               redo B;  
             } else {  
               #  
5645              }              }
5646              } # OE
5647              $foster_parent_element = $self->{open_elements}->[0]->[0] and
5648              $prev_sibling = $foster_parent_element->last_child
5649                  unless defined $foster_parent_element;
5650              undef $prev_sibling unless $open_tables->[-1]->[2]; # ~node inserted
5651              if (defined $prev_sibling and
5652                  $prev_sibling->node_type == 3) {
5653                !!!cp ('t198');
5654                $prev_sibling->manakai_append_text ($token->{data});
5655            } else {            } else {
5656              #              !!!cp ('t199');
5657            }              $foster_parent_element->insert_before
5658                    ($self->{document}->create_text_node ($token->{data}),
5659            !!!parse-error (type => 'in table:'.$token->{tag_name});                   $next_sibling);
5660            $in_body->($insert_to_foster);            }
5661            redo B;            $open_tables->[-1]->[1] = 1; # tainted
5662          } elsif ($self->{insertion_mode} eq 'in column group') {            $open_tables->[-1]->[2] = 1; # ~node inserted
5663            if ($token->{type} eq 'character') {          } else {
5664              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {            ## NOTE: Fragment case or in a foster parent'ed element
5665                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            ## (e.g. |<table><span>a|).  In fragment case, whether the
5666                unless (length $token->{data}) {            ## character is appended to existing node or a new node is
5667                  !!!next-token;            ## created is irrelevant, since the foster parent'ed nodes
5668                  redo B;            ## are discarded and fragment parsing does not invoke any
5669                }            ## script.
5670              !!!cp ('t200');
5671              $self->{open_elements}->[-1]->[0]->manakai_append_text
5672                  ($token->{data});
5673            }
5674                
5675            !!!next-token;
5676            next B;
5677          } elsif ($token->{type} == START_TAG_TOKEN) {
5678            if ({
5679                 tr => ($self->{insertion_mode} != IN_ROW_IM),
5680                 th => 1, td => 1,
5681                }->{$token->{tag_name}}) {
5682              if ($self->{insertion_mode} == IN_TABLE_IM) {
5683                ## Clear back to table context
5684                while (not ($self->{open_elements}->[-1]->[1]
5685                                & TABLE_SCOPING_EL)) {
5686                  !!!cp ('t201');
5687                  pop @{$self->{open_elements}};
5688              }              }
5689                            
5690              #              !!!insert-element ('tbody',, $token);
5691            } elsif ($token->{type} eq 'start tag') {              $self->{insertion_mode} = IN_TABLE_BODY_IM;
5692              if ($token->{tag_name} eq 'col') {              ## reprocess in the "in table body" insertion mode...
5693                !!!insert-element ($token->{tag_name}, $token->{attributes});            }
5694              
5695              if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5696                unless ($token->{tag_name} eq 'tr') {
5697                  !!!cp ('t202');
5698                  !!!parse-error (type => 'missing start tag:tr', token => $token);
5699                }
5700                    
5701                ## Clear back to table body context
5702                while (not ($self->{open_elements}->[-1]->[1]
5703                                & TABLE_ROWS_SCOPING_EL)) {
5704                  !!!cp ('t203');
5705                  ## ISSUE: Can this case be reached?
5706                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
               !!!next-token;  
               redo B;  
             } else {  
               #  
5707              }              }
5708            } elsif ($token->{type} eq 'end tag') {                  
5709              if ($token->{tag_name} eq 'colgroup') {              $self->{insertion_mode} = IN_ROW_IM;
5710                if ($self->{open_elements}->[-1]->[1] eq 'html') {              if ($token->{tag_name} eq 'tr') {
5711                  !!!parse-error (type => 'unmatched end tag:colgroup');                !!!cp ('t204');
5712                  ## Ignore the token                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5713                  !!!next-token;                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5714                  redo B;                !!!nack ('t204');
               } else {  
                 pop @{$self->{open_elements}}; # colgroup  
                 $self->{insertion_mode} = 'in table';  
                 !!!next-token;  
                 redo B;              
               }  
             } elsif ($token->{tag_name} eq 'col') {  
               !!!parse-error (type => 'unmatched end tag:col');  
               ## Ignore the token  
5715                !!!next-token;                !!!next-token;
5716                redo B;                next B;
5717              } else {              } else {
5718                #                !!!cp ('t205');
5719                  !!!insert-element ('tr',, $token);
5720                  ## reprocess in the "in row" insertion mode
5721              }              }
5722            } else {            } else {
5723              #              !!!cp ('t206');
5724            }            }
5725    
5726            ## As if </colgroup>                ## Clear back to table row context
5727            if ($self->{open_elements}->[-1]->[1] eq 'html') {                while (not ($self->{open_elements}->[-1]->[1]
5728              !!!parse-error (type => 'unmatched end tag:colgroup');                                & TABLE_ROW_SCOPING_EL)) {
5729              ## Ignore the token                  !!!cp ('t207');
5730              !!!next-token;                  pop @{$self->{open_elements}};
             redo B;  
           } else {  
             pop @{$self->{open_elements}}; # colgroup  
             $self->{insertion_mode} = 'in table';  
             ## reprocess  
             redo B;  
           }  
         } elsif ($self->{insertion_mode} eq 'in table body') {  
           if ($token->{type} eq 'character') {  
             ## NOTE: This is a "character in table" code clone.  
             if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
                 
               unless (length $token->{data}) {  
                 !!!next-token;  
                 redo B;  
5731                }                }
5732              }                
5733              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5734              !!!parse-error (type => 'in table:#character');            $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5735              $self->{insertion_mode} = IN_CELL_IM;
             ## As if in body, but insert into foster parent element  
             ## ISSUE: Spec says that "whenever a node would be inserted  
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
5736    
5737              if ({            push @$active_formatting_elements, ['#marker', ''];
5738                   table => 1, tbody => 1, tfoot => 1,                
5739                   thead => 1, tr => 1,            !!!nack ('t207.1');
5740                  }->{$self->{open_elements}->[-1]->[1]}) {            !!!next-token;
5741                # MUST            next B;
5742                my $foster_parent_element;          } elsif ({
5743                my $next_sibling;                    caption => 1, col => 1, colgroup => 1,
5744                my $prev_sibling;                    tbody => 1, tfoot => 1, thead => 1,
5745                OE: for (reverse 0..$#{$self->{open_elements}}) {                    tr => 1, # $self->{insertion_mode} == IN_ROW_IM
5746                  if ($self->{open_elements}->[$_]->[1] eq 'table') {                   }->{$token->{tag_name}}) {
5747                    my $parent = $self->{open_elements}->[$_]->[0]->parent_node;            if ($self->{insertion_mode} == IN_ROW_IM) {
5748                    if (defined $parent and $parent->node_type == 1) {              ## As if </tr>
5749                      $foster_parent_element = $parent;              ## have an element in table scope
5750                      $next_sibling = $self->{open_elements}->[$_]->[0];              my $i;
5751                      $prev_sibling = $next_sibling->previous_sibling;              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5752                    } else {                my $node = $self->{open_elements}->[$_];
5753                      $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];                if ($node->[1] == TABLE_ROW_EL) {
5754                      $prev_sibling = $foster_parent_element->last_child;                  !!!cp ('t208');
5755                    }                  $i = $_;
5756                    last OE;                  last INSCOPE;
5757                  }                } elsif ($node->[1] & TABLE_SCOPING_EL) {
5758                } # OE                  !!!cp ('t209');
5759                $foster_parent_element = $self->{open_elements}->[0]->[0] and                  last INSCOPE;
5760                $prev_sibling = $foster_parent_element->last_child                }
5761                  unless defined $foster_parent_element;              } # INSCOPE
5762                if (defined $prev_sibling and              unless (defined $i) {
5763                    $prev_sibling->node_type == 3) {                !!!cp ('t210');
5764                  $prev_sibling->manakai_append_text ($token->{data});                ## TODO: This type is wrong.
5765                } else {                !!!parse-error (type => 'unmacthed end tag',
5766                  $foster_parent_element->insert_before                                text => $token->{tag_name}, token => $token);
5767                    ($self->{document}->create_text_node ($token->{data}),                ## Ignore the token
5768                     $next_sibling);                !!!nack ('t210.1');
5769                }                !!!next-token;
5770              } else {                next B;
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
5771              }              }
5772                                
5773              !!!next-token;                  ## Clear back to table row context
5774              redo B;                  while (not ($self->{open_elements}->[-1]->[1]
5775            } elsif ($token->{type} eq 'start tag') {                                  & TABLE_ROW_SCOPING_EL)) {
5776              if ({                    !!!cp ('t211');
5777                   tr => 1,                    ## ISSUE: Can this case be reached?
5778                   th => 1, td => 1,                    pop @{$self->{open_elements}};
5779                  }->{$token->{tag_name}}) {                  }
5780                unless ($token->{tag_name} eq 'tr') {                  
5781                  !!!parse-error (type => 'missing start tag:tr');                  pop @{$self->{open_elements}}; # tr
5782                    $self->{insertion_mode} = IN_TABLE_BODY_IM;
5783                    if ($token->{tag_name} eq 'tr') {
5784                      !!!cp ('t212');
5785                      ## reprocess
5786                      !!!ack-later;
5787                      next B;
5788                    } else {
5789                      !!!cp ('t213');
5790                      ## reprocess in the "in table body" insertion mode...
5791                    }
5792                }                }
5793    
5794                ## Clear back to table body context                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
5795                while (not {                  ## have an element in table scope
5796                  tbody => 1, tfoot => 1, thead => 1, html => 1,                  my $i;
5797                }->{$self->{open_elements}->[-1]->[1]}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5798                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    my $node = $self->{open_elements}->[$_];
5799                  pop @{$self->{open_elements}};                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
5800                }                      !!!cp ('t214');
5801                                      $i = $_;
5802                $self->{insertion_mode} = 'in row';                      last INSCOPE;
5803                if ($token->{tag_name} eq 'tr') {                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
5804                  !!!insert-element ($token->{tag_name}, $token->{attributes});                      !!!cp ('t215');
5805                  !!!next-token;                      last INSCOPE;
5806                } else {                    }
5807                  !!!insert-element ('tr');                  } # INSCOPE
5808                  ## reprocess                  unless (defined $i) {
5809                }                    !!!cp ('t216');
5810                redo B;  ## TODO: This erorr type is wrong.
5811              } elsif ({                    !!!parse-error (type => 'unmatched end tag',
5812                        caption => 1, col => 1, colgroup => 1,                                    text => $token->{tag_name}, token => $token);
5813                        tbody => 1, tfoot => 1, thead => 1,                    ## Ignore the token
5814                       }->{$token->{tag_name}}) {                    !!!nack ('t216.1');
5815                ## have an element in table scope                    !!!next-token;
5816                my $i;                    next B;
               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;  
5817                  }                  }
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
5818    
5819                ## Clear back to table body context                  ## Clear back to table body context
5820                while (not {                  while (not ($self->{open_elements}->[-1]->[1]
5821                  tbody => 1, tfoot => 1, thead => 1, html => 1,                                  & TABLE_ROWS_SCOPING_EL)) {
5822                }->{$self->{open_elements}->[-1]->[1]}) {                    !!!cp ('t217');
5823                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                    ## ISSUE: Can this state be reached?
5824                      pop @{$self->{open_elements}};
5825                    }
5826                    
5827                    ## As if <{current node}>
5828                    ## have an element in table scope
5829                    ## true by definition
5830                    
5831                    ## Clear back to table body context
5832                    ## nop by definition
5833                    
5834                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
5835                    $self->{insertion_mode} = IN_TABLE_IM;
5836                    ## reprocess in "in table" insertion mode...
5837                  } else {
5838                    !!!cp ('t218');
5839                }                }
5840    
5841                ## As if <{current node}>            if ($token->{tag_name} eq 'col') {
5842                ## have an element in table scope              ## Clear back to table context
5843                ## true by definition              while (not ($self->{open_elements}->[-1]->[1]
5844                                & TABLE_SCOPING_EL)) {
5845                ## Clear back to table body context                !!!cp ('t219');
5846                ## nop by definition                ## ISSUE: Can this state be reached?
   
5847                pop @{$self->{open_elements}};                pop @{$self->{open_elements}};
5848                $self->{insertion_mode} = 'in table';              }
5849                ## reprocess              
5850                redo B;              !!!insert-element ('colgroup',, $token);
5851                $self->{insertion_mode} = IN_COLUMN_GROUP_IM;
5852                ## reprocess
5853                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5854                !!!ack-later;
5855                next B;
5856              } elsif ({
5857                        caption => 1,
5858                        colgroup => 1,
5859                        tbody => 1, tfoot => 1, thead => 1,
5860                       }->{$token->{tag_name}}) {
5861                ## Clear back to table context
5862                    while (not ($self->{open_elements}->[-1]->[1]
5863                                    & TABLE_SCOPING_EL)) {
5864                      !!!cp ('t220');
5865                      ## ISSUE: Can this state be reached?
5866                      pop @{$self->{open_elements}};
5867                    }
5868                    
5869                push @$active_formatting_elements, ['#marker', '']
5870                    if $token->{tag_name} eq 'caption';
5871                    
5872                !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5873                $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5874                $self->{insertion_mode} = {
5875                                           caption => IN_CAPTION_IM,
5876                                           colgroup => IN_COLUMN_GROUP_IM,
5877                                           tbody => IN_TABLE_BODY_IM,
5878                                           tfoot => IN_TABLE_BODY_IM,
5879                                           thead => IN_TABLE_BODY_IM,
5880                                          }->{$token->{tag_name}};
5881                !!!next-token;
5882                !!!nack ('t220.1');
5883                next B;
5884              } else {
5885                die "$0: in table: <>: $token->{tag_name}";
5886              }
5887              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
5888                ## NOTE: This is a code clone of "table in table"                !!!parse-error (type => 'not closed',
5889                !!!parse-error (type => 'not closed:table');                                text => $self->{open_elements}->[-1]->[0]
5890                                      ->manakai_local_name,
5891                                  token => $token);
5892    
5893                ## As if </table>                ## As if </table>
5894                ## have a table element in table scope                ## have a table element in table scope
5895                my $i;                my $i;
5896                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
5897                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
5898                  if ($node->[1] eq 'table') {                  if ($node->[1] == TABLE_EL) {
5899                      !!!cp ('t221');
5900                    $i = $_;                    $i = $_;
5901                    last INSCOPE;                    last INSCOPE;
5902                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
5903                            table => 1, html => 1,                    !!!cp ('t222');
                          }->{$node->[1]}) {  
5904                    last INSCOPE;                    last INSCOPE;
5905                  }                  }
5906                } # INSCOPE                } # INSCOPE
5907                unless (defined $i) {                unless (defined $i) {
5908                  !!!parse-error (type => 'unmatched end tag:table');                  !!!cp ('t223');
5909    ## TODO: The following is wrong, maybe.
5910                    !!!parse-error (type => 'unmatched end tag', text => 'table',
5911                                    token => $token);
5912                  ## Ignore tokens </table><table>                  ## Ignore tokens </table><table>
5913                    !!!nack ('t223.1');
5914                  !!!next-token;                  !!!next-token;
5915                  redo B;                  next B;
5916                }                }
5917                                
5918    ## TODO: Followings are removed from the latest spec.
5919                ## generate implied end tags                ## generate implied end tags
5920                if ({                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
5921                     dd => 1, dt => 1, li => 1, p => 1,                  !!!cp ('t224');
5922                     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;  
5923                }                }
5924    
5925                if ($self->{open_elements}->[-1]->[1] ne 'table') {                unless ($self->{open_elements}->[-1]->[1] == TABLE_EL) {
5926                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                  !!!cp ('t225');
5927                    ## NOTE: |<table><tr><table>|
5928                    !!!parse-error (type => 'not closed',
5929                                    text => $self->{open_elements}->[-1]->[0]
5930                                        ->manakai_local_name,
5931                                    token => $token);
5932                  } else {
5933                    !!!cp ('t226');
5934                }                }
5935    
5936                splice @{$self->{open_elements}}, $i;                splice @{$self->{open_elements}}, $i;
5937                  pop @{$open_tables};
5938    
5939                $self->_reset_insertion_mode;                $self->_reset_insertion_mode;
5940    
5941                ## reprocess            ## reprocess
5942                redo B;            !!!ack-later;
5943              } else {            next B;
5944                #          } elsif ($token->{tag_name} eq 'style') {
5945              }            if (not $open_tables->[-1]->[1]) { # tainted
5946            } elsif ($token->{type} eq 'end tag') {              !!!cp ('t227.8');
5947              if ({              ## NOTE: This is a "as if in head" code clone.
5948                   tbody => 1, tfoot => 1, thead => 1,              $parse_rcdata->(CDATA_CONTENT_MODEL);
5949                  }->{$token->{tag_name}}) {              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5950                ## have an element in table scope              next B;
5951                my $i;            } else {
5952                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              !!!cp ('t227.7');
5953                  my $node = $self->{open_elements}->[$_];              #
5954                  if ($node->[1] eq $token->{tag_name}) {            }
5955                    $i = $_;          } elsif ($token->{tag_name} eq 'script') {
5956                    last INSCOPE;            if (not $open_tables->[-1]->[1]) { # tainted
5957                  } elsif ({              !!!cp ('t227.6');
5958                            table => 1, html => 1,              ## NOTE: This is a "as if in head" code clone.
5959                           }->{$node->[1]}) {              $script_start_tag->();
5960                    last INSCOPE;              $open_tables->[-1]->[2] = 0 if @$open_tables; # ~node inserted
5961                  }              next B;
5962                } # INSCOPE            } else {
5963                unless (defined $i) {              !!!cp ('t227.5');
5964                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              #
5965                  ## Ignore the token            }
5966                  !!!next-token;          } elsif ($token->{tag_name} eq 'input') {
5967                  redo B;            if (not $open_tables->[-1]->[1]) { # tainted
5968                }              if ($token->{attributes}->{type}) { ## TODO: case
5969                  my $type = lc $token->{attributes}->{type}->{value};
5970                  if ($type eq 'hidden') {
5971                    !!!cp ('t227.3');
5972                    !!!parse-error (type => 'in table',
5973                                    text => $token->{tag_name}, token => $token);
5974    
5975                ## Clear back to table body context                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
5976                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}};  
               }  
5977    
5978                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;  
               }  
5979    
               ## 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]);  
5980                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
               }  
5981    
5982                ## As if <{current node}>                  !!!next-token;
5983                ## have an element in table scope                  !!!ack ('t227.2.1');
5984                ## true by definition                  next B;
5985                  } else {
5986                ## Clear back to table body context                  !!!cp ('t227.2');
5987                ## nop by definition                  #
5988                  }
               pop @{$self->{open_elements}};  
               $self->{insertion_mode} = 'in table';  
               ## reprocess  
               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;  
5989              } else {              } else {
5990                  !!!cp ('t227.1');
5991                #                #
5992              }              }
5993            } else {            } else {
5994                !!!cp ('t227.4');
5995              #              #
5996            }            }
5997                      } else {
5998            ## As if in table            !!!cp ('t227');
5999            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
6000            $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');  
6001    
6002              ## As if in body, but insert into foster parent element          !!!parse-error (type => 'in table', text => $token->{tag_name},
6003              ## ISSUE: Spec says that "whenever a node would be inserted                          token => $token);
             ## into the current node" while characters might not be  
             ## result in a new Text node.  
             $reconstruct_active_formatting_elements->($insert_to_foster);  
               
             if ({  
                  table => 1, tbody => 1, tfoot => 1,  
                  thead => 1, tr => 1,  
                 }->{$self->{open_elements}->[-1]->[1]}) {  
               # MUST  
               my $foster_parent_element;  
               my $next_sibling;  
               my $prev_sibling;  
               OE: for (reverse 0..$#{$self->{open_elements}}) {  
                 if ($self->{open_elements}->[$_]->[1] eq 'table') {  
                   my $parent = $self->{open_elements}->[$_]->[0]->parent_node;  
                   if (defined $parent and $parent->node_type == 1) {  
                     $foster_parent_element = $parent;  
                     $next_sibling = $self->{open_elements}->[$_]->[0];  
                     $prev_sibling = $next_sibling->previous_sibling;  
                   } else {  
                     $foster_parent_element = $self->{open_elements}->[$_ - 1]->[0];  
                     $prev_sibling = $foster_parent_element->last_child;  
                   }  
                   last OE;  
                 }  
               } # OE  
               $foster_parent_element = $self->{open_elements}->[0]->[0] and  
               $prev_sibling = $foster_parent_element->last_child  
                 unless defined $foster_parent_element;  
               if (defined $prev_sibling and  
                   $prev_sibling->node_type == 3) {  
                 $prev_sibling->manakai_append_text ($token->{data});  
               } else {  
                 $foster_parent_element->insert_before  
                   ($self->{document}->create_text_node ($token->{data}),  
                    $next_sibling);  
               }  
             } else {  
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             }  
               
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'th' or  
                 $token->{tag_name} eq 'td') {  
               ## Clear back to table row context  
               while (not {  
                 tr => 1, html => 1,  
               }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
                 pop @{$self->{open_elements}};  
               }  
                 
               !!!insert-element ($token->{tag_name}, $token->{attributes});  
               $self->{insertion_mode} = 'in cell';  
6004    
6005                push @$active_formatting_elements, ['#marker', ''];          $insert = $insert_to_foster;
6006                          #
6007                !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
6008                redo B;              if ($token->{tag_name} eq 'tr' and
6009              } 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>  
6010                ## have an element in table scope                ## have an element in table scope
6011                my $i;                my $i;
6012                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6013                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6014                  if ($node->[1] eq 'tr') {                  if ($node->[1] == TABLE_ROW_EL) {
6015                      !!!cp ('t228');
6016                    $i = $_;                    $i = $_;
6017                    last INSCOPE;                    last INSCOPE;
6018                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6019                            table => 1, html => 1,                    !!!cp ('t229');
                          }->{$node->[1]}) {  
6020                    last INSCOPE;                    last INSCOPE;
6021                  }                  }
6022                } # INSCOPE                } # INSCOPE
6023                unless (defined $i) {                unless (defined $i) {
6024                  !!!parse-error (type => 'unmacthed end tag:'.$token->{tag_name});                  !!!cp ('t230');
6025                    !!!parse-error (type => 'unmatched end tag',
6026                                    text => $token->{tag_name}, token => $token);
6027                  ## Ignore the token                  ## Ignore the token
6028                    !!!nack ('t230.1');
6029                  !!!next-token;                  !!!next-token;
6030                  redo B;                  next B;
6031                  } else {
6032                    !!!cp ('t232');
6033                }                }
6034    
6035                ## Clear back to table row context                ## Clear back to table row context
6036                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6037                  tr => 1, html => 1,                                & TABLE_ROW_SCOPING_EL)) {
6038                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t231');
6039                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this state be reached?
6040                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6041                }                }
6042    
6043                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}}; # tr
6044                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_BODY_IM;
6045                ## reprocess                !!!next-token;
6046                redo B;                !!!nack ('t231.1');
6047                  next B;
6048              } elsif ($token->{tag_name} eq 'table') {              } elsif ($token->{tag_name} eq 'table') {
6049                ## NOTE: This is a code clone of "table in table"                if ($self->{insertion_mode} == IN_ROW_IM) {
6050                !!!parse-error (type => 'not closed:table');                  ## As if </tr>
6051                    ## have an element in table scope
6052                ## As if </table>                  my $i;
6053                ## have a table element in table scope                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6054                my $i;                    my $node = $self->{open_elements}->[$_];
6055                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    if ($node->[1] == TABLE_ROW_EL) {
6056                  my $node = $self->{open_elements}->[$_];                      !!!cp ('t233');
6057                  if ($node->[1] eq 'table') {                      $i = $_;
6058                    $i = $_;                      last INSCOPE;
6059                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6060                  } elsif ({                      !!!cp ('t234');
6061                            table => 1, html => 1,                      last INSCOPE;
6062                           }->{$node->[1]}) {                    }
6063                    last INSCOPE;                  } # INSCOPE
6064                    unless (defined $i) {
6065                      !!!cp ('t235');
6066    ## TODO: The following is wrong.
6067                      !!!parse-error (type => 'unmatched end tag',
6068                                      text => $token->{type}, token => $token);
6069                      ## Ignore the token
6070                      !!!nack ('t236.1');
6071                      !!!next-token;
6072                      next B;
6073                  }                  }
6074                } # INSCOPE                  
6075                unless (defined $i) {                  ## Clear back to table row context
6076                  !!!parse-error (type => 'unmatched end tag:table');                  while (not ($self->{open_elements}->[-1]->[1]
6077                  ## Ignore tokens </table><table>                                  & TABLE_ROW_SCOPING_EL)) {
6078                  !!!next-token;                    !!!cp ('t236');
6079                  redo B;  ## ISSUE: Can this state be reached?
6080                }                    pop @{$self->{open_elements}};
                 
               ## generate implied end tags  
               if ({  
                    dd => 1, dt => 1, li => 1, p => 1,  
                    td => 1, th => 1, tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token; # <table>  
                 $token = {type => 'end tag', tag_name => 'table'};  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne 'table') {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
               }  
   
               splice @{$self->{open_elements}}, $i;  
   
               $self->_reset_insertion_mode;  
   
               ## reprocess  
               redo B;  
             } else {  
               #  
             }  
           } elsif ($token->{type} eq 'end tag') {  
             if ($token->{tag_name} eq 'tr') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
6081                  }                  }
6082                } # INSCOPE                  
6083                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
6084                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6085                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
6086                  !!!next-token;                }
6087                  redo B;  
6088                }                if ($self->{insertion_mode} == IN_TABLE_BODY_IM) {
6089                    ## have an element in table scope
6090                ## Clear back to table row context                  my $i;
6091                while (not {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6092                  tr => 1, html => 1,                    my $node = $self->{open_elements}->[$_];
6093                }->{$self->{open_elements}->[-1]->[1]}) {                    if ($node->[1] == TABLE_ROW_GROUP_EL) {
6094                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                      !!!cp ('t237');
6095                        $i = $_;
6096                        last INSCOPE;
6097                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6098                        !!!cp ('t238');
6099                        last INSCOPE;
6100                      }
6101                    } # INSCOPE
6102                    unless (defined $i) {
6103                      !!!cp ('t239');
6104                      !!!parse-error (type => 'unmatched end tag',
6105                                      text => $token->{tag_name}, token => $token);
6106                      ## Ignore the token
6107                      !!!nack ('t239.1');
6108                      !!!next-token;
6109                      next B;
6110                    }
6111                    
6112                    ## Clear back to table body context
6113                    while (not ($self->{open_elements}->[-1]->[1]
6114                                    & TABLE_ROWS_SCOPING_EL)) {
6115                      !!!cp ('t240');
6116                      pop @{$self->{open_elements}};
6117                    }
6118                    
6119                    ## As if <{current node}>
6120                    ## have an element in table scope
6121                    ## true by definition
6122                    
6123                    ## Clear back to table body context
6124                    ## nop by definition
6125                    
6126                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6127                    $self->{insertion_mode} = IN_TABLE_IM;
6128                    ## reprocess in the "in table" insertion mode...
6129                }                }
6130    
6131                pop @{$self->{open_elements}}; # tr                ## NOTE: </table> in the "in table" insertion mode.
6132                $self->{insertion_mode} = 'in table body';                ## When you edit the code fragment below, please ensure that
6133                !!!next-token;                ## the code for <table> in the "in table" insertion mode
6134                redo B;                ## is synced with it.
6135              } elsif ($token->{tag_name} eq 'table') {  
6136                ## As if </tr>                ## have a table element in table scope
               ## have an element in table scope  
6137                my $i;                my $i;
6138                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6139                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6140                  if ($node->[1] eq 'tr') {                  if ($node->[1] == TABLE_EL) {
6141                      !!!cp ('t241');
6142                    $i = $_;                    $i = $_;
6143                    last INSCOPE;                    last INSCOPE;
6144                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6145                            table => 1, html => 1,                    !!!cp ('t242');
                          }->{$node->[1]}) {  
6146                    last INSCOPE;                    last INSCOPE;
6147                  }                  }
6148                } # INSCOPE                } # INSCOPE
6149                unless (defined $i) {                unless (defined $i) {
6150                  !!!parse-error (type => 'unmatched end tag:'.$token->{type});                  !!!cp ('t243');
6151                    !!!parse-error (type => 'unmatched end tag',
6152                                    text => $token->{tag_name}, token => $token);
6153                  ## Ignore the token                  ## Ignore the token
6154                    !!!nack ('t243.1');
6155                  !!!next-token;                  !!!next-token;
6156                  redo B;                  next B;
6157                }                }
6158                    
6159                ## Clear back to table row context                splice @{$self->{open_elements}}, $i;
6160                while (not {                pop @{$open_tables};
6161                  tr => 1, html => 1,                
6162                }->{$self->{open_elements}->[-1]->[1]}) {                $self->_reset_insertion_mode;
6163                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);                
6164                  pop @{$self->{open_elements}};                !!!next-token;
6165                }                next B;
   
               pop @{$self->{open_elements}}; # tr  
               $self->{insertion_mode} = 'in table body';  
               ## reprocess  
               redo B;  
6166              } elsif ({              } elsif ({
6167                        tbody => 1, tfoot => 1, thead => 1,                        tbody => 1, tfoot => 1, thead => 1,
6168                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}} and
6169                ## have an element in table scope                       $self->{insertion_mode} & ROW_IMS) {
6170                my $i;                if ($self->{insertion_mode} == IN_ROW_IM) {
6171                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  ## have an element in table scope
6172                  my $node = $self->{open_elements}->[$_];                  my $i;
6173                  if ($node->[1] eq $token->{tag_name}) {                  INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6174                    $i = $_;                    my $node = $self->{open_elements}->[$_];
6175                    last INSCOPE;                    if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6176                  } elsif ({                      !!!cp ('t247');
6177                            table => 1, html => 1,                      $i = $_;
6178                           }->{$node->[1]}) {                      last INSCOPE;
6179                    last INSCOPE;                    } elsif ($node->[1] & TABLE_SCOPING_EL) {
6180                        !!!cp ('t248');
6181                        last INSCOPE;
6182                      }
6183                    } # INSCOPE
6184                      unless (defined $i) {
6185                        !!!cp ('t249');
6186                        !!!parse-error (type => 'unmatched end tag',
6187                                        text => $token->{tag_name}, token => $token);
6188                        ## Ignore the token
6189                        !!!nack ('t249.1');
6190                        !!!next-token;
6191                        next B;
6192                      }
6193                    
6194                    ## As if </tr>
6195                    ## have an element in table scope
6196                    my $i;
6197                    INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6198                      my $node = $self->{open_elements}->[$_];
6199                      if ($node->[1] == TABLE_ROW_EL) {
6200                        !!!cp ('t250');
6201                        $i = $_;
6202                        last INSCOPE;
6203                      } elsif ($node->[1] & TABLE_SCOPING_EL) {
6204                        !!!cp ('t251');
6205                        last INSCOPE;
6206                      }
6207                    } # INSCOPE
6208                      unless (defined $i) {
6209                        !!!cp ('t252');
6210                        !!!parse-error (type => 'unmatched end tag',
6211                                        text => 'tr', token => $token);
6212                        ## Ignore the token
6213                        !!!nack ('t252.1');
6214                        !!!next-token;
6215                        next B;
6216                      }
6217                    
6218                    ## Clear back to table row context
6219                    while (not ($self->{open_elements}->[-1]->[1]
6220                                    & TABLE_ROW_SCOPING_EL)) {
6221                      !!!cp ('t253');
6222    ## ISSUE: Can this case be reached?
6223                      pop @{$self->{open_elements}};
6224                  }                  }
6225                } # INSCOPE                  
6226                unless (defined $i) {                  pop @{$self->{open_elements}}; # tr
6227                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                  $self->{insertion_mode} = IN_TABLE_BODY_IM;
6228                  ## Ignore the token                  ## reprocess in the "in table body" insertion mode...
                 !!!next-token;  
                 redo B;  
6229                }                }
6230    
               ## As if </tr>  
6231                ## have an element in table scope                ## have an element in table scope
6232                my $i;                my $i;
6233                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6234                  my $node = $self->{open_elements}->[$_];                  my $node = $self->{open_elements}->[$_];
6235                  if ($node->[1] eq 'tr') {                  if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6236                      !!!cp ('t254');
6237                    $i = $_;                    $i = $_;
6238                    last INSCOPE;                    last INSCOPE;
6239                  } elsif ({                  } elsif ($node->[1] & TABLE_SCOPING_EL) {
6240                            table => 1, html => 1,                    !!!cp ('t255');
                          }->{$node->[1]}) {  
6241                    last INSCOPE;                    last INSCOPE;
6242                  }                  }
6243                } # INSCOPE                } # INSCOPE
6244                unless (defined $i) {                unless (defined $i) {
6245                  !!!parse-error (type => 'unmatched end tag:tr');                  !!!cp ('t256');
6246                    !!!parse-error (type => 'unmatched end tag',
6247                                    text => $token->{tag_name}, token => $token);
6248                  ## Ignore the token                  ## Ignore the token
6249                    !!!nack ('t256.1');
6250                  !!!next-token;                  !!!next-token;
6251                  redo B;                  next B;
6252                }                }
6253    
6254                ## Clear back to table row context                ## Clear back to table body context
6255                while (not {                while (not ($self->{open_elements}->[-1]->[1]
6256                  tr => 1, html => 1,                                & TABLE_ROWS_SCOPING_EL)) {
6257                }->{$self->{open_elements}->[-1]->[1]}) {                  !!!cp ('t257');
6258                  !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  ## ISSUE: Can this case be reached?
6259                  pop @{$self->{open_elements}};                  pop @{$self->{open_elements}};
6260                }                }
6261    
6262                pop @{$self->{open_elements}}; # tr                pop @{$self->{open_elements}};
6263                $self->{insertion_mode} = 'in table body';                $self->{insertion_mode} = IN_TABLE_IM;
6264                ## reprocess                !!!nack ('t257.1');
6265                redo B;                !!!next-token;
6266                  next B;
6267              } elsif ({              } elsif ({
6268                        body => 1, caption => 1, col => 1,                        body => 1, caption => 1, col => 1, colgroup => 1,
6269                        colgroup => 1, html => 1, td => 1, th => 1,                        html => 1, td => 1, th => 1,
6270                          tr => 1, # $self->{insertion_mode} == IN_ROW_IM
6271                          tbody => 1, tfoot => 1, thead => 1, # $self->{insertion_mode} == IN_TABLE_IM
6272                       }->{$token->{tag_name}}) {                       }->{$token->{tag_name}}) {
6273                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            !!!cp ('t258');
6274                ## Ignore the token            !!!parse-error (type => 'unmatched end tag',
6275                !!!next-token;                            text => $token->{tag_name}, token => $token);
6276                redo B;            ## Ignore the token
6277              } else {            !!!nack ('t258.1');
6278                #             !!!next-token;
6279              }            next B;
6280            } else {          } else {
6281              #            !!!cp ('t259');
6282            }            !!!parse-error (type => 'in table:/',
6283                              text => $token->{tag_name}, token => $token);
6284    
6285            ## As if in table            $insert = $insert_to_foster;
6286            !!!parse-error (type => 'in table:'.$token->{tag_name});            #
6287            $in_body->($insert_to_foster);          }
6288            redo B;        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6289          } elsif ($self->{insertion_mode} eq 'in cell') {          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6290            if ($token->{type} eq 'character') {                  @{$self->{open_elements}} == 1) { # redundant, maybe
6291              ## NOTE: This is a code clone of "character in body".            !!!parse-error (type => 'in body:#eof', token => $token);
6292              $reconstruct_active_formatting_elements->($insert_to_current);            !!!cp ('t259.1');
6293                          #
6294              $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});          } else {
6295              !!!cp ('t259.2');
6296              #
6297            }
6298    
6299              !!!next-token;          ## Stop parsing
6300              redo B;          last B;
6301            } elsif ($token->{type} eq 'start tag') {        } else {
6302              if ({          die "$0: $token->{type}: Unknown token type";
6303                   caption => 1, col => 1, colgroup => 1,        }
6304                   tbody => 1, td => 1, tfoot => 1, th => 1,      } elsif ($self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
6305                   thead => 1, tr => 1,            if ($token->{type} == CHARACTER_TOKEN) {
6306                  }->{$token->{tag_name}}) {              if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6307                ## have an element in table scope                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6308                my $tn;                unless (length $token->{data}) {
6309                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  !!!cp ('t260');
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $tn) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
6310                  !!!next-token;                  !!!next-token;
6311                  redo B;                  next B;
6312                }                }
6313                }
6314                ## Close the cell              
6315                !!!back-token; # <?>              !!!cp ('t261');
6316                $token = {type => 'end tag', tag_name => $tn};              #
6317                redo B;            } elsif ($token->{type} == START_TAG_TOKEN) {
6318              } else {              if ($token->{tag_name} eq 'col') {
6319                  !!!cp ('t262');
6320                  !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6321                  pop @{$self->{open_elements}};
6322                  !!!ack ('t262.1');
6323                  !!!next-token;
6324                  next B;
6325                } else {
6326                  !!!cp ('t263');
6327                #                #
6328              }              }
6329            } elsif ($token->{type} eq 'end tag') {            } elsif ($token->{type} == END_TAG_TOKEN) {
6330              if ($token->{tag_name} eq 'td' or $token->{tag_name} eq 'th') {              if ($token->{tag_name} eq 'colgroup') {
6331                ## have an element in table scope                if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6332                my $i;                  !!!cp ('t264');
6333                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                  !!!parse-error (type => 'unmatched end tag',
6334                  my $node = $self->{open_elements}->[$_];                                  text => 'colgroup', token => $token);
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
6335                  ## Ignore the token                  ## Ignore the token
6336                  !!!next-token;                  !!!next-token;
6337                  redo B;                  next B;
6338                }                } else {
6339                                  !!!cp ('t265');
6340                ## generate implied end tags                  pop @{$self->{open_elements}}; # colgroup
6341                if ({                  $self->{insertion_mode} = IN_TABLE_IM;
6342                     dd => 1, dt => 1, li => 1, p => 1,                  !!!next-token;
6343                     td => ($token->{tag_name} eq 'th'),                  next B;            
                    th => ($token->{tag_name} eq 'td'),  
                    tr => 1,  
                    tbody => 1, tfoot=> 1, thead => 1,  
                   }->{$self->{open_elements}->[-1]->[1]}) {  
                 !!!back-token;  
                 $token = {type => 'end tag',  
                           tag_name => $self->{open_elements}->[-1]->[1]}; # MUST  
                 redo B;  
               }  
   
               if ($self->{open_elements}->[-1]->[1] ne $token->{tag_name}) {  
                 !!!parse-error (type => 'not closed:'.$self->{open_elements}->[-1]->[1]);  
6344                }                }
6345                } elsif ($token->{tag_name} eq 'col') {
6346                splice @{$self->{open_elements}}, $i;                !!!cp ('t266');
6347                  !!!parse-error (type => 'unmatched end tag',
6348                $clear_up_to_marker->();                                text => 'col', token => $token);
   
               $self->{insertion_mode} = 'in row';  
   
               !!!next-token;  
               redo B;  
             } elsif ({  
                       body => 1, caption => 1, col => 1,  
                       colgroup => 1, html => 1,  
                      }->{$token->{tag_name}}) {  
               !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
6349                ## Ignore the token                ## Ignore the token
6350                !!!next-token;                !!!next-token;
6351                redo B;                next B;
             } elsif ({  
                       table => 1, tbody => 1, tfoot => 1,  
                       thead => 1, tr => 1,  
                      }->{$token->{tag_name}}) {  
               ## have an element in table scope  
               my $i;  
               my $tn;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ($node->[1] eq 'td' or $node->[1] eq 'th') {  
                   $tn = $node->[1];  
                   ## NOTE: There is exactly one |td| or |th| element  
                   ## in scope in the stack of open elements by definition.  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
   
               ## Close the cell  
               !!!back-token; # </?>  
               $token = {type => 'end tag', tag_name => $tn};  
               redo B;  
6352              } else {              } else {
6353                #                !!!cp ('t267');
6354                  #
6355              }              }
6356          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6357            if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6358                @{$self->{open_elements}} == 1) { # redundant, maybe
6359              !!!cp ('t270.2');
6360              ## Stop parsing.
6361              last B;
6362            } else {
6363              ## NOTE: As if </colgroup>.
6364              !!!cp ('t270.1');
6365              pop @{$self->{open_elements}}; # colgroup
6366              $self->{insertion_mode} = IN_TABLE_IM;
6367              ## Reprocess.
6368              next B;
6369            }
6370          } else {
6371            die "$0: $token->{type}: Unknown token type";
6372          }
6373    
6374              ## As if </colgroup>
6375              if ($self->{open_elements}->[-1]->[1] == HTML_EL) {
6376                !!!cp ('t269');
6377    ## TODO: Wrong error type?
6378                !!!parse-error (type => 'unmatched end tag',
6379                                text => 'colgroup', token => $token);
6380                ## Ignore the token
6381                !!!nack ('t269.1');
6382                !!!next-token;
6383                next B;
6384            } else {            } else {
6385              #              !!!cp ('t270');
6386                pop @{$self->{open_elements}}; # colgroup
6387                $self->{insertion_mode} = IN_TABLE_IM;
6388                !!!ack-later;
6389                ## reprocess
6390                next B;
6391              }
6392        } elsif ($self->{insertion_mode} & SELECT_IMS) {
6393          if ($token->{type} == CHARACTER_TOKEN) {
6394            !!!cp ('t271');
6395            $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});
6396            !!!next-token;
6397            next B;
6398          } elsif ($token->{type} == START_TAG_TOKEN) {
6399            if ($token->{tag_name} eq 'option') {
6400              if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6401                !!!cp ('t272');
6402                ## As if </option>
6403                pop @{$self->{open_elements}};
6404              } else {
6405                !!!cp ('t273');
6406            }            }
             
           $in_body->($insert_to_current);  
           redo B;  
         } elsif ($self->{insertion_mode} eq 'in select') {  
           if ($token->{type} eq 'character') {  
             $self->{open_elements}->[-1]->[0]->manakai_append_text ($token->{data});  
             !!!next-token;  
             redo B;  
           } elsif ($token->{type} eq 'start tag') {  
             if ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 ## As if </option>  
                 pop @{$self->{open_elements}};  
               }  
6407    
6408                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6409                !!!next-token;            !!!nack ('t273.1');
6410                redo B;            !!!next-token;
6411              } elsif ($token->{tag_name} eq 'optgroup') {            next B;
6412                if ($self->{open_elements}->[-1]->[1] eq 'option') {          } elsif ($token->{tag_name} eq 'optgroup') {
6413                  ## As if </option>            if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6414                  pop @{$self->{open_elements}};              !!!cp ('t274');
6415                }              ## As if </option>
6416                pop @{$self->{open_elements}};
6417              } else {
6418                !!!cp ('t275');
6419              }
6420    
6421                if ($self->{open_elements}->[-1]->[1] eq 'optgroup') {            if ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6422                  ## As if </optgroup>              !!!cp ('t276');
6423                  pop @{$self->{open_elements}};              ## As if </optgroup>
6424                }              pop @{$self->{open_elements}};
6425              } else {
6426                !!!cp ('t277');
6427              }
6428    
6429                !!!insert-element ($token->{tag_name}, $token->{attributes});            !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6430                !!!next-token;            !!!nack ('t277.1');
6431                redo B;            !!!next-token;
6432              } elsif ($token->{tag_name} eq 'select') {            next B;
6433                !!!parse-error (type => 'not closed:select');          } elsif ({
6434                ## As if </select> instead                     select => 1, input => 1, textarea => 1,
6435                ## have an element in table scope                   }->{$token->{tag_name}} or
6436                my $i;                   ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6437                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                    {
6438                  my $node = $self->{open_elements}->[$_];                     caption => 1, table => 1,
6439                  if ($node->[1] eq $token->{tag_name}) {                     tbody => 1, tfoot => 1, thead => 1,
6440                    $i = $_;                     tr => 1, td => 1, th => 1,
6441                    last INSCOPE;                    }->{$token->{tag_name}})) {
6442                  } elsif ({            ## TODO: The type below is not good - <select> is replaced by </select>
6443                            table => 1, html => 1,            !!!parse-error (type => 'not closed', text => 'select',
6444                           }->{$node->[1]}) {                            token => $token);
6445                    last INSCOPE;            ## NOTE: As if the token were </select> (<select> case) or
6446                  }            ## as if there were </select> (otherwise).
6447                } # INSCOPE            ## have an element in table scope
6448                unless (defined $i) {            my $i;
6449                  !!!parse-error (type => 'unmatched end tag:select');            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6450                  ## Ignore the token              my $node = $self->{open_elements}->[$_];
6451                  !!!next-token;              if ($node->[1] == SELECT_EL) {
6452                  redo B;                !!!cp ('t278');
6453                }                $i = $_;
6454                  last INSCOPE;
6455                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6456                  !!!cp ('t279');
6457                  last INSCOPE;
6458                }
6459              } # INSCOPE
6460              unless (defined $i) {
6461                !!!cp ('t280');
6462                !!!parse-error (type => 'unmatched end tag',
6463                                text => 'select', token => $token);
6464                ## Ignore the token
6465                !!!nack ('t280.1');
6466                !!!next-token;
6467                next B;
6468              }
6469                                
6470                splice @{$self->{open_elements}}, $i;            !!!cp ('t281');
6471              splice @{$self->{open_elements}}, $i;
6472    
6473                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6474    
6475                !!!next-token;            if ($token->{tag_name} eq 'select') {
6476                redo B;              !!!nack ('t281.2');
6477              } else {              !!!next-token;
6478                #              next B;
6479              } else {
6480                !!!cp ('t281.1');
6481                !!!ack-later;
6482                ## Reprocess the token.
6483                next B;
6484              }
6485            } else {
6486              !!!cp ('t282');
6487              !!!parse-error (type => 'in select',
6488                              text => $token->{tag_name}, token => $token);
6489              ## Ignore the token
6490              !!!nack ('t282.1');
6491              !!!next-token;
6492              next B;
6493            }
6494          } elsif ($token->{type} == END_TAG_TOKEN) {
6495            if ($token->{tag_name} eq 'optgroup') {
6496              if ($self->{open_elements}->[-1]->[1] == OPTION_EL and
6497                  $self->{open_elements}->[-2]->[1] == OPTGROUP_EL) {
6498                !!!cp ('t283');
6499                ## As if </option>
6500                splice @{$self->{open_elements}}, -2;
6501              } elsif ($self->{open_elements}->[-1]->[1] == OPTGROUP_EL) {
6502                !!!cp ('t284');
6503                pop @{$self->{open_elements}};
6504              } else {
6505                !!!cp ('t285');
6506                !!!parse-error (type => 'unmatched end tag',
6507                                text => $token->{tag_name}, token => $token);
6508                ## Ignore the token
6509              }
6510              !!!nack ('t285.1');
6511              !!!next-token;
6512              next B;
6513            } elsif ($token->{tag_name} eq 'option') {
6514              if ($self->{open_elements}->[-1]->[1] == OPTION_EL) {
6515                !!!cp ('t286');
6516                pop @{$self->{open_elements}};
6517              } else {
6518                !!!cp ('t287');
6519                !!!parse-error (type => 'unmatched end tag',
6520                                text => $token->{tag_name}, token => $token);
6521                ## Ignore the token
6522              }
6523              !!!nack ('t287.1');
6524              !!!next-token;
6525              next B;
6526            } elsif ($token->{tag_name} eq 'select') {
6527              ## have an element in table scope
6528              my $i;
6529              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6530                my $node = $self->{open_elements}->[$_];
6531                if ($node->[1] == SELECT_EL) {
6532                  !!!cp ('t288');
6533                  $i = $_;
6534                  last INSCOPE;
6535                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6536                  !!!cp ('t289');
6537                  last INSCOPE;
6538              }              }
6539            } elsif ($token->{type} eq 'end tag') {            } # INSCOPE
6540              if ($token->{tag_name} eq 'optgroup') {            unless (defined $i) {
6541                if ($self->{open_elements}->[-1]->[1] eq 'option' and              !!!cp ('t290');
6542                    $self->{open_elements}->[-2]->[1] eq 'optgroup') {              !!!parse-error (type => 'unmatched end tag',
6543                  ## As if </option>                              text => $token->{tag_name}, token => $token);
6544                  splice @{$self->{open_elements}}, -2;              ## Ignore the token
6545                } elsif ($self->{open_elements}->[-1]->[1] eq 'optgroup') {              !!!nack ('t290.1');
6546                  pop @{$self->{open_elements}};              !!!next-token;
6547                } else {              next B;
6548                  !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});            }
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'option') {  
               if ($self->{open_elements}->[-1]->[1] eq 'option') {  
                 pop @{$self->{open_elements}};  
               } else {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
               }  
               !!!next-token;  
               redo B;  
             } elsif ($token->{tag_name} eq 'select') {  
               ## have an element in table scope  
               my $i;  
               INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {  
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6549                                
6550                splice @{$self->{open_elements}}, $i;            !!!cp ('t291');
6551              splice @{$self->{open_elements}}, $i;
6552    
6553                $self->_reset_insertion_mode;            $self->_reset_insertion_mode;
6554    
6555                !!!next-token;            !!!nack ('t291.1');
6556                redo B;            !!!next-token;
6557              } elsif ({            next B;
6558                        caption => 1, table => 1, tbody => 1,          } elsif ($self->{insertion_mode} == IN_SELECT_IN_TABLE_IM and
6559                        tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,                   {
6560                       }->{$token->{tag_name}}) {                    caption => 1, table => 1, tbody => 1,
6561                !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});                    tfoot => 1, thead => 1, tr => 1, td => 1, th => 1,
6562                                   }->{$token->{tag_name}}) {
6563                ## have an element in table scope  ## TODO: The following is wrong?
6564                my $i;            !!!parse-error (type => 'unmatched end tag',
6565                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {                            text => $token->{tag_name}, token => $token);
                 my $node = $self->{open_elements}->[$_];  
                 if ($node->[1] eq $token->{tag_name}) {  
                   $i = $_;  
                   last INSCOPE;  
                 } elsif ({  
                           table => 1, html => 1,  
                          }->{$node->[1]}) {  
                   last INSCOPE;  
                 }  
               } # INSCOPE  
               unless (defined $i) {  
                 ## Ignore the token  
                 !!!next-token;  
                 redo B;  
               }  
6566                                
6567                ## As if </select>            ## have an element in table scope
6568                ## have an element in table scope            my $i;
6569                undef $i;            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6570                INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {              my $node = $self->{open_elements}->[$_];
6571                  my $node = $self->{open_elements}->[$_];              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
6572                  if ($node->[1] eq 'select') {                !!!cp ('t292');
6573                    $i = $_;                $i = $_;
6574                    last INSCOPE;                last INSCOPE;
6575                  } elsif ({              } elsif ($node->[1] & TABLE_SCOPING_EL) {
6576                            table => 1, html => 1,                !!!cp ('t293');
6577                           }->{$node->[1]}) {                last INSCOPE;
6578                    last INSCOPE;              }
6579                  }            } # INSCOPE
6580                } # INSCOPE            unless (defined $i) {
6581                unless (defined $i) {              !!!cp ('t294');
6582                  !!!parse-error (type => 'unmatched end tag:select');              ## Ignore the token
6583                  ## Ignore the </select> token              !!!nack ('t294.1');
6584                  !!!next-token; ## TODO: ok?              !!!next-token;
6585                  redo B;              next B;
6586                }            }
6587                                
6588                splice @{$self->{open_elements}}, $i;            ## As if </select>
6589              ## have an element in table scope
6590                $self->_reset_insertion_mode;            undef $i;
6591              INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
6592                ## reprocess              my $node = $self->{open_elements}->[$_];
6593                redo B;              if ($node->[1] == SELECT_EL) {
6594              } else {                !!!cp ('t295');
6595                #                $i = $_;
6596                  last INSCOPE;
6597                } elsif ($node->[1] & TABLE_SCOPING_EL) {
6598    ## ISSUE: Can this state be reached?
6599                  !!!cp ('t296');
6600                  last INSCOPE;
6601              }              }
6602            } else {            } # INSCOPE
6603              #            unless (defined $i) {
6604                !!!cp ('t297');
6605    ## TODO: The following error type is correct?
6606                !!!parse-error (type => 'unmatched end tag',
6607                                text => 'select', token => $token);
6608                ## Ignore the </select> token
6609                !!!nack ('t297.1');
6610                !!!next-token; ## TODO: ok?
6611                next B;
6612            }            }
6613                  
6614              !!!cp ('t298');
6615              splice @{$self->{open_elements}}, $i;
6616    
6617            !!!parse-error (type => 'in select:'.$token->{tag_name});            $self->_reset_insertion_mode;
6618    
6619              !!!ack-later;
6620              ## reprocess
6621              next B;
6622            } else {
6623              !!!cp ('t299');
6624              !!!parse-error (type => 'in select:/',
6625                              text => $token->{tag_name}, token => $token);
6626            ## Ignore the token            ## Ignore the token
6627              !!!nack ('t299.3');
6628            !!!next-token;            !!!next-token;
6629            redo B;            next B;
6630          } elsif ($self->{insertion_mode} eq 'after body') {          }
6631            if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6632              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6633                my $data = $1;                  @{$self->{open_elements}} == 1) { # redundant, maybe
6634                ## As if in body            !!!cp ('t299.1');
6635                $reconstruct_active_formatting_elements->($insert_to_current);            !!!parse-error (type => 'in body:#eof', token => $token);
6636            } else {
6637              !!!cp ('t299.2');
6638            }
6639    
6640            ## Stop parsing.
6641            last B;
6642          } else {
6643            die "$0: $token->{type}: Unknown token type";
6644          }
6645        } elsif ($self->{insertion_mode} & BODY_AFTER_IMS) {
6646          if ($token->{type} == CHARACTER_TOKEN) {
6647            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6648              my $data = $1;
6649              ## As if in body
6650              $reconstruct_active_formatting_elements->($insert_to_current);
6651                                
6652                $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6653              
6654              unless (length $token->{data}) {
6655                !!!cp ('t300');
6656                !!!next-token;
6657                next B;
6658              }
6659            }
6660            
6661            if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6662              !!!cp ('t301');
6663              !!!parse-error (type => 'after html:#text', token => $token);
6664              #
6665            } else {
6666              !!!cp ('t302');
6667              ## "after body" insertion mode
6668              !!!parse-error (type => 'after body:#text', token => $token);
6669              #
6670            }
6671    
6672                unless (length $token->{data}) {          $self->{insertion_mode} = IN_BODY_IM;
6673                  !!!next-token;          ## reprocess
6674                  redo B;          next B;
6675                }        } elsif ($token->{type} == START_TAG_TOKEN) {
6676              }          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6677                          !!!cp ('t303');
6678              #            !!!parse-error (type => 'after html',
6679              !!!parse-error (type => 'after body:#character');                            text => $token->{tag_name}, token => $token);
6680            } elsif ($token->{type} eq 'start tag') {            #
6681              !!!parse-error (type => 'after body:'.$token->{tag_name});          } else {
6682              #            !!!cp ('t304');
6683            } elsif ($token->{type} eq 'end tag') {            ## "after body" insertion mode
6684              if ($token->{tag_name} eq 'html') {            !!!parse-error (type => 'after body',
6685                if (defined $self->{inner_html_node}) {                            text => $token->{tag_name}, token => $token);
6686                  !!!parse-error (type => 'unmatched end tag:html');            #
6687                  ## Ignore the token          }
6688                  !!!next-token;  
6689                  redo B;          $self->{insertion_mode} = IN_BODY_IM;
6690                } else {          !!!ack-later;
6691                  $previous_insertion_mode = $self->{insertion_mode};          ## reprocess
6692                  $self->{insertion_mode} = 'trailing end';          next B;
6693                  !!!next-token;        } elsif ($token->{type} == END_TAG_TOKEN) {
6694                  redo B;          if ($self->{insertion_mode} == AFTER_HTML_BODY_IM) {
6695                }            !!!cp ('t305');
6696              } else {            !!!parse-error (type => 'after html:/',
6697                !!!parse-error (type => 'after body:/'.$token->{tag_name});                            text => $token->{tag_name}, token => $token);
6698              }            
6699              $self->{insertion_mode} = IN_BODY_IM;
6700              ## Reprocess.
6701              next B;
6702            } else {
6703              !!!cp ('t306');
6704            }
6705    
6706            ## "after body" insertion mode
6707            if ($token->{tag_name} eq 'html') {
6708              if (defined $self->{inner_html_node}) {
6709                !!!cp ('t307');
6710                !!!parse-error (type => 'unmatched end tag',
6711                                text => 'html', token => $token);
6712                ## Ignore the token
6713                !!!next-token;
6714                next B;
6715            } else {            } else {
6716              die "$0: $token->{type}: Unknown token type";              !!!cp ('t308');
6717                $self->{insertion_mode} = AFTER_HTML_BODY_IM;
6718                !!!next-token;
6719                next B;
6720            }            }
6721            } else {
6722              !!!cp ('t309');
6723              !!!parse-error (type => 'after body:/',
6724                              text => $token->{tag_name}, token => $token);
6725    
6726            $self->{insertion_mode} = 'in body';            $self->{insertion_mode} = IN_BODY_IM;
6727            ## reprocess            ## reprocess
6728            redo B;            next B;
6729      } elsif ($self->{insertion_mode} eq 'in frameset') {          }
6730        if ($token->{type} eq 'character') {        } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6731          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {          !!!cp ('t309.2');
6732            ## Stop parsing
6733            last B;
6734          } else {
6735            die "$0: $token->{type}: Unknown token type";
6736          }
6737        } elsif ($self->{insertion_mode} & FRAME_IMS) {
6738          if ($token->{type} == CHARACTER_TOKEN) {
6739            if ($token->{data} =~ s/^([\x09\x0A\x0C\x20]+)//) {
6740            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);            $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);
6741              
6742            unless (length $token->{data}) {            unless (length $token->{data}) {
6743                !!!cp ('t310');
6744              !!!next-token;              !!!next-token;
6745              redo B;              next B;
6746            }            }
6747          }          }
6748            
6749          !!!parse-error (type => 'in frameset:#character');          if ($token->{data} =~ s/^[^\x09\x0A\x0C\x20]+//) {
6750          ## Ignore the token            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6751          !!!next-token;              !!!cp ('t311');
6752          redo B;              !!!parse-error (type => 'in frameset:#text', token => $token);
6753        } elsif ($token->{type} eq 'start tag') {            } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6754          if ($token->{tag_name} eq 'frameset') {              !!!cp ('t312');
6755            !!!insert-element ($token->{tag_name}, $token->{attributes});              !!!parse-error (type => 'after frameset:#text', token => $token);
6756              } else { # "after after frameset"
6757                !!!cp ('t313');
6758                !!!parse-error (type => 'after html:#text', token => $token);
6759              }
6760              
6761              ## Ignore the token.
6762              if (length $token->{data}) {
6763                !!!cp ('t314');
6764                ## reprocess the rest of characters
6765              } else {
6766                !!!cp ('t315');
6767                !!!next-token;
6768              }
6769              next B;
6770            }
6771            
6772            die qq[$0: Character "$token->{data}"];
6773          } elsif ($token->{type} == START_TAG_TOKEN) {
6774            if ($token->{tag_name} eq 'frameset' and
6775                $self->{insertion_mode} == IN_FRAMESET_IM) {
6776              !!!cp ('t318');
6777              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6778              !!!nack ('t318.1');
6779            !!!next-token;            !!!next-token;
6780            redo B;            next B;
6781          } elsif ($token->{tag_name} eq 'frame') {          } elsif ($token->{tag_name} eq 'frame' and
6782            !!!insert-element ($token->{tag_name}, $token->{attributes});                   $self->{insertion_mode} == IN_FRAMESET_IM) {
6783              !!!cp ('t319');
6784              !!!insert-element ($token->{tag_name}, $token->{attributes}, $token);
6785            pop @{$self->{open_elements}};            pop @{$self->{open_elements}};
6786              !!!ack ('t319.1');
6787            !!!next-token;            !!!next-token;
6788            redo B;            next B;
6789          } elsif ($token->{tag_name} eq 'noframes') {          } elsif ($token->{tag_name} eq 'noframes') {
6790            $in_body->($insert_to_current);            !!!cp ('t320');
6791            redo B;            ## NOTE: As if in head.
6792          } else {            $parse_rcdata->(CDATA_CONTENT_MODEL);
6793            !!!parse-error (type => 'in frameset:'.$token->{tag_name});            next B;
6794    
6795              ## NOTE: |<!DOCTYPE HTML><frameset></frameset></html><noframes></noframes>|
6796              ## has no parse error.
6797            } else {
6798              if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6799                !!!cp ('t321');
6800                !!!parse-error (type => 'in frameset',
6801                                text => $token->{tag_name}, token => $token);
6802              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6803                !!!cp ('t322');
6804                !!!parse-error (type => 'after frameset',
6805                                text => $token->{tag_name}, token => $token);
6806              } else { # "after after frameset"
6807                !!!cp ('t322.2');
6808                !!!parse-error (type => 'after after frameset',
6809                                text => $token->{tag_name}, token => $token);
6810              }
6811            ## Ignore the token            ## Ignore the token
6812              !!!nack ('t322.1');
6813            !!!next-token;            !!!next-token;
6814            redo B;            next B;
6815          }          }
6816        } elsif ($token->{type} eq 'end tag') {        } elsif ($token->{type} == END_TAG_TOKEN) {
6817          if ($token->{tag_name} eq 'frameset') {          if ($token->{tag_name} eq 'frameset' and
6818            if ($self->{open_elements}->[-1]->[1] eq 'html' and              $self->{insertion_mode} == IN_FRAMESET_IM) {
6819              if ($self->{open_elements}->[-1]->[1] == HTML_EL and
6820                @{$self->{open_elements}} == 1) {                @{$self->{open_elements}} == 1) {
6821              !!!parse-error (type => 'unmatched end tag:'.$token->{tag_name});              !!!cp ('t325');
6822                !!!parse-error (type => 'unmatched end tag',
6823                                text => $token->{tag_name}, token => $token);
6824              ## Ignore the token              ## Ignore the token
6825              !!!next-token;              !!!next-token;
6826            } else {            } else {
6827                !!!cp ('t326');
6828              pop @{$self->{open_elements}};              pop @{$self->{open_elements}};
6829              !!!next-token;              !!!next-token;
6830            }            }
6831    
6832            if (not defined $self->{inner_html_node} and            if (not defined $self->{inner_html_node} and
6833                $self->{open_elements}->[-1]->[1] ne 'frameset') {                not ($self->{open_elements}->[-1]->[1] == FRAMESET_EL)) {
6834              $self->{insertion_mode} = 'after frameset';              !!!cp ('t327');
6835                $self->{insertion_mode} = AFTER_FRAMESET_IM;
6836              } else {
6837                !!!cp ('t328');
6838            }            }
6839            redo B;            next B;
6840            } elsif ($token->{tag_name} eq 'html' and
6841                     $self->{insertion_mode} == AFTER_FRAMESET_IM) {
6842              !!!cp ('t329');
6843              $self->{insertion_mode} = AFTER_HTML_FRAMESET_IM;
6844              !!!next-token;
6845              next B;
6846          } else {          } else {
6847            !!!parse-error (type => 'in frameset:/'.$token->{tag_name});            if ($self->{insertion_mode} == IN_FRAMESET_IM) {
6848                !!!cp ('t330');
6849                !!!parse-error (type => 'in frameset:/',
6850                                text => $token->{tag_name}, token => $token);
6851              } elsif ($self->{insertion_mode} == AFTER_FRAMESET_IM) {
6852                !!!cp ('t330.1');
6853                !!!parse-error (type => 'after frameset:/',
6854                                text => $token->{tag_name}, token => $token);
6855              } else { # "after after html"
6856                !!!cp ('t331');
6857                !!!parse-error (type => 'after after frameset:/',
6858                                text => $token->{tag_name}, token => $token);
6859              }
6860            ## Ignore the token            ## Ignore the token
6861            !!!next-token;            !!!next-token;
6862            redo B;            next B;
6863          }          }
6864          } elsif ($token->{type} == END_OF_FILE_TOKEN) {
6865            unless ($self->{open_elements}->[-1]->[1] == HTML_EL and
6866                    @{$self->{open_elements}} == 1) { # redundant, maybe
6867              !!!cp ('t331.1');
6868              !!!parse-error (type => 'in body:#eof', token => $token);
6869            } else {
6870              !!!cp ('t331.2');
6871            }
6872            
6873            ## Stop parsing
6874            last B;
6875        } else {        } else {
6876          die "$0: $token->{type}: Unknown token type";          die "$0: $token->{type}: Unknown token type";
6877        }        }
6878      } elsif ($self->{insertion_mode} eq 'after frameset') {      } else {
6879        if ($token->{type} eq 'character') {        die "$0: $self->{insertion_mode}: Unknown insertion mode";
6880              if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {      }
               $self->{open_elements}->[-1]->[0]->manakai_append_text ($1);  
6881    
6882                unless (length $token->{data}) {      ## "in body" insertion mode
6883                  !!!next-token;      if ($token->{type} == START_TAG_TOKEN) {
6884                  redo B;        if ($token->{tag_name} eq 'script') {
6885            !!!cp ('t332');
6886            ## NOTE: This is an "as if in head" code clone
6887            $script_start_tag->();
6888            next B;
6889          } elsif ($token->{tag_name} eq 'style') {
6890            !!!cp ('t333');
6891            ## NOTE: This is an "as if in head" code clone
6892            $parse_rcdata->(CDATA_CONTENT_MODEL);
6893            next B;
6894          } elsif ({
6895                    base => 1, command => 1, eventsource => 1, link => 1,
6896                   }->{$token->{tag_name}}) {
6897            !!!cp ('t334');
6898            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6899            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6900            pop @{$self->{open_elements}};
6901            !!!ack ('t334.1');
6902            !!!next-token;
6903            next B;
6904          } elsif ($token->{tag_name} eq 'meta') {
6905            ## NOTE: This is an "as if in head" code clone, only "-t" differs
6906            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
6907            my $meta_el = pop @{$self->{open_elements}};
6908    
6909            unless ($self->{confident}) {
6910              if ($token->{attributes}->{charset}) {
6911                !!!cp ('t335');
6912                ## NOTE: Whether the encoding is supported or not is handled
6913                ## in the {change_encoding} callback.
6914                $self->{change_encoding}
6915                    ->($self, $token->{attributes}->{charset}->{value}, $token);
6916                
6917                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6918                    ->set_user_data (manakai_has_reference =>
6919                                         $token->{attributes}->{charset}
6920                                             ->{has_reference});
6921              } elsif ($token->{attributes}->{content}) {
6922                if ($token->{attributes}->{content}->{value}
6923                    =~ /[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
6924                        [\x09\x0A\x0C\x0D\x20]*=
6925                        [\x09\x0A\x0C\x0D\x20]*(?>"([^"]*)"|'([^']*)'|
6926                        ([^"'\x09\x0A\x0C\x0D\x20][^\x09\x0A\x0C\x0D\x20\x3B]*))
6927                       /x) {
6928                  !!!cp ('t336');
6929                  ## NOTE: Whether the encoding is supported or not is handled
6930                  ## in the {change_encoding} callback.
6931                  $self->{change_encoding}
6932                      ->($self, defined $1 ? $1 : defined $2 ? $2 : $3, $token);
6933                  $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6934                      ->set_user_data (manakai_has_reference =>
6935                                           $token->{attributes}->{content}
6936                                                 ->{has_reference});
6937                }
6938              }
6939            } else {
6940              if ($token->{attributes}->{charset}) {
6941                !!!cp ('t337');
6942                $meta_el->[0]->get_attribute_node_ns (undef, 'charset')
6943                    ->set_user_data (manakai_has_reference =>
6944                                         $token->{attributes}->{charset}
6945                                             ->{has_reference});
6946              }
6947              if ($token->{attributes}->{content}) {
6948                !!!cp ('t338');
6949                $meta_el->[0]->get_attribute_node_ns (undef, 'content')
6950                    ->set_user_data (manakai_has_reference =>
6951                                         $token->{attributes}->{content}
6952                                             ->{has_reference});
6953              }
6954            }
6955    
6956            !!!ack ('t338.1');
6957            !!!next-token;
6958            next B;
6959          } elsif ($token->{tag_name} eq 'title') {
6960            !!!cp ('t341');
6961            ## NOTE: This is an "as if in head" code clone
6962            $parse_rcdata->(RCDATA_CONTENT_MODEL);
6963            next B;
6964          } elsif ($token->{tag_name} eq 'body') {
6965            !!!parse-error (type => 'in body', text => 'body', token => $token);
6966                  
6967            if (@{$self->{open_elements}} == 1 or
6968                not ($self->{open_elements}->[1]->[1] == BODY_EL)) {
6969              !!!cp ('t342');
6970              ## Ignore the token
6971            } else {
6972              my $body_el = $self->{open_elements}->[1]->[0];
6973              for my $attr_name (keys %{$token->{attributes}}) {
6974                unless ($body_el->has_attribute_ns (undef, $attr_name)) {
6975                  !!!cp ('t343');
6976                  $body_el->set_attribute_ns
6977                    (undef, [undef, $attr_name],
6978                     $token->{attributes}->{$attr_name}->{value});
6979                }
6980              }
6981            }
6982            !!!nack ('t343.1');
6983            !!!next-token;
6984            next B;
6985          } elsif ({
6986                    ## NOTE: Start tags for non-phrasing flow content elements
6987    
6988                    ## NOTE: The normal one
6989                    address => 1, article => 1, aside => 1, blockquote => 1,
6990                    center => 1, datagrid => 1, details => 1, dialog => 1,
6991                    dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
6992                    footer => 1, h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1,
6993                    h6 => 1, header => 1, menu => 1, nav => 1, ol => 1, p => 1,
6994                    section => 1, ul => 1,
6995                    ## NOTE: As normal, but drops leading newline
6996                    pre => 1, listing => 1,
6997                    ## NOTE: As normal, but interacts with the form element pointer
6998                    form => 1,
6999                    
7000                    table => 1,
7001                    hr => 1,
7002                   }->{$token->{tag_name}}) {
7003            if ($token->{tag_name} eq 'form' and defined $self->{form_element}) {
7004              !!!cp ('t350');
7005              !!!parse-error (type => 'in form:form', token => $token);
7006              ## Ignore the token
7007              !!!nack ('t350.1');
7008              !!!next-token;
7009              next B;
7010            }
7011    
7012            ## has a p element in scope
7013            INSCOPE: for (reverse @{$self->{open_elements}}) {
7014              if ($_->[1] == P_EL) {
7015                !!!cp ('t344');
7016                !!!back-token; # <form>
7017                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7018                          line => $token->{line}, column => $token->{column}};
7019                next B;
7020              } elsif ($_->[1] & SCOPING_EL) {
7021                !!!cp ('t345');
7022                last INSCOPE;
7023              }
7024            } # INSCOPE
7025              
7026            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7027            if ($token->{tag_name} eq 'pre' or $token->{tag_name} eq 'listing') {
7028              !!!nack ('t346.1');
7029              !!!next-token;
7030              if ($token->{type} == CHARACTER_TOKEN) {
7031                $token->{data} =~ s/^\x0A//;
7032                unless (length $token->{data}) {
7033                  !!!cp ('t346');
7034                  !!!next-token;
7035                } else {
7036                  !!!cp ('t349');
7037                }
7038              } else {
7039                !!!cp ('t348');
7040              }
7041            } elsif ($token->{tag_name} eq 'form') {
7042              !!!cp ('t347.1');
7043              $self->{form_element} = $self->{open_elements}->[-1]->[0];
7044    
7045              !!!nack ('t347.2');
7046              !!!next-token;
7047            } elsif ($token->{tag_name} eq 'table') {
7048              !!!cp ('t382');
7049              push @{$open_tables}, [$self->{open_elements}->[-1]->[0]];
7050              
7051              $self->{insertion_mode} = IN_TABLE_IM;
7052    
7053              !!!nack ('t382.1');
7054              !!!next-token;
7055            } elsif ($token->{tag_name} eq 'hr') {
7056              !!!cp ('t386');
7057              pop @{$self->{open_elements}};
7058            
7059              !!!nack ('t386.1');
7060              !!!next-token;
7061            } else {
7062              !!!nack ('t347.1');
7063              !!!next-token;
7064            }
7065            next B;
7066          } elsif ($token->{tag_name} eq 'li') {
7067            ## NOTE: As normal, but imply </li> when there's another <li> ...
7068    
7069            ## NOTE: Special, Scope (<li><foo><li> == <li><foo><li/></foo></li>)
7070              ## Interpreted as <li><foo/></li><li/> (non-conforming)
7071              ## blockquote (O9.27), center (O), dd (Fx3, O, S3.1.2, IE7),
7072              ## dt (Fx, O, S, IE), dl (O), fieldset (O, S, IE), form (Fx, O, S),
7073              ## hn (O), pre (O), applet (O, S), button (O, S), marquee (Fx, O, S),
7074              ## object (Fx)
7075              ## Generate non-tree (non-conforming)
7076              ## basefont (IE7 (where basefont is non-void)), center (IE),
7077              ## form (IE), hn (IE)
7078            ## address, div, p (<li><foo><li> == <li><foo/></li><li/>)
7079              ## Interpreted as <li><foo><li/></foo></li> (non-conforming)
7080              ## div (Fx, S)
7081    
7082            my $non_optional;
7083            my $i = -1;
7084    
7085            ## 1.
7086            for my $node (reverse @{$self->{open_elements}}) {
7087              if ($node->[1] == LI_EL) {
7088                ## 2. (a) As if </li>
7089                {
7090                  ## If no </li> - not applied
7091                  #
7092    
7093                  ## Otherwise
7094    
7095                  ## 1. generate implied end tags, except for </li>
7096                  #
7097    
7098                  ## 2. If current node != "li", parse error
7099                  if ($non_optional) {
7100                    !!!parse-error (type => 'not closed',
7101                                    text => $non_optional->[0]->manakai_local_name,
7102                                    token => $token);
7103                    !!!cp ('t355');
7104                  } else {
7105                    !!!cp ('t356');
7106                }                }
7107    
7108                  ## 3. Pop
7109                  splice @{$self->{open_elements}}, $i;
7110              }              }
7111    
7112              if ($token->{data} =~ s/^[^\x09\x0A\x0B\x0C\x20]+//) {              last; ## 2. (b) goto 5.
7113                !!!parse-error (type => 'after frameset:#character');            } elsif (
7114                       ## NOTE: not "formatting" and not "phrasing"
7115                       ($node->[1] & SPECIAL_EL or
7116                        $node->[1] & SCOPING_EL) and
7117                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7118                       (not $node->[1] & ADDRESS_DIV_P_EL)
7119                      ) {
7120                ## 3.
7121                !!!cp ('t357');
7122                last; ## goto 5.
7123              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7124                !!!cp ('t358');
7125                #
7126              } else {
7127                !!!cp ('t359');
7128                $non_optional ||= $node;
7129                #
7130              }
7131              ## 4.
7132              ## goto 2.
7133              $i--;
7134            }
7135    
7136            ## 5. (a) has a |p| element in scope
7137            INSCOPE: for (reverse @{$self->{open_elements}}) {
7138              if ($_->[1] == P_EL) {
7139                !!!cp ('t353');
7140    
7141                ## NOTE: |<p><li>|, for example.
7142    
7143                ## Ignore the token.              !!!back-token; # <x>
7144                if (length $token->{data}) {              $token = {type => END_TAG_TOKEN, tag_name => 'p',
7145                  ## reprocess the rest of characters                        line => $token->{line}, column => $token->{column}};
7146                next B;
7147              } elsif ($_->[1] & SCOPING_EL) {
7148                !!!cp ('t354');
7149                last INSCOPE;
7150              }
7151            } # INSCOPE
7152    
7153            ## 5. (b) insert
7154            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7155            !!!nack ('t359.1');
7156            !!!next-token;
7157            next B;
7158          } elsif ($token->{tag_name} eq 'dt' or
7159                   $token->{tag_name} eq 'dd') {
7160            ## NOTE: As normal, but imply </dt> or </dd> when ...
7161    
7162            my $non_optional;
7163            my $i = -1;
7164    
7165            ## 1.
7166            for my $node (reverse @{$self->{open_elements}}) {
7167              if ($node->[1] == DT_EL or $node->[1] == DD_EL) {
7168                ## 2. (a) As if </li>
7169                {
7170                  ## If no </li> - not applied
7171                  #
7172    
7173                  ## Otherwise
7174    
7175                  ## 1. generate implied end tags, except for </dt> or </dd>
7176                  #
7177    
7178                  ## 2. If current node != "dt"|"dd", parse error
7179                  if ($non_optional) {
7180                    !!!parse-error (type => 'not closed',
7181                                    text => $non_optional->[0]->manakai_local_name,
7182                                    token => $token);
7183                    !!!cp ('t355.1');
7184                } else {                } else {
7185                  !!!next-token;                  !!!cp ('t356.1');
7186                }                }
7187                redo B;  
7188                  ## 3. Pop
7189                  splice @{$self->{open_elements}}, $i;
7190              }              }
7191    
7192          die qq[$0: Character "$token->{data}"];              last; ## 2. (b) goto 5.
7193        } elsif ($token->{type} eq 'start tag') {            } elsif (
7194          if ($token->{tag_name} eq 'noframes') {                     ## NOTE: not "formatting" and not "phrasing"
7195            $in_body->($insert_to_current);                     ($node->[1] & SPECIAL_EL or
7196            redo B;                      $node->[1] & SCOPING_EL) and
7197                       ## NOTE: "li", "dt", and "dd" are in |SPECIAL_EL|.
7198    
7199                       (not $node->[1] & ADDRESS_DIV_P_EL)
7200                      ) {
7201                ## 3.
7202                !!!cp ('t357.1');
7203                last; ## goto 5.
7204              } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7205                !!!cp ('t358.1');
7206                #
7207              } else {
7208                !!!cp ('t359.1');
7209                $non_optional ||= $node;
7210                #
7211              }
7212              ## 4.
7213              ## goto 2.
7214              $i--;
7215            }
7216    
7217            ## 5. (a) has a |p| element in scope
7218            INSCOPE: for (reverse @{$self->{open_elements}}) {
7219              if ($_->[1] == P_EL) {
7220                !!!cp ('t353.1');
7221                !!!back-token; # <x>
7222                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7223                          line => $token->{line}, column => $token->{column}};
7224                next B;
7225              } elsif ($_->[1] & SCOPING_EL) {
7226                !!!cp ('t354.1');
7227                last INSCOPE;
7228              }
7229            } # INSCOPE
7230    
7231            ## 5. (b) insert
7232            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7233            !!!nack ('t359.2');
7234            !!!next-token;
7235            next B;
7236          } elsif ($token->{tag_name} eq 'plaintext') {
7237            ## NOTE: As normal, but effectively ends parsing
7238    
7239            ## has a p element in scope
7240            INSCOPE: for (reverse @{$self->{open_elements}}) {
7241              if ($_->[1] == P_EL) {
7242                !!!cp ('t367');
7243                !!!back-token; # <plaintext>
7244                $token = {type => END_TAG_TOKEN, tag_name => 'p',
7245                          line => $token->{line}, column => $token->{column}};
7246                next B;
7247              } elsif ($_->[1] & SCOPING_EL) {
7248                !!!cp ('t368');
7249                last INSCOPE;
7250              }
7251            } # INSCOPE
7252              
7253            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7254              
7255            $self->{content_model} = PLAINTEXT_CONTENT_MODEL;
7256              
7257            !!!nack ('t368.1');
7258            !!!next-token;
7259            next B;
7260          } elsif ($token->{tag_name} eq 'a') {
7261            AFE: for my $i (reverse 0..$#$active_formatting_elements) {
7262              my $node = $active_formatting_elements->[$i];
7263              if ($node->[1] == A_EL) {
7264                !!!cp ('t371');
7265                !!!parse-error (type => 'in a:a', token => $token);
7266                
7267                !!!back-token; # <a>
7268                $token = {type => END_TAG_TOKEN, tag_name => 'a',
7269                          line => $token->{line}, column => $token->{column}};
7270                $formatting_end_tag->($token);
7271                
7272                AFE2: for (reverse 0..$#$active_formatting_elements) {
7273                  if ($active_formatting_elements->[$_]->[0] eq $node->[0]) {
7274                    !!!cp ('t372');
7275                    splice @$active_formatting_elements, $_, 1;
7276                    last AFE2;
7277                  }
7278                } # AFE2
7279                OE: for (reverse 0..$#{$self->{open_elements}}) {
7280                  if ($self->{open_elements}->[$_]->[0] eq $node->[0]) {
7281                    !!!cp ('t373');
7282                    splice @{$self->{open_elements}}, $_, 1;
7283                    last OE;
7284                  }
7285                } # OE
7286                last AFE;
7287              } elsif ($node->[0] eq '#marker') {
7288                !!!cp ('t374');
7289                last AFE;
7290              }
7291            } # AFE
7292              
7293            $reconstruct_active_formatting_elements->($insert_to_current);
7294    
7295            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7296            push @$active_formatting_elements, $self->{open_elements}->[-1];
7297    
7298            !!!nack ('t374.1');
7299            !!!next-token;
7300            next B;
7301          } elsif ($token->{tag_name} eq 'nobr') {
7302            $reconstruct_active_formatting_elements->($insert_to_current);
7303    
7304            ## has a |nobr| element in scope
7305            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7306              my $node = $self->{open_elements}->[$_];
7307              if ($node->[1] == NOBR_EL) {
7308                !!!cp ('t376');
7309                !!!parse-error (type => 'in nobr:nobr', token => $token);
7310                !!!back-token; # <nobr>
7311                $token = {type => END_TAG_TOKEN, tag_name => 'nobr',
7312                          line => $token->{line}, column => $token->{column}};
7313                next B;
7314              } elsif ($node->[1] & SCOPING_EL) {
7315                !!!cp ('t377');
7316                last INSCOPE;
7317              }
7318            } # INSCOPE
7319            
7320            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7321            push @$active_formatting_elements, $self->{open_elements}->[-1];
7322            
7323            !!!nack ('t377.1');
7324            !!!next-token;
7325            next B;
7326          } elsif ($token->{tag_name} eq 'button') {
7327            ## has a button element in scope
7328            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7329              my $node = $self->{open_elements}->[$_];
7330              if ($node->[1] == BUTTON_EL) {
7331                !!!cp ('t378');
7332                !!!parse-error (type => 'in button:button', token => $token);
7333                !!!back-token; # <button>
7334                $token = {type => END_TAG_TOKEN, tag_name => 'button',
7335                          line => $token->{line}, column => $token->{column}};
7336                next B;
7337              } elsif ($node->[1] & SCOPING_EL) {
7338                !!!cp ('t379');
7339                last INSCOPE;
7340              }
7341            } # INSCOPE
7342              
7343            $reconstruct_active_formatting_elements->($insert_to_current);
7344              
7345            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7346    
7347            ## TODO: associate with $self->{form_element} if defined
7348    
7349            push @$active_formatting_elements, ['#marker', ''];
7350    
7351            !!!nack ('t379.1');
7352            !!!next-token;
7353            next B;
7354          } elsif ({
7355                    xmp => 1,
7356                    iframe => 1,
7357                    noembed => 1,
7358                    noframes => 1, ## NOTE: This is an "as if in head" code clone.
7359                    noscript => 0, ## TODO: 1 if scripting is enabled
7360                   }->{$token->{tag_name}}) {
7361            if ($token->{tag_name} eq 'xmp') {
7362              !!!cp ('t381');
7363              $reconstruct_active_formatting_elements->($insert_to_current);
7364          } else {          } else {
7365            !!!parse-error (type => 'after frameset:'.$token->{tag_name});            !!!cp ('t399');
7366            }
7367            ## NOTE: There is an "as if in body" code clone.
7368            $parse_rcdata->(CDATA_CONTENT_MODEL);
7369            next B;
7370          } elsif ($token->{tag_name} eq 'isindex') {
7371            !!!parse-error (type => 'isindex', token => $token);
7372            
7373            if (defined $self->{form_element}) {
7374              !!!cp ('t389');
7375            ## Ignore the token            ## Ignore the token
7376              !!!nack ('t389'); ## NOTE: Not acknowledged.
7377              !!!next-token;
7378              next B;
7379            } else {
7380              !!!ack ('t391.1');
7381    
7382              my $at = $token->{attributes};
7383              my $form_attrs;
7384              $form_attrs->{action} = $at->{action} if $at->{action};
7385              my $prompt_attr = $at->{prompt};
7386              $at->{name} = {name => 'name', value => 'isindex'};
7387              delete $at->{action};
7388              delete $at->{prompt};
7389              my @tokens = (
7390                            {type => START_TAG_TOKEN, tag_name => 'form',
7391                             attributes => $form_attrs,
7392                             line => $token->{line}, column => $token->{column}},
7393                            {type => START_TAG_TOKEN, tag_name => 'hr',
7394                             line => $token->{line}, column => $token->{column}},
7395                            {type => START_TAG_TOKEN, tag_name => 'p',
7396                             line => $token->{line}, column => $token->{column}},
7397                            {type => START_TAG_TOKEN, tag_name => 'label',
7398                             line => $token->{line}, column => $token->{column}},
7399                           );
7400              if ($prompt_attr) {
7401                !!!cp ('t390');
7402                push @tokens, {type => CHARACTER_TOKEN, data => $prompt_attr->{value},
7403                               #line => $token->{line}, column => $token->{column},
7404                              };
7405              } else {
7406                !!!cp ('t391');
7407                push @tokens, {type => CHARACTER_TOKEN,
7408                               data => 'This is a searchable index. Insert your search keywords here: ',
7409                               #line => $token->{line}, column => $token->{column},
7410                              }; # SHOULD
7411                ## TODO: make this configurable
7412              }
7413              push @tokens,
7414                            {type => START_TAG_TOKEN, tag_name => 'input', attributes => $at,
7415                             line => $token->{line}, column => $token->{column}},
7416                            #{type => CHARACTER_TOKEN, data => ''}, # SHOULD
7417                            {type => END_TAG_TOKEN, tag_name => 'label',
7418                             line => $token->{line}, column => $token->{column}},
7419                            {type => END_TAG_TOKEN, tag_name => 'p',
7420                             line => $token->{line}, column => $token->{column}},
7421                            {type => START_TAG_TOKEN, tag_name => 'hr',
7422                             line => $token->{line}, column => $token->{column}},
7423                            {type => END_TAG_TOKEN, tag_name => 'form',
7424                             line => $token->{line}, column => $token->{column}};
7425              !!!back-token (@tokens);
7426            !!!next-token;            !!!next-token;
7427            redo B;            next B;
7428          }          }
7429        } elsif ($token->{type} eq 'end tag') {        } elsif ($token->{tag_name} eq 'textarea') {
7430          if ($token->{tag_name} eq 'html') {          ## Step 1
7431            $previous_insertion_mode = $self->{insertion_mode};          !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7432            $self->{insertion_mode} = 'trailing end';          
7433            ## Step 2
7434            ## TODO: $self->{form_element} if defined
7435    
7436            ## Step 3
7437            $self->{ignore_newline} = 1;
7438    
7439            ## Step 4
7440            ## ISSUE: This step is wrong. (r2302 enbugged)
7441    
7442            ## Step 5
7443            $self->{content_model} = RCDATA_CONTENT_MODEL;
7444            delete $self->{escape}; # MUST
7445    
7446            ## Step 6-7
7447            $self->{insertion_mode} |= IN_CDATA_RCDATA_IM;
7448    
7449            !!!nack ('t392.1');
7450            !!!next-token;
7451            next B;
7452          } elsif ($token->{tag_name} eq 'optgroup' or
7453                   $token->{tag_name} eq 'option') {
7454            ## has an |option| element in scope
7455            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7456              my $node = $self->{open_elements}->[$_];
7457              if ($node->[1] == OPTION_EL) {
7458                !!!cp ('t397.1');
7459                ## NOTE: As if </option>
7460                !!!back-token; # <option> or <optgroup>
7461                $token = {type => END_TAG_TOKEN, tag_name => 'option',
7462                          line => $token->{line}, column => $token->{column}};
7463                next B;
7464              } elsif ($node->[1] & SCOPING_EL) {
7465                !!!cp ('t397.2');
7466                last INSCOPE;
7467              }
7468            } # INSCOPE
7469    
7470            $reconstruct_active_formatting_elements->($insert_to_current);
7471    
7472            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7473    
7474            !!!nack ('t397.3');
7475            !!!next-token;
7476            redo B;
7477          } elsif ($token->{tag_name} eq 'rt' or
7478                   $token->{tag_name} eq 'rp') {
7479            ## has a |ruby| element in scope
7480            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7481              my $node = $self->{open_elements}->[$_];
7482              if ($node->[1] == RUBY_EL) {
7483                !!!cp ('t398.1');
7484                ## generate implied end tags
7485                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7486                  !!!cp ('t398.2');
7487                  pop @{$self->{open_elements}};
7488                }
7489                unless ($self->{open_elements}->[-1]->[1] == RUBY_EL) {
7490                  !!!cp ('t398.3');
7491                  !!!parse-error (type => 'not closed',
7492                                  text => $self->{open_elements}->[-1]->[0]
7493                                      ->manakai_local_name,
7494                                  token => $token);
7495                  pop @{$self->{open_elements}}
7496                      while not $self->{open_elements}->[-1]->[1] == RUBY_EL;
7497                }
7498                last INSCOPE;
7499              } elsif ($node->[1] & SCOPING_EL) {
7500                !!!cp ('t398.4');
7501                last INSCOPE;
7502              }
7503            } # INSCOPE
7504    
7505            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7506    
7507            !!!nack ('t398.5');
7508            !!!next-token;
7509            redo B;
7510          } elsif ($token->{tag_name} eq 'math' or
7511                   $token->{tag_name} eq 'svg') {
7512            $reconstruct_active_formatting_elements->($insert_to_current);
7513    
7514            ## "Adjust MathML attributes" ('math' only) - done in insert-element-f
7515    
7516            ## "adjust SVG attributes" ('svg' only) - done in insert-element-f
7517    
7518            ## "adjust foreign attributes" - done in insert-element-f
7519            
7520            !!!insert-element-f ($token->{tag_name} eq 'math' ? $MML_NS : $SVG_NS, $token->{tag_name}, $token->{attributes}, $token);
7521            
7522            if ($self->{self_closing}) {
7523              pop @{$self->{open_elements}};
7524              !!!ack ('t398.6');
7525            } else {
7526              !!!cp ('t398.7');
7527              $self->{insertion_mode} |= IN_FOREIGN_CONTENT_IM;
7528              ## NOTE: |<body><math><mi><svg>| -> "in foreign content" insertion
7529              ## mode, "in body" (not "in foreign content") secondary insertion
7530              ## mode, maybe.
7531            }
7532    
7533            !!!next-token;
7534            next B;
7535          } elsif ({
7536                    caption => 1, col => 1, colgroup => 1, frame => 1,
7537                    frameset => 1, head => 1,
7538                    tbody => 1, td => 1, tfoot => 1, th => 1,
7539                    thead => 1, tr => 1,
7540                   }->{$token->{tag_name}}) {
7541            !!!cp ('t401');
7542            !!!parse-error (type => 'in body',
7543                            text => $token->{tag_name}, token => $token);
7544            ## Ignore the token
7545            !!!nack ('t401.1'); ## NOTE: |<col/>| or |<frame/>| here is an error.
7546            !!!next-token;
7547            next B;
7548          } elsif ($token->{tag_name} eq 'param' or
7549                   $token->{tag_name} eq 'source') {
7550            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7551            pop @{$self->{open_elements}};
7552    
7553            !!!ack ('t398.5');
7554            !!!next-token;
7555            redo B;
7556          } else {
7557            if ($token->{tag_name} eq 'image') {
7558              !!!cp ('t384');
7559              !!!parse-error (type => 'image', token => $token);
7560              $token->{tag_name} = 'img';
7561            } else {
7562              !!!cp ('t385');
7563            }
7564    
7565            ## NOTE: There is an "as if <br>" code clone.
7566            $reconstruct_active_formatting_elements->($insert_to_current);
7567            
7568            !!!insert-element-t ($token->{tag_name}, $token->{attributes}, $token);
7569    
7570            if ({
7571                 applet => 1, marquee => 1, object => 1,
7572                }->{$token->{tag_name}}) {
7573              !!!cp ('t380');
7574              push @$active_formatting_elements, ['#marker', ''];
7575              !!!nack ('t380.1');
7576            } elsif ({
7577                      b => 1, big => 1, em => 1, font => 1, i => 1,
7578                      s => 1, small => 1, strike => 1,
7579                      strong => 1, tt => 1, u => 1,
7580                     }->{$token->{tag_name}}) {
7581              !!!cp ('t375');
7582              push @$active_formatting_elements, $self->{open_elements}->[-1];
7583              !!!nack ('t375.1');
7584            } elsif ($token->{tag_name} eq 'input') {
7585              !!!cp ('t388');
7586              ## TODO: associate with $self->{form_element} if defined
7587              pop @{$self->{open_elements}};
7588              !!!ack ('t388.2');
7589            } elsif ({
7590                      area => 1, basefont => 1, bgsound => 1, br => 1,
7591                      embed => 1, img => 1, spacer => 1, wbr => 1,
7592                     }->{$token->{tag_name}}) {
7593              !!!cp ('t388.1');
7594              pop @{$self->{open_elements}};
7595              !!!ack ('t388.3');
7596            } elsif ($token->{tag_name} eq 'select') {
7597              ## TODO: associate with $self->{form_element} if defined
7598            
7599              if ($self->{insertion_mode} & TABLE_IMS or
7600                  $self->{insertion_mode} & BODY_TABLE_IMS or
7601                  $self->{insertion_mode} == IN_COLUMN_GROUP_IM) {
7602                !!!cp ('t400.1');
7603                $self->{insertion_mode} = IN_SELECT_IN_TABLE_IM;
7604              } else {
7605                !!!cp ('t400.2');
7606                $self->{insertion_mode} = IN_SELECT_IM;
7607              }
7608              !!!nack ('t400.3');
7609            } else {
7610              !!!nack ('t402');
7611            }
7612            
7613            !!!next-token;
7614            next B;
7615          }
7616        } elsif ($token->{type} == END_TAG_TOKEN) {
7617          if ($token->{tag_name} eq 'body') {
7618            ## has a |body| element in scope
7619            my $i;
7620            INSCOPE: {
7621              for (reverse @{$self->{open_elements}}) {
7622                if ($_->[1] == BODY_EL) {
7623                  !!!cp ('t405');
7624                  $i = $_;
7625                  last INSCOPE;
7626                } elsif ($_->[1] & SCOPING_EL) {
7627                  !!!cp ('t405.1');
7628                  last;
7629                }
7630              }
7631    
7632              ## NOTE: |<marquee></body>|, |<svg><foreignobject></body>|
7633    
7634              !!!parse-error (type => 'unmatched end tag',
7635                              text => $token->{tag_name}, token => $token);
7636              ## NOTE: Ignore the token.
7637            !!!next-token;            !!!next-token;
7638            redo B;            next B;
7639            } # INSCOPE
7640    
7641            for (@{$self->{open_elements}}) {
7642              unless ($_->[1] & ALL_END_TAG_OPTIONAL_EL) {
7643                !!!cp ('t403');
7644                !!!parse-error (type => 'not closed',
7645                                text => $_->[0]->manakai_local_name,
7646                                token => $token);
7647                last;
7648              } else {
7649                !!!cp ('t404');
7650              }
7651            }
7652    
7653            $self->{insertion_mode} = AFTER_BODY_IM;
7654            !!!next-token;
7655            next B;
7656          } elsif ($token->{tag_name} eq 'html') {
7657            ## TODO: Update this code.  It seems that the code below is not
7658            ## up-to-date, though it has same effect as speced.
7659            if (@{$self->{open_elements}} > 1 and
7660                $self->{open_elements}->[1]->[1] == BODY_EL) {
7661              unless ($self->{open_elements}->[-1]->[1] == BODY_EL) {
7662                !!!cp ('t406');
7663                !!!parse-error (type => 'not closed',
7664                                text => $self->{open_elements}->[1]->[0]
7665                                    ->manakai_local_name,
7666                                token => $token);
7667              } else {
7668                !!!cp ('t407');
7669              }
7670              $self->{insertion_mode} = AFTER_BODY_IM;
7671              ## reprocess
7672              next B;
7673          } else {          } else {
7674            !!!parse-error (type => 'after frameset:/'.$token->{tag_name});            !!!cp ('t408');
7675              !!!parse-error (type => 'unmatched end tag',
7676                              text => $token->{tag_name}, token => $token);
7677            ## Ignore the token            ## Ignore the token
7678            !!!next-token;            !!!next-token;
7679            redo B;            next B;
7680          }          }
7681        } else {        } elsif ({
7682          die "$0: $token->{type}: Unknown token type";                  ## NOTE: End tags for non-phrasing flow content elements
       }  
7683    
7684        ## ISSUE: An issue in spec here                  ## NOTE: The normal ones
7685      } elsif ($self->{insertion_mode} eq 'trailing end') {                  address => 1, article => 1, aside => 1, blockquote => 1,
7686        ## states in the main stage is preserved yet # MUST                  center => 1, datagrid => 1, details => 1, dialog => 1,
7687                          dir => 1, div => 1, dl => 1, fieldset => 1, figure => 1,
7688        if ($token->{type} eq 'character') {                  footer => 1, header => 1, listing => 1, menu => 1, nav => 1,
7689          if ($token->{data} =~ s/^([\x09\x0A\x0B\x0C\x20]+)//) {                  ol => 1, pre => 1, section => 1, ul => 1,
7690            my $data = $1;  
7691            ## As if in the main phase.                  ## NOTE: As normal, but ... optional tags
7692            ## NOTE: The insertion mode in the main phase                  dd => 1, dt => 1, li => 1,
7693            ## just before the phase has been changed to the trailing  
7694            ## end phase is either "after body" or "after frameset".                  applet => 1, button => 1, marquee => 1, object => 1,
7695            $reconstruct_active_formatting_elements->($insert_to_current);                 }->{$token->{tag_name}}) {
7696            ## NOTE: Code for <li> start tags includes "as if </li>" code.
7697            ## Code for <dt> or <dd> start tags includes "as if </dt> or
7698            ## </dd>" code.
7699    
7700            ## has an element in scope
7701            my $i;
7702            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7703              my $node = $self->{open_elements}->[$_];
7704              if ($node->[0]->manakai_local_name eq $token->{tag_name}) {
7705                !!!cp ('t410');
7706                $i = $_;
7707                last INSCOPE;
7708              } elsif ($node->[1] & SCOPING_EL) {
7709                !!!cp ('t411');
7710                last INSCOPE;
7711              }
7712            } # INSCOPE
7713    
7714            unless (defined $i) { # has an element in scope
7715              !!!cp ('t413');
7716              !!!parse-error (type => 'unmatched end tag',
7717                              text => $token->{tag_name}, token => $token);
7718              ## NOTE: Ignore the token.
7719            } else {
7720              ## Step 1. generate implied end tags
7721              while ({
7722                      ## END_TAG_OPTIONAL_EL
7723                      dd => ($token->{tag_name} ne 'dd'),
7724                      dt => ($token->{tag_name} ne 'dt'),
7725                      li => ($token->{tag_name} ne 'li'),
7726                      option => 1,
7727                      optgroup => 1,
7728                      p => 1,
7729                      rt => 1,
7730                      rp => 1,
7731                     }->{$self->{open_elements}->[-1]->[0]->manakai_local_name}) {
7732                !!!cp ('t409');
7733                pop @{$self->{open_elements}};
7734              }
7735    
7736              ## Step 2.
7737              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7738                      ne $token->{tag_name}) {
7739                !!!cp ('t412');
7740                !!!parse-error (type => 'not closed',
7741                                text => $self->{open_elements}->[-1]->[0]
7742                                    ->manakai_local_name,
7743                                token => $token);
7744              } else {
7745                !!!cp ('t414');
7746              }
7747    
7748              ## Step 3.
7749              splice @{$self->{open_elements}}, $i;
7750    
7751              ## Step 4.
7752              $clear_up_to_marker->()
7753                  if {
7754                    applet => 1, button => 1, marquee => 1, object => 1,
7755                  }->{$token->{tag_name}};
7756            }
7757            !!!next-token;
7758            next B;
7759          } elsif ($token->{tag_name} eq 'form') {
7760            ## NOTE: As normal, but interacts with the form element pointer
7761    
7762            undef $self->{form_element};
7763    
7764            ## has an element in scope
7765            my $i;
7766            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7767              my $node = $self->{open_elements}->[$_];
7768              if ($node->[1] == FORM_EL) {
7769                !!!cp ('t418');
7770                $i = $_;
7771                last INSCOPE;
7772              } elsif ($node->[1] & SCOPING_EL) {
7773                !!!cp ('t419');
7774                last INSCOPE;
7775              }
7776            } # INSCOPE
7777    
7778            unless (defined $i) { # has an element in scope
7779              !!!cp ('t421');
7780              !!!parse-error (type => 'unmatched end tag',
7781                              text => $token->{tag_name}, token => $token);
7782              ## NOTE: Ignore the token.
7783            } else {
7784              ## Step 1. generate implied end tags
7785              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7786                !!!cp ('t417');
7787                pop @{$self->{open_elements}};
7788              }
7789                        
7790            $self->{open_elements}->[-1]->[0]->manakai_append_text ($data);            ## Step 2.
7791              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7792                      ne $token->{tag_name}) {
7793                !!!cp ('t417.1');
7794                !!!parse-error (type => 'not closed',
7795                                text => $self->{open_elements}->[-1]->[0]
7796                                    ->manakai_local_name,
7797                                token => $token);
7798              } else {
7799                !!!cp ('t420');
7800              }  
7801                        
7802            unless (length $token->{data}) {            ## Step 3.
7803              !!!next-token;            splice @{$self->{open_elements}}, $i;
7804              redo B;          }
7805    
7806            !!!next-token;
7807            next B;
7808          } elsif ({
7809                    ## NOTE: As normal, except acts as a closer for any ...
7810                    h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1,
7811                   }->{$token->{tag_name}}) {
7812            ## has an element in scope
7813            my $i;
7814            INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7815              my $node = $self->{open_elements}->[$_];
7816              if ($node->[1] == HEADING_EL) {
7817                !!!cp ('t423');
7818                $i = $_;
7819                last INSCOPE;
7820              } elsif ($node->[1] & SCOPING_EL) {
7821                !!!cp ('t424');
7822                last INSCOPE;
7823              }
7824            } # INSCOPE
7825    
7826            unless (defined $i) { # has an element in scope
7827              !!!cp ('t425.1');
7828              !!!parse-error (type => 'unmatched end tag',
7829                              text => $token->{tag_name}, token => $token);
7830              ## NOTE: Ignore the token.
7831            } else {
7832              ## Step 1. generate implied end tags
7833              while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7834                !!!cp ('t422');
7835                pop @{$self->{open_elements}};
7836              }
7837              
7838              ## Step 2.
7839              if ($self->{open_elements}->[-1]->[0]->manakai_local_name
7840                      ne $token->{tag_name}) {
7841                !!!cp ('t425');
7842                !!!parse-error (type => 'unmatched end tag',
7843                                text => $token->{tag_name}, token => $token);
7844              } else {
7845                !!!cp ('t426');
7846            }            }
7847    
7848              ## Step 3.
7849              splice @{$self->{open_elements}}, $i;
7850          }          }
7851            
7852            !!!next-token;
7853            next B;
7854          } elsif ($token->{tag_name} eq 'p') {
7855            ## NOTE: As normal, except </p> implies <p> and ...
7856    
7857          !!!parse-error (type => 'after html:#character');          ## has an element in scope
7858          $self->{insertion_mode} = $previous_insertion_mode;          my $non_optional;
7859          ## reprocess          my $i;
7860          redo B;          INSCOPE: for (reverse 0..$#{$self->{open_elements}}) {
7861        } elsif ($token->{type} eq 'start tag') {            my $node = $self->{open_elements}->[$_];
7862          !!!parse-error (type => 'after html:'.$token->{tag_name});            if ($node->[1] == P_EL) {
7863          $self->{insertion_mode} = $previous_insertion_mode;              !!!cp ('t410.1');
7864          ## reprocess              $i = $_;
7865          redo B;              last INSCOPE;
7866        } elsif ($token->{type} eq 'end tag') {            } elsif ($node->[1] & SCOPING_EL) {
7867          !!!parse-error (type => 'after html:/'.$token->{tag_name});              !!!cp ('t411.1');
7868          $self->{insertion_mode} = $previous_insertion_mode;              last INSCOPE;
7869          ## reprocess            } elsif ($node->[1] & END_TAG_OPTIONAL_EL) {
7870          redo B;              ## NOTE: |END_TAG_OPTIONAL_EL| includes "p"
7871                !!!cp ('t411.2');
7872                #
7873              } else {
7874                !!!cp ('t411.3');
7875                $non_optional ||= $node;
7876                #
7877              }
7878            } # INSCOPE
7879    
7880            if (defined $i) {
7881              ## 1. Generate implied end tags
7882              #
7883    
7884              ## 2. If current node != "p", parse error
7885              if ($non_optional) {
7886                !!!cp ('t412.1');
7887                !!!parse-error (type => 'not closed',
7888                                text => $non_optional->[0]->manakai_local_name,
7889                                token => $token);
7890              } else {
7891                !!!cp ('t414.1');
7892              }
7893    
7894              ## 3. Pop
7895              splice @{$self->{open_elements}}, $i;
7896            } else {
7897              !!!cp ('t413.1');
7898              !!!parse-error (type => 'unmatched end tag',
7899                              text => $token->{tag_name}, token => $token);
7900    
7901              !!!cp ('t415.1');
7902              ## As if <p>, then reprocess the current token
7903              my $el;
7904              !!!create-element ($el, $HTML_NS, 'p',, $token);
7905              $insert->($el);
7906              ## NOTE: Not inserted into |$self->{open_elements}|.
7907            }
7908    
7909            !!!next-token;
7910            next B;
7911          } elsif ({
7912                    a => 1,
7913                    b => 1, big => 1, em => 1, font => 1, i => 1,
7914                    nobr => 1, s => 1, small => 1, strike => 1,
7915                    strong => 1, tt => 1, u => 1,
7916                   }->{$token->{tag_name}}) {
7917            !!!cp ('t427');
7918            $formatting_end_tag->($token);
7919            next B;
7920          } elsif ($token->{tag_name} eq 'br') {
7921            !!!cp ('t428');
7922            !!!parse-error (type => 'unmatched end tag',
7923                            text => 'br', token => $token);
7924    
7925            ## As if <br>
7926            $reconstruct_active_formatting_elements->($insert_to_current);
7927            
7928            my $el;
7929            !!!create-element ($el, $HTML_NS, 'br',, $token);
7930            $insert->($el);
7931            
7932            ## Ignore the token.
7933            !!!next-token;
7934            next B;
7935        } else {        } else {
7936          die "$0: $token->{type}: Unknown token";          if ($token->{tag_name} eq 'sarcasm') {
7937              sleep 0.001; # take a deep breath
7938            }
7939    
7940            ## Step 1
7941            my $node_i = -1;
7942            my $node = $self->{open_elements}->[$node_i];
7943    
7944            ## Step 2
7945            S2: {
7946              my $node_tag_name = $node->[0]->manakai_local_name;
7947              $node_tag_name =~ tr/A-Z/a-z/; # for SVG camelCase tag names
7948              if ($node_tag_name eq $token->{tag_name}) {
7949                ## Step 1
7950                ## generate implied end tags
7951                while ($self->{open_elements}->[-1]->[1] & END_TAG_OPTIONAL_EL) {
7952                  !!!cp ('t430');
7953                  ## NOTE: |<ruby><rt></ruby>|.
7954                  ## ISSUE: <ruby><rt></rt> will also take this code path,
7955                  ## which seems wrong.
7956                  pop @{$self->{open_elements}};
7957                  $node_i++;
7958                }
7959            
7960                ## Step 2
7961                my $current_tag_name
7962                    = $self->{open_elements}->[-1]->[0]->manakai_local_name;
7963                $current_tag_name =~ tr/A-Z/a-z/;
7964                if ($current_tag_name ne $token->{tag_name}) {
7965                  !!!cp ('t431');
7966                  ## NOTE: <x><y></x>
7967                  !!!parse-error (type => 'not closed',
7968                                  text => $self->{open_elements}->[-1]->[0]
7969                                      ->manakai_local_name,
7970                                  token => $token);
7971                } else {
7972                  !!!cp ('t432');
7973                }
7974                
7975                ## Step 3
7976                splice @{$self->{open_elements}}, $node_i if $node_i < 0;
7977    
7978                !!!next-token;
7979                last S2;
7980              } else {
7981                ## Step 3
7982                if (not ($node->[1] & FORMATTING_EL) and
7983                    #not $phrasing_category->{$node->[1]} and
7984                    ($node->[1] & SPECIAL_EL or
7985                     $node->[1] & SCOPING_EL)) {
7986                  !!!cp ('t433');
7987                  !!!parse-error (type => 'unmatched end tag',
7988                                  text => $token->{tag_name}, token => $token);
7989                  ## Ignore the token
7990                  !!!next-token;
7991                  last S2;
7992    
7993                  ## NOTE: |<span><dd></span>a|: In Safari 3.1.2 and Opera
7994                  ## 9.27, "a" is a child of <dd> (conforming).  In
7995                  ## Firefox 3.0.2, "a" is a child of <body>.  In WinIE 7,
7996                  ## "a" is a child of both <body> and <dd>.
7997                }
7998                
7999                !!!cp ('t434');
8000              }
8001              
8002              ## Step 4
8003              $node_i--;
8004              $node = $self->{open_elements}->[$node_i];
8005              
8006              ## Step 5;
8007              redo S2;
8008            } # S2
8009            next B;
8010        }        }
8011      } else {      }
8012        die "$0: $self->{insertion_mode}: Unknown insertion mode";      next B;
8013      } continue { # B
8014        if ($self->{insertion_mode} & IN_FOREIGN_CONTENT_IM) {
8015          ## NOTE: The code below is executed in cases where it does not have
8016          ## to be, but it it is harmless even in those cases.
8017          ## has an element in scope
8018          INSCOPE: {
8019            for (reverse 0..$#{$self->{open_elements}}) {
8020              my $node = $self->{open_elements}->[$_];
8021              if ($node->[1] & FOREIGN_EL) {
8022                last INSCOPE;
8023              } elsif ($node->[1] & SCOPING_EL) {
8024                last;
8025              }
8026            }
8027            
8028            ## NOTE: No foreign element in scope.
8029            $self->{insertion_mode} &= ~ IN_FOREIGN_CONTENT_IM;
8030          } # INSCOPE
8031      }      }
8032    } # B    } # B
8033    
# Line 5207  sub _tree_construction_main ($) { Line 8036  sub _tree_construction_main ($) {
8036    ## TODO: script stuffs    ## TODO: script stuffs
8037  } # _tree_construct_main  } # _tree_construct_main
8038    
8039  sub set_inner_html ($$$) {  sub set_inner_html ($$$$;$) {
8040    my $class = shift;    my $class = shift;
8041    my $node = shift;    my $node = shift;
8042    my $s = \$_[0];    #my $s = \$_[0];
8043    my $onerror = $_[1];    my $onerror = $_[1];
8044      my $get_wrapper = $_[2] || sub ($) { return $_[0] };
8045    
8046      ## ISSUE: Should {confident} be true?
8047    
8048    my $nt = $node->node_type;    my $nt = $node->node_type;
8049    if ($nt == 9) {    if ($nt == 9) {
# Line 5228  sub set_inner_html ($$$) { Line 8060  sub set_inner_html ($$$) {
8060      }      }
8061    
8062      ## Step 3, 4, 5 # MUST      ## Step 3, 4, 5 # MUST
8063      $class->parse_string ($$s => $node, $onerror);      $class->parse_char_string ($_[0] => $node, $onerror, $get_wrapper);
8064    } elsif ($nt == 1) {    } elsif ($nt == 1) {
8065      ## TODO: If non-html element      ## TODO: If non-html element
8066    
8067      ## NOTE: Most of this code is copied from |parse_string|      ## NOTE: Most of this code is copied from |parse_string|
8068    
8069    ## TODO: Support for $get_wrapper
8070    
8071      ## Step 1 # MUST      ## Step 1 # MUST
8072      my $this_doc = $node->owner_document;      my $this_doc = $node->owner_document;
8073      my $doc = $this_doc->implementation->create_document;      my $doc = $this_doc->implementation->create_document;
# Line 5241  sub set_inner_html ($$$) { Line 8075  sub set_inner_html ($$$) {
8075      my $p = $class->new;      my $p = $class->new;
8076      $p->{document} = $doc;      $p->{document} = $doc;
8077    
8078      ## Step 9 # MUST      ## Step 8 # MUST
8079      my $i = 0;      my $i = 0;
8080      my $line = 1;      $p->{line_prev} = $p->{line} = 1;
8081      my $column = 0;      $p->{column_prev} = $p->{column} = 0;
8082      $p->{set_next_input_character} = sub {      require Whatpm::Charset::DecodeHandle;
8083        my $input = Whatpm::Charset::DecodeHandle::CharString->new (\($_[0]));
8084        $input = $get_wrapper->($input);
8085        $p->{set_nc} = sub {
8086        my $self = shift;        my $self = shift;
8087    
8088        pop @{$self->{prev_input_character}};        my $char = '';
8089        unshift @{$self->{prev_input_character}}, $self->{next_input_character};        if (defined $self->{next_nc}) {
8090            $char = $self->{next_nc};
8091            delete $self->{next_nc};
8092            $self->{nc} = ord $char;
8093          } else {
8094            $self->{char_buffer} = '';
8095            $self->{char_buffer_pos} = 0;
8096            
8097            my $count = $input->manakai_read_until
8098                ($self->{char_buffer}, qr/[^\x00\x0A\x0D]/,
8099                 $self->{char_buffer_pos});
8100            if ($count) {
8101              $self->{line_prev} = $self->{line};
8102              $self->{column_prev} = $self->{column};
8103              $self->{column}++;
8104              $self->{nc}
8105                  = ord substr ($self->{char_buffer},
8106                                $self->{char_buffer_pos}++, 1);
8107              return;
8108            }
8109            
8110            if ($input->read ($char, 1)) {
8111              $self->{nc} = ord $char;
8112            } else {
8113              $self->{nc} = -1;
8114              return;
8115            }
8116          }
8117    
8118          ($p->{line_prev}, $p->{column_prev}) = ($p->{line}, $p->{column});
8119          $p->{column}++;
8120    
8121        $self->{next_input_character} = -1 and return if $i >= length $$s;        if ($self->{nc} == 0x000A) { # LF
8122        $self->{next_input_character} = ord substr $$s, $i++, 1;          $p->{line}++;
8123        $column++;          $p->{column} = 0;
8124            !!!cp ('i1');
8125        if ($self->{next_input_character} == 0x000A) { # LF        } elsif ($self->{nc} == 0x000D) { # CR
8126          $line++;  ## TODO: support for abort/streaming
8127          $column = 0;          my $next = '';
8128        } elsif ($self->{next_input_character} == 0x000D) { # CR          if ($input->read ($next, 1) and $next ne "\x0A") {
8129          $i++ if substr ($$s, $i, 1) eq "\x0A";            $self->{next_nc} = $next;
8130          $self->{next_input_character} = 0x000A; # LF # MUST          }
8131          $line++;          $self->{nc} = 0x000A; # LF # MUST
8132          $column = 0;          $p->{line}++;
8133        } elsif ($self->{next_input_character} > 0x10FFFF) {          $p->{column} = 0;
8134          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          !!!cp ('i2');
8135        } elsif ($self->{next_input_character} == 0x0000) { # NULL        } elsif ($self->{nc} == 0x0000) { # NULL
8136            !!!cp ('i4');
8137          !!!parse-error (type => 'NULL');          !!!parse-error (type => 'NULL');
8138          $self->{next_input_character} = 0xFFFD; # REPLACEMENT CHARACTER # MUST          $self->{nc} = 0xFFFD; # REPLACEMENT CHARACTER # MUST
8139        }        }
8140      };      };
8141      $p->{prev_input_character} = [-1, -1, -1];  
8142      $p->{next_input_character} = -1;      $p->{read_until} = sub {
8143              #my ($scalar, $specials_range, $offset) = @_;
8144          return 0 if defined $p->{next_nc};
8145    
8146          my $pattern = qr/[^$_[1]\x00\x0A\x0D]/;
8147          my $offset = $_[2] || 0;
8148          
8149          if ($p->{char_buffer_pos} < length $p->{char_buffer}) {
8150            pos ($p->{char_buffer}) = $p->{char_buffer_pos};
8151            if ($p->{char_buffer} =~ /\G(?>$pattern)+/) {
8152              substr ($_[0], $offset)
8153                  = substr ($p->{char_buffer}, $-[0], $+[0] - $-[0]);
8154              my $count = $+[0] - $-[0];
8155              if ($count) {
8156                $p->{column} += $count;
8157                $p->{char_buffer_pos} += $count;
8158                $p->{line_prev} = $p->{line};
8159                $p->{column_prev} = $p->{column} - 1;
8160                $p->{nc} = -1;
8161              }
8162              return $count;
8163            } else {
8164              return 0;
8165            }
8166          } else {
8167            my $count = $input->manakai_read_until ($_[0], $pattern, $_[2]);
8168            if ($count) {
8169              $p->{column} += $count;
8170              $p->{column_prev} += $count;
8171              $p->{nc} = -1;
8172            }
8173            return $count;
8174          }
8175        }; # $p->{read_until}
8176    
8177      my $ponerror = $onerror || sub {      my $ponerror = $onerror || sub {
8178        my (%opt) = @_;        my (%opt) = @_;
8179        warn "Parse error ($opt{type}) at line $opt{line} column $opt{column}\n";        my $line = $opt{line};
8180          my $column = $opt{column};
8181          if (defined $opt{token} and defined $opt{token}->{line}) {
8182            $line = $opt{token}->{line};
8183            $column = $opt{token}->{column};
8184          }
8185          warn "Parse error ($opt{type}) at line $line column $column\n";
8186      };      };
8187      $p->{parse_error} = sub {      $p->{parse_error} = sub {
8188        $ponerror->(@_, line => $line, column => $column);        $ponerror->(line => $p->{line}, column => $p->{column}, @_);
8189      };      };
8190            
8191        my $char_onerror = sub {
8192          my (undef, $type, %opt) = @_;
8193          $ponerror->(layer => 'encode',
8194                      line => $p->{line}, column => $p->{column} + 1,
8195                      %opt, type => $type);
8196        }; # $char_onerror
8197        $input->onerror ($char_onerror);
8198    
8199      $p->_initialize_tokenizer;      $p->_initialize_tokenizer;
8200      $p->_initialize_tree_constructor;      $p->_initialize_tree_constructor;
8201    
8202      ## Step 2      ## Step 2
8203      my $node_ln = $node->local_name;      my $node_ln = $node->manakai_local_name;
8204      $p->{content_model} = {      $p->{content_model} = {
8205        title => RCDATA_CONTENT_MODEL,        title => RCDATA_CONTENT_MODEL,
8206        textarea => RCDATA_CONTENT_MODEL,        textarea => RCDATA_CONTENT_MODEL,
# Line 5302  sub set_inner_html ($$$) { Line 8217  sub set_inner_html ($$$) {
8217          unless defined $p->{content_model};          unless defined $p->{content_model};
8218          ## ISSUE: What is "the name of the element"? local name?          ## ISSUE: What is "the name of the element"? local name?
8219    
8220      $p->{inner_html_node} = [$node, $node_ln];      $p->{inner_html_node} = [$node, $el_category->{$node_ln}];
8221          ## TODO: Foreign element OK?
8222    
8223      ## Step 4      ## Step 3
8224      my $root = $doc->create_element_ns      my $root = $doc->create_element_ns
8225        ('http://www.w3.org/1999/xhtml', [undef, 'html']);        ('http://www.w3.org/1999/xhtml', [undef, 'html']);
8226    
8227      ## Step 5 # MUST      ## Step 4 # MUST
8228      $doc->append_child ($root);      $doc->append_child ($root);
8229    
8230      ## Step 6 # MUST      ## Step 5 # MUST
8231      push @{$p->{open_elements}}, [$root, 'html'];      push @{$p->{open_elements}}, [$root, $el_category->{html}];
8232    
8233      undef $p->{head_element};      undef $p->{head_element};
8234        undef $p->{head_element_inserted};
8235    
8236      ## Step 7 # MUST      ## Step 6 # MUST
8237      $p->_reset_insertion_mode;      $p->_reset_insertion_mode;
8238    
8239      ## Step 8 # MUST      ## Step 7 # MUST
8240      my $anode = $node;      my $anode = $node;
8241      AN: while (defined $anode) {      AN: while (defined $anode) {
8242        if ($anode->node_type == 1) {        if ($anode->node_type == 1) {
8243          my $nsuri = $anode->namespace_uri;          my $nsuri = $anode->namespace_uri;
8244          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {          if (defined $nsuri and $nsuri eq 'http://www.w3.org/1999/xhtml') {
8245            if ($anode->local_name eq 'form') { ## TODO: case?            if ($anode->manakai_local_name eq 'form') {
8246                !!!cp ('i5');
8247              $p->{form_element} = $anode;              $p->{form_element} = $anode;
8248              last AN;              last AN;
8249            }            }
# Line 5334  sub set_inner_html ($$$) { Line 8252  sub set_inner_html ($$$) {
8252        $anode = $anode->parent_node;        $anode = $anode->parent_node;
8253      } # AN      } # AN
8254            
8255      ## Step 3 # MUST      ## Step 9 # MUST
     ## Step 10 # MUST  
8256      {      {
8257        my $self = $p;        my $self = $p;
8258        !!!next-token;        !!!next-token;
8259      }      }
8260      $p->_tree_construction_main;      $p->_tree_construction_main;
8261    
8262      ## Step 11 # MUST      ## Step 10 # MUST
8263      my @cn = @{$node->child_nodes};      my @cn = @{$node->child_nodes};
8264      for (@cn) {      for (@cn) {
8265        $node->remove_child ($_);        $node->remove_child ($_);
8266      }      }
8267      ## ISSUE: mutation events? read-only?      ## ISSUE: mutation events? read-only?
8268    
8269      ## Step 12 # MUST      ## Step 11 # MUST
8270      @cn = @{$root->child_nodes};      @cn = @{$root->child_nodes};
8271      for (@cn) {      for (@cn) {
8272        $this_doc->adopt_node ($_);        $this_doc->adopt_node ($_);
# Line 5358  sub set_inner_html ($$$) { Line 8275  sub set_inner_html ($$$) {
8275      ## ISSUE: mutation events?      ## ISSUE: mutation events?
8276    
8277      $p->_terminate_tree_constructor;      $p->_terminate_tree_constructor;
8278    
8279        delete $p->{parse_error}; # delete loop
8280    } else {    } else {
8281      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";
8282    }    }
# Line 5365  sub set_inner_html ($$$) { Line 8284  sub set_inner_html ($$$) {
8284    
8285  } # tree construction stage  } # tree construction stage
8286    
8287  sub get_inner_html ($$$) {  package Whatpm::HTML::RestartParser;
8288    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  
8289    
8290  1;  1;
8291  # $Date$  # $Date$

Legend:
Removed from v.1.42  
changed lines
  Added in v.1.206

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24